Skip to content

Commit 9fd6270

Browse files
committed
Add support for a "load binary" LC_NOTE in mach-o corefiles
Add lldb support for a Mach-O "load binary" LC_NOTE which provides a UUID, load address/slide, and possibly a name of a binary that should be loaded when examining the core. struct load_binary { uint32_t version; // currently 1 uuid_t uuid; // all zeroes if uuid not specified uint64_t load_address; // virtual address where the macho is loaded, UINT64_MAX if unavail uint64_t slide; // slide to be applied to file address to get load address, 0 if unavail char name_cstring[]; // must be nul-byte terminated c-string, '\0' alone if name unavail } __attribute__((packed)); Differential Revision: https://reviews.llvm.org/D115494 rdar://85069250 (cherry picked from commit f212032)
1 parent 69ffbe4 commit 9fd6270

File tree

4 files changed

+165
-34
lines changed

4 files changed

+165
-34
lines changed

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

Lines changed: 91 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7052,6 +7052,23 @@ ObjectFileMachO::GetCorefileAllImageInfos() {
70527052
}
70537053
image_infos.all_image_infos.push_back(image_entry);
70547054
}
7055+
} else if (strcmp("load binary", data_owner) == 0) {
7056+
uint32_t version = m_data.GetU32(&fileoff);
7057+
if (version == 1) {
7058+
uuid_t uuid;
7059+
memcpy(&uuid, m_data.GetData(&fileoff, sizeof(uuid_t)),
7060+
sizeof(uuid_t));
7061+
uint64_t load_address = m_data.GetU64(&fileoff);
7062+
uint64_t slide = m_data.GetU64(&fileoff);
7063+
std::string filename = m_data.GetCStr(&fileoff);
7064+
7065+
MachOCorefileImageEntry image_entry;
7066+
image_entry.filename = filename;
7067+
image_entry.uuid = UUID::fromData(uuid, sizeof(uuid_t));
7068+
image_entry.load_address = load_address;
7069+
image_entry.slide = slide;
7070+
image_infos.all_image_infos.push_back(image_entry);
7071+
}
70557072
}
70567073
}
70577074
offset = cmd_offset + lc.cmdsize;
@@ -7062,29 +7079,42 @@ ObjectFileMachO::GetCorefileAllImageInfos() {
70627079

70637080
bool ObjectFileMachO::LoadCoreFileImages(lldb_private::Process &process) {
70647081
MachOCorefileAllImageInfos image_infos = GetCorefileAllImageInfos();
7065-
bool added_images = false;
7066-
if (image_infos.IsValid()) {
7067-
for (const MachOCorefileImageEntry &image : image_infos.all_image_infos) {
7068-
ModuleSpec module_spec;
7069-
module_spec.GetUUID() = image.uuid;
7070-
module_spec.GetFileSpec() = FileSpec(image.filename.c_str());
7071-
if (image.currently_executing) {
7072-
Symbols::DownloadObjectAndSymbolFile(module_spec, true);
7073-
if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) {
7074-
process.GetTarget().GetOrCreateModule(module_spec, false);
7075-
}
7082+
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
7083+
7084+
ModuleList added_modules;
7085+
for (const MachOCorefileImageEntry &image : image_infos.all_image_infos) {
7086+
ModuleSpec module_spec;
7087+
module_spec.GetUUID() = image.uuid;
7088+
module_spec.GetFileSpec() = FileSpec(image.filename.c_str());
7089+
if (image.currently_executing) {
7090+
Symbols::DownloadObjectAndSymbolFile(module_spec, true);
7091+
if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) {
7092+
process.GetTarget().GetOrCreateModule(module_spec, false);
70767093
}
7077-
Status error;
7078-
ModuleSP module_sp =
7079-
process.GetTarget().GetOrCreateModule(module_spec, false, &error);
7080-
if (!module_sp.get() || !module_sp->GetObjectFile()) {
7081-
if (image.load_address != LLDB_INVALID_ADDRESS) {
7082-
module_sp = process.ReadModuleFromMemory(module_spec.GetFileSpec(),
7083-
image.load_address);
7084-
}
7094+
}
7095+
Status error;
7096+
ModuleSP module_sp =
7097+
process.GetTarget().GetOrCreateModule(module_spec, false, &error);
7098+
if (!module_sp.get() || !module_sp->GetObjectFile()) {
7099+
if (image.load_address != LLDB_INVALID_ADDRESS) {
7100+
module_sp = process.ReadModuleFromMemory(module_spec.GetFileSpec(),
7101+
image.load_address);
70857102
}
7086-
if (module_sp.get()) {
7087-
added_images = true;
7103+
}
7104+
if (module_sp.get()) {
7105+
// Will call ModulesDidLoad with all modules once they've all
7106+
// been added to the Target with load addresses. Don't notify
7107+
// here, before the load address is set.
7108+
const bool notify = false;
7109+
process.GetTarget().GetImages().AppendIfNeeded(module_sp, notify);
7110+
added_modules.Append(module_sp, notify);
7111+
if (image.segment_load_addresses.size() > 0) {
7112+
if (log) {
7113+
std::string uuidstr = image.uuid.GetAsString();
7114+
log->Printf("ObjectFileMachO::LoadCoreFileImages adding binary '%s' "
7115+
"UUID %s with section load addresses",
7116+
image.filename.c_str(), uuidstr.c_str());
7117+
}
70887118
for (auto name_vmaddr_tuple : image.segment_load_addresses) {
70897119
SectionList *sectlist = module_sp->GetObjectFile()->GetSectionList();
70907120
if (sectlist) {
@@ -7096,8 +7126,47 @@ bool ObjectFileMachO::LoadCoreFileImages(lldb_private::Process &process) {
70967126
}
70977127
}
70987128
}
7129+
} else if (image.load_address != LLDB_INVALID_ADDRESS) {
7130+
if (log) {
7131+
std::string uuidstr = image.uuid.GetAsString();
7132+
log->Printf("ObjectFileMachO::LoadCoreFileImages adding binary '%s' "
7133+
"UUID %s with load address 0x%" PRIx64,
7134+
image.filename.c_str(), uuidstr.c_str(),
7135+
image.load_address);
7136+
}
7137+
const bool address_is_slide = false;
7138+
bool changed = false;
7139+
module_sp->SetLoadAddress(process.GetTarget(), image.load_address,
7140+
address_is_slide, changed);
7141+
} else if (image.slide != 0) {
7142+
if (log) {
7143+
std::string uuidstr = image.uuid.GetAsString();
7144+
log->Printf("ObjectFileMachO::LoadCoreFileImages adding binary '%s' "
7145+
"UUID %s with slide amount 0x%" PRIx64,
7146+
image.filename.c_str(), uuidstr.c_str(), image.slide);
7147+
}
7148+
const bool address_is_slide = true;
7149+
bool changed = false;
7150+
module_sp->SetLoadAddress(process.GetTarget(), image.slide,
7151+
address_is_slide, changed);
7152+
} else {
7153+
if (log) {
7154+
std::string uuidstr = image.uuid.GetAsString();
7155+
log->Printf("ObjectFileMachO::LoadCoreFileImages adding binary '%s' "
7156+
"UUID %s at its file address, no slide applied",
7157+
image.filename.c_str(), uuidstr.c_str());
7158+
}
7159+
const bool address_is_slide = true;
7160+
bool changed = false;
7161+
module_sp->SetLoadAddress(process.GetTarget(), 0, address_is_slide,
7162+
changed);
70997163
}
71007164
}
71017165
}
7102-
return added_images;
7166+
if (added_modules.GetSize() > 0) {
7167+
process.GetTarget().ModulesDidLoad(added_modules);
7168+
process.Flush();
7169+
return true;
7170+
}
7171+
return false;
71037172
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,8 @@ class ObjectFileMachO : public lldb_private::ObjectFile {
228228
std::string filename;
229229
lldb_private::UUID uuid;
230230
lldb::addr_t load_address = LLDB_INVALID_ADDRESS;
231-
bool currently_executing;
231+
lldb::addr_t slide = 0;
232+
bool currently_executing = false;
232233
std::vector<std::tuple<lldb_private::ConstString, lldb::addr_t>>
233234
segment_load_addresses;
234235
};

lldb/test/API/macosx/corefile-default-ptrauth/TestCorefileDefaultPtrauth.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class TestCorefileDefaultPtrauth(TestBase):
1818
@skipIf(debug_info=no_match(["dsym"]), bugnumber="This test is looking explicitly for a dSYM")
1919
@skipIf(archs=no_match(['arm64','arm64e']))
2020
@skipUnlessDarwin
21+
@skipIfRemote
2122
def test_lc_note(self):
2223
self.build()
2324
self.test_exe = self.getBuildArtifact("a.out")
@@ -32,16 +33,18 @@ def test_lc_note(self):
3233
## to fall back on its old default value for Darwin arm64 ABIs
3334
## to correctly strip the bits.
3435

36+
# Create a Target with our main executable binary to get it
37+
# seeded in lldb's global module cache. Then delete the Target.
38+
# This way when the corefile searches for a binary with its UUID,
39+
# it'll be found by that search.
40+
initial_target = self.dbg.CreateTarget(self.test_exe)
41+
self.dbg.DeleteTarget(initial_target)
42+
3543
self.target = self.dbg.CreateTarget('')
3644
err = lldb.SBError()
3745
self.process = self.target.LoadCore(self.corefile)
3846
self.assertEqual(self.process.IsValid(), True)
39-
modspec = lldb.SBModuleSpec()
40-
modspec.SetFileSpec(lldb.SBFileSpec(self.test_exe, True))
41-
m = self.target.AddModule(modspec)
42-
self.assertTrue(m.IsValid())
43-
self.target.SetModuleLoadAddress (m, 0)
44-
47+
4548
# target variable should show us both the actual function
4649
# pointer with ptrauth bits and the symbol it resolves to,
4750
# with the ptrauth bits stripped, e.g.

lldb/test/API/macosx/corefile-default-ptrauth/create-corefile.c

Lines changed: 63 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <mach/machine/thread_state.h>
77
#include <inttypes.h>
88
#include <sys/syslimits.h>
9+
#include <uuid/uuid.h>
910

1011
// Given an executable binary with
1112
// "fmain" (a function pointer to main)
@@ -55,6 +56,28 @@ int main(int argc, char **argv)
5556
}
5657
pclose (nm);
5758

59+
sprintf (buf, "dwarfdump -u '%s'", argv[1]);
60+
FILE *dwarfdump = popen(buf, "r");
61+
if (!dwarfdump) {
62+
fprintf (stderr, "Unable to run dwarfdump -u on '%s'\n", argv[1]);
63+
exit (1);
64+
}
65+
uuid_t uuid;
66+
uuid_clear (uuid);
67+
while (fgets (buf, sizeof(buf), dwarfdump)) {
68+
if (strncmp (buf, "UUID: ", 6) == 0) {
69+
buf[6 + 36] = '\0';
70+
if (uuid_parse (buf + 6, uuid) != 0) {
71+
fprintf (stderr, "Unable to parse UUID in '%s'\n", buf);
72+
exit (1);
73+
}
74+
}
75+
}
76+
if (uuid_is_null(uuid)) {
77+
fprintf (stderr, "Got a null uuid for the binary\n");
78+
exit (1);
79+
}
80+
5881
if (main_addr == 0 || fmain_addr == 0) {
5982
fprintf(stderr, "Unable to find address of main or fmain in %s.\n",
6083
argv[1]);
@@ -65,7 +88,9 @@ int main(int argc, char **argv)
6588
// 1. mach header
6689
// 2. LC_THREAD load command
6790
// 3. LC_SEGMENT_64 load command
68-
// 4. memory segment contents
91+
// 4. LC_NOTE load command
92+
// 5. memory segment contents
93+
// 6. "load binary" note contents
6994

7095
// struct thread_command {
7196
// uint32_t cmd;
@@ -80,21 +105,22 @@ int main(int argc, char **argv)
80105
mh.cputype = CPU_TYPE_ARM64;
81106
mh.cpusubtype = CPU_SUBTYPE_ARM64E;
82107
mh.filetype = MH_CORE;
83-
mh.ncmds = 2; // LC_THREAD, LC_SEGMENT_64
84-
mh.sizeofcmds = size_of_thread_cmd + sizeof(struct segment_command_64);
108+
mh.ncmds = 3; // LC_THREAD, LC_SEGMENT_64, LC_NOTE
109+
mh.sizeofcmds = size_of_thread_cmd + sizeof(struct segment_command_64) + sizeof(struct note_command);
85110
mh.flags = 0;
86111
mh.reserved = 0;
87112

88113
fwrite(&mh, sizeof (mh), 1, out);
89114

115+
struct note_command lcnote;
90116
struct segment_command_64 seg;
91117
seg.cmd = LC_SEGMENT_64;
92118
seg.cmdsize = sizeof(seg);
93119
memset (&seg.segname, 0, 16);
94120
seg.vmaddr = fmain_addr;
95121
seg.vmsize = 8;
96122
// Offset to segment contents
97-
seg.fileoff = sizeof (mh) + size_of_thread_cmd + sizeof(seg);
123+
seg.fileoff = sizeof (mh) + size_of_thread_cmd + sizeof(seg) + sizeof(lcnote);
98124
seg.filesize = 8;
99125
seg.maxprot = 3;
100126
seg.initprot = 3;
@@ -116,15 +142,47 @@ int main(int argc, char **argv)
116142
memset (&regstate, 0, sizeof (regstate));
117143
fwrite (&regstate, sizeof (regstate), 1, out);
118144

145+
lcnote.cmd = LC_NOTE;
146+
lcnote.cmdsize = sizeof (lcnote);
147+
strcpy (lcnote.data_owner, "load binary");
148+
149+
// 8 is the size of the LC_SEGMENT contents
150+
lcnote.offset = sizeof (mh) + size_of_thread_cmd + sizeof(seg) + sizeof(lcnote) + 8;
151+
152+
// struct load_binary
153+
// {
154+
// uint32_t version; // currently 1
155+
// uuid_t uuid; // all zeroes if uuid not specified
156+
// uint64_t load_address; // virtual address where the macho is loaded, UINT64_MAX if unavail
157+
// uint64_t slide; // slide to be applied to file address to get load address, 0 if unavail
158+
// char name_cstring[]; // must be nul-byte terminated c-string, '\0' alone if name unavail
159+
// } __attribute__((packed));
160+
lcnote.size = 4 + 16 + 8 + 8 + sizeof("a.out");
161+
162+
fwrite (&lcnote, sizeof(lcnote), 1, out);
163+
164+
// Write the contents of the memory segment
119165

120166
// Or together a random PAC value from a system using 39 bits
121167
// of addressing with the address of main(). lldb will need
122168
// to correctly strip off the high bits to find the address of
123169
// main.
124170
uint64_t segment_contents = 0xe46bff0000000000 | main_addr;
125-
126171
fwrite (&segment_contents, sizeof (segment_contents), 1, out);
127172

173+
// Now write the contents of the "load binary" LC_NOTE.
174+
{
175+
uint32_t version = 1;
176+
fwrite (&version, sizeof (version), 1, out);
177+
fwrite (&uuid, sizeof (uuid), 1, out);
178+
uint64_t load_address = UINT64_MAX;
179+
fwrite (&load_address, sizeof (load_address), 1, out);
180+
uint64_t slide = 0;
181+
fwrite (&slide, sizeof (slide), 1, out);
182+
strcpy (buf, "a.out");
183+
fwrite (buf, 6, 1, out);
184+
}
185+
128186
fclose (out);
129187

130188
exit (0);

0 commit comments

Comments
 (0)