Skip to content

Commit 5e36f06

Browse files
authored
Merge pull request #10199 from jasonmolenda/r146167816-dont-read-nlist-records-from-memory-for-specially-marked-binary-6.1
[lldb][Mach-O] Don't read symbol table of specially marked binary (llvm#129967)
2 parents b3bb7aa + 743bf28 commit 5e36f06

File tree

9 files changed

+208
-24
lines changed

9 files changed

+208
-24
lines changed

lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp

Lines changed: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -921,6 +921,11 @@ ConstString ObjectFileMachO::GetSectionNameEHFrame() {
921921
return g_section_name_eh_frame;
922922
}
923923

924+
ConstString ObjectFileMachO::GetSectionNameLLDBNoNlist() {
925+
static ConstString g_section_name_lldb_no_nlist("__lldb_no_nlist");
926+
return g_section_name_lldb_no_nlist;
927+
}
928+
924929
bool ObjectFileMachO::MagicBytesMatch(DataBufferSP data_sp,
925930
lldb::addr_t data_offset,
926931
lldb::addr_t data_length) {
@@ -2395,15 +2400,54 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) {
23952400
uint32_t memory_module_load_level = eMemoryModuleLoadLevelComplete;
23962401
bool is_shared_cache_image = IsSharedCacheBinary();
23972402
bool is_local_shared_cache_image = is_shared_cache_image && !IsInMemory();
2403+
2404+
ConstString g_segment_name_TEXT = GetSegmentNameTEXT();
2405+
ConstString g_segment_name_DATA = GetSegmentNameDATA();
2406+
ConstString g_segment_name_DATA_DIRTY = GetSegmentNameDATA_DIRTY();
2407+
ConstString g_segment_name_DATA_CONST = GetSegmentNameDATA_CONST();
2408+
ConstString g_segment_name_OBJC = GetSegmentNameOBJC();
2409+
ConstString g_section_name_eh_frame = GetSectionNameEHFrame();
2410+
ConstString g_section_name_lldb_no_nlist = GetSectionNameLLDBNoNlist();
2411+
SectionSP text_section_sp(
2412+
section_list->FindSectionByName(g_segment_name_TEXT));
2413+
SectionSP data_section_sp(
2414+
section_list->FindSectionByName(g_segment_name_DATA));
23982415
SectionSP linkedit_section_sp(
23992416
section_list->FindSectionByName(GetSegmentNameLINKEDIT()));
2417+
SectionSP data_dirty_section_sp(
2418+
section_list->FindSectionByName(g_segment_name_DATA_DIRTY));
2419+
SectionSP data_const_section_sp(
2420+
section_list->FindSectionByName(g_segment_name_DATA_CONST));
2421+
SectionSP objc_section_sp(
2422+
section_list->FindSectionByName(g_segment_name_OBJC));
2423+
SectionSP eh_frame_section_sp;
2424+
SectionSP lldb_no_nlist_section_sp;
2425+
if (text_section_sp.get()) {
2426+
eh_frame_section_sp = text_section_sp->GetChildren().FindSectionByName(
2427+
g_section_name_eh_frame);
2428+
lldb_no_nlist_section_sp = text_section_sp->GetChildren().FindSectionByName(
2429+
g_section_name_lldb_no_nlist);
2430+
} else {
2431+
eh_frame_section_sp =
2432+
section_list->FindSectionByName(g_section_name_eh_frame);
2433+
lldb_no_nlist_section_sp =
2434+
section_list->FindSectionByName(g_section_name_lldb_no_nlist);
2435+
}
24002436

24012437
if (process && m_header.filetype != llvm::MachO::MH_OBJECT &&
24022438
!is_local_shared_cache_image) {
24032439
Target &target = process->GetTarget();
24042440

24052441
memory_module_load_level = target.GetMemoryModuleLoadLevel();
24062442

2443+
// If __TEXT,__lldb_no_nlist section is present in this binary,
2444+
// and we're reading it out of memory, do not read any of the
2445+
// nlist entries. They are not needed in lldb and it may be
2446+
// expensive to load these. This is to handle a dylib consisting
2447+
// of only metadata, no code, but it has many nlist entries.
2448+
if (lldb_no_nlist_section_sp)
2449+
memory_module_load_level = eMemoryModuleLoadLevelMinimal;
2450+
24072451
// Reading mach file from memory in a process or core file...
24082452

24092453
if (linkedit_section_sp) {
@@ -2528,30 +2572,6 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) {
25282572

25292573
const bool have_strtab_data = strtab_data.GetByteSize() > 0;
25302574

2531-
ConstString g_segment_name_TEXT = GetSegmentNameTEXT();
2532-
ConstString g_segment_name_DATA = GetSegmentNameDATA();
2533-
ConstString g_segment_name_DATA_DIRTY = GetSegmentNameDATA_DIRTY();
2534-
ConstString g_segment_name_DATA_CONST = GetSegmentNameDATA_CONST();
2535-
ConstString g_segment_name_OBJC = GetSegmentNameOBJC();
2536-
ConstString g_section_name_eh_frame = GetSectionNameEHFrame();
2537-
SectionSP text_section_sp(
2538-
section_list->FindSectionByName(g_segment_name_TEXT));
2539-
SectionSP data_section_sp(
2540-
section_list->FindSectionByName(g_segment_name_DATA));
2541-
SectionSP data_dirty_section_sp(
2542-
section_list->FindSectionByName(g_segment_name_DATA_DIRTY));
2543-
SectionSP data_const_section_sp(
2544-
section_list->FindSectionByName(g_segment_name_DATA_CONST));
2545-
SectionSP objc_section_sp(
2546-
section_list->FindSectionByName(g_segment_name_OBJC));
2547-
SectionSP eh_frame_section_sp;
2548-
if (text_section_sp.get())
2549-
eh_frame_section_sp = text_section_sp->GetChildren().FindSectionByName(
2550-
g_section_name_eh_frame);
2551-
else
2552-
eh_frame_section_sp =
2553-
section_list->FindSectionByName(g_section_name_eh_frame);
2554-
25552575
const bool is_arm = (m_header.cputype == llvm::MachO::CPU_TYPE_ARM);
25562576
const bool always_thumb = GetArchitecture().IsAlwaysThumbInstructions();
25572577

lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ class ObjectFileMachO : public lldb_private::ObjectFile {
285285
static lldb_private::ConstString GetSegmentNameDWARF();
286286
static lldb_private::ConstString GetSegmentNameLLVM_COV();
287287
static lldb_private::ConstString GetSectionNameEHFrame();
288+
static lldb_private::ConstString GetSectionNameLLDBNoNlist();
288289

289290
llvm::MachO::dysymtab_command m_dysymtab;
290291
std::vector<llvm::MachO::section_64> m_mach_sections;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
C_SOURCES := main.c
2+
LD_EXTRAS = -Wl,-rpath "-Wl,$(shell pwd)" -L. -lno-nlists -lhas-nlists
3+
4+
include Makefile.rules
5+
6+
a.out: dylib_HasNlists dylib_NoNlists
7+
8+
dylib_HasNlists:
9+
"$(MAKE)" -f $(MAKEFILE_RULES) \
10+
DYLIB_ONLY=YES DYLIB_NAME=has-nlists DYLIB_C_SOURCES=has-nlists.c
11+
12+
dylib_NoNlists:
13+
"$(MAKE)" VPATH=$(SRCDIR) -I $(SRCDIR) -f $(SRCDIR)/NoNlists.mk
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
DYLIB_ONLY := YES
2+
DYLIB_NAME := no-nlists
3+
DYLIB_C_SOURCES := no-nlists.c
4+
DYLIB_OBJECTS += no-nlist-sect.o
5+
6+
no-nlist-sect.o:
7+
$(CC) $(CFLAGS) -c -o no-nlist-sect.o $(SRCDIR)/no-nlist-sect.s
8+
9+
include Makefile.rules
10+
11+
clean::
12+
rm -rf *.o *.dylib a.out *.dSYM
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
"""
2+
Test that we read don't read the nlist symbols for a specially marked dylib
3+
when read from memory.
4+
"""
5+
6+
import os
7+
import lldb
8+
from lldbsuite.test.decorators import *
9+
from lldbsuite.test.lldbtest import *
10+
from lldbsuite.test import lldbutil
11+
from time import sleep
12+
13+
14+
class NoNlistsTestCase(TestBase):
15+
NO_DEBUG_INFO_TESTCASE = True
16+
17+
@skipIfRemote
18+
@skipUnlessDarwin
19+
def test_no_nlist_symbols(self):
20+
self.build()
21+
22+
exe = os.path.realpath(self.getBuildArtifact("a.out"))
23+
24+
# Use a file as a synchronization point between test and inferior.
25+
pid_file_path = lldbutil.append_to_process_working_directory(
26+
self, "pid_file_%d" % (int(time.time()))
27+
)
28+
self.addTearDownHook(
29+
lambda: self.run_platform_command("rm %s" % (pid_file_path))
30+
)
31+
32+
# Spawn a new process
33+
popen = self.spawnSubprocess(exe, [pid_file_path])
34+
35+
pid = lldbutil.wait_for_file_on_target(self, pid_file_path)
36+
37+
os.unlink(self.getBuildArtifact("libno-nlists.dylib"))
38+
os.unlink(self.getBuildArtifact("libhas-nlists.dylib"))
39+
40+
self.runCmd("process attach -p " + str(pid))
41+
target = self.dbg.GetSelectedTarget()
42+
process = target.GetProcess()
43+
m_no_nlist = target.FindModule(lldb.SBFileSpec("libno-nlists.dylib"))
44+
m_has_nlist = target.FindModule(lldb.SBFileSpec("libhas-nlists.dylib"))
45+
46+
self.assertTrue(process, PROCESS_IS_VALID)
47+
48+
if self.TraceOn():
49+
self.runCmd("image list")
50+
self.runCmd("target modules dump symtab libno-nlists.dylib")
51+
self.runCmd("target modules dump symtab libhas-nlists.dylib")
52+
53+
# Test that we found libno-nlists.dylib, it is a memory
54+
# module, and that it has no symbols.
55+
self.assertTrue(m_no_nlist.IsValid())
56+
self.assertFalse(m_no_nlist.IsFileBacked())
57+
self.assertEqual(m_no_nlist.GetNumSymbols(), 0)
58+
59+
# Test that we found libhas-nlists.dylib, it is a memory
60+
# module, and that it has more than zero symbols.
61+
self.assertTrue(m_has_nlist.IsValid())
62+
self.assertFalse(m_has_nlist.IsFileBacked())
63+
self.assertGreater(m_has_nlist.GetNumSymbols(), 0)
64+
65+
# And as a sanity check, get the main binary's module,
66+
# test that it is file backed and that it has more than
67+
# zero symbols.
68+
m_exe = target.FindModule(lldb.SBFileSpec("a.out"))
69+
self.assertTrue(m_exe.IsValid())
70+
self.assertTrue(m_exe.IsFileBacked())
71+
self.assertGreater(m_exe.GetNumSymbols(), 0)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
int get_return_value2() { return 20; }
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#include <fcntl.h>
2+
#include <libgen.h>
3+
#include <stdbool.h>
4+
#include <stdint.h>
5+
#include <stdio.h>
6+
#include <stdlib.h>
7+
#include <string.h>
8+
#include <sys/errno.h>
9+
#include <sys/param.h>
10+
#include <sys/stat.h>
11+
#include <unistd.h>
12+
13+
int get_return_value();
14+
int get_return_value2();
15+
16+
// Create \a file_name with the c-string of our
17+
// pid in it. Initially open & write the contents
18+
// to a temporary file, then move it to the actual
19+
// filename once writing is completed.
20+
bool writePid(const char *file_name, const pid_t pid) {
21+
char *tmp_file_name = (char *)malloc(strlen(file_name) + 16);
22+
strcpy(tmp_file_name, file_name);
23+
strcat(tmp_file_name, "_tmp");
24+
int fd = open(tmp_file_name, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR);
25+
if (fd == -1) {
26+
fprintf(stderr, "open(%s) failed: %s\n", tmp_file_name, strerror(errno));
27+
free(tmp_file_name);
28+
return false;
29+
}
30+
char buffer[64];
31+
snprintf(buffer, sizeof(buffer), "%ld", (long)pid);
32+
bool res = true;
33+
if (write(fd, buffer, strlen(buffer)) == -1) {
34+
fprintf(stderr, "write(%s) failed: %s\n", buffer, strerror(errno));
35+
res = false;
36+
}
37+
close(fd);
38+
39+
if (rename(tmp_file_name, file_name) == -1) {
40+
fprintf(stderr, "rename(%s, %s) failed: %s\n", tmp_file_name, file_name,
41+
strerror(errno));
42+
res = false;
43+
}
44+
free(tmp_file_name);
45+
46+
return res;
47+
}
48+
49+
int main(int argc, char **argv) {
50+
if (writePid(argv[1], getpid())) {
51+
// we've signaled lldb we are ready to be attached to,
52+
// this sleep() call will be interrupted when lldb
53+
// attaches.
54+
sleep(200);
55+
} else {
56+
printf("Error writing pid to '%s', exiting.\n", argv[1]);
57+
exit(3);
58+
}
59+
60+
int retval = get_return_value();
61+
return retval + get_return_value2();
62+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.section __TEXT,__lldb_no_nlist,regular,pure_instructions
2+
.p2align 2
3+
.byte 0x10
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
int get_return_value() { return 10; }

0 commit comments

Comments
 (0)