Skip to content

Commit f212032

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
1 parent 5c23acb commit f212032

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
@@ -6973,6 +6973,23 @@ ObjectFileMachO::GetCorefileAllImageInfos() {
69736973
}
69746974
image_infos.all_image_infos.push_back(image_entry);
69756975
}
6976+
} else if (strcmp("load binary", data_owner) == 0) {
6977+
uint32_t version = m_data.GetU32(&fileoff);
6978+
if (version == 1) {
6979+
uuid_t uuid;
6980+
memcpy(&uuid, m_data.GetData(&fileoff, sizeof(uuid_t)),
6981+
sizeof(uuid_t));
6982+
uint64_t load_address = m_data.GetU64(&fileoff);
6983+
uint64_t slide = m_data.GetU64(&fileoff);
6984+
std::string filename = m_data.GetCStr(&fileoff);
6985+
6986+
MachOCorefileImageEntry image_entry;
6987+
image_entry.filename = filename;
6988+
image_entry.uuid = UUID::fromData(uuid, sizeof(uuid_t));
6989+
image_entry.load_address = load_address;
6990+
image_entry.slide = slide;
6991+
image_infos.all_image_infos.push_back(image_entry);
6992+
}
69766993
}
69776994
}
69786995
offset = cmd_offset + lc.cmdsize;
@@ -6983,29 +7000,42 @@ ObjectFileMachO::GetCorefileAllImageInfos() {
69837000

69847001
bool ObjectFileMachO::LoadCoreFileImages(lldb_private::Process &process) {
69857002
MachOCorefileAllImageInfos image_infos = GetCorefileAllImageInfos();
6986-
bool added_images = false;
6987-
if (image_infos.IsValid()) {
6988-
for (const MachOCorefileImageEntry &image : image_infos.all_image_infos) {
6989-
ModuleSpec module_spec;
6990-
module_spec.GetUUID() = image.uuid;
6991-
module_spec.GetFileSpec() = FileSpec(image.filename.c_str());
6992-
if (image.currently_executing) {
6993-
Symbols::DownloadObjectAndSymbolFile(module_spec, true);
6994-
if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) {
6995-
process.GetTarget().GetOrCreateModule(module_spec, false);
6996-
}
7003+
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
7004+
7005+
ModuleList added_modules;
7006+
for (const MachOCorefileImageEntry &image : image_infos.all_image_infos) {
7007+
ModuleSpec module_spec;
7008+
module_spec.GetUUID() = image.uuid;
7009+
module_spec.GetFileSpec() = FileSpec(image.filename.c_str());
7010+
if (image.currently_executing) {
7011+
Symbols::DownloadObjectAndSymbolFile(module_spec, true);
7012+
if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) {
7013+
process.GetTarget().GetOrCreateModule(module_spec, false);
69977014
}
6998-
Status error;
6999-
ModuleSP module_sp =
7000-
process.GetTarget().GetOrCreateModule(module_spec, false, &error);
7001-
if (!module_sp.get() || !module_sp->GetObjectFile()) {
7002-
if (image.load_address != LLDB_INVALID_ADDRESS) {
7003-
module_sp = process.ReadModuleFromMemory(module_spec.GetFileSpec(),
7004-
image.load_address);
7005-
}
7015+
}
7016+
Status error;
7017+
ModuleSP module_sp =
7018+
process.GetTarget().GetOrCreateModule(module_spec, false, &error);
7019+
if (!module_sp.get() || !module_sp->GetObjectFile()) {
7020+
if (image.load_address != LLDB_INVALID_ADDRESS) {
7021+
module_sp = process.ReadModuleFromMemory(module_spec.GetFileSpec(),
7022+
image.load_address);
70067023
}
7007-
if (module_sp.get()) {
7008-
added_images = true;
7024+
}
7025+
if (module_sp.get()) {
7026+
// Will call ModulesDidLoad with all modules once they've all
7027+
// been added to the Target with load addresses. Don't notify
7028+
// here, before the load address is set.
7029+
const bool notify = false;
7030+
process.GetTarget().GetImages().AppendIfNeeded(module_sp, notify);
7031+
added_modules.Append(module_sp, notify);
7032+
if (image.segment_load_addresses.size() > 0) {
7033+
if (log) {
7034+
std::string uuidstr = image.uuid.GetAsString();
7035+
log->Printf("ObjectFileMachO::LoadCoreFileImages adding binary '%s' "
7036+
"UUID %s with section load addresses",
7037+
image.filename.c_str(), uuidstr.c_str());
7038+
}
70097039
for (auto name_vmaddr_tuple : image.segment_load_addresses) {
70107040
SectionList *sectlist = module_sp->GetObjectFile()->GetSectionList();
70117041
if (sectlist) {
@@ -7017,8 +7047,47 @@ bool ObjectFileMachO::LoadCoreFileImages(lldb_private::Process &process) {
70177047
}
70187048
}
70197049
}
7050+
} else if (image.load_address != LLDB_INVALID_ADDRESS) {
7051+
if (log) {
7052+
std::string uuidstr = image.uuid.GetAsString();
7053+
log->Printf("ObjectFileMachO::LoadCoreFileImages adding binary '%s' "
7054+
"UUID %s with load address 0x%" PRIx64,
7055+
image.filename.c_str(), uuidstr.c_str(),
7056+
image.load_address);
7057+
}
7058+
const bool address_is_slide = false;
7059+
bool changed = false;
7060+
module_sp->SetLoadAddress(process.GetTarget(), image.load_address,
7061+
address_is_slide, changed);
7062+
} else if (image.slide != 0) {
7063+
if (log) {
7064+
std::string uuidstr = image.uuid.GetAsString();
7065+
log->Printf("ObjectFileMachO::LoadCoreFileImages adding binary '%s' "
7066+
"UUID %s with slide amount 0x%" PRIx64,
7067+
image.filename.c_str(), uuidstr.c_str(), image.slide);
7068+
}
7069+
const bool address_is_slide = true;
7070+
bool changed = false;
7071+
module_sp->SetLoadAddress(process.GetTarget(), image.slide,
7072+
address_is_slide, changed);
7073+
} else {
7074+
if (log) {
7075+
std::string uuidstr = image.uuid.GetAsString();
7076+
log->Printf("ObjectFileMachO::LoadCoreFileImages adding binary '%s' "
7077+
"UUID %s at its file address, no slide applied",
7078+
image.filename.c_str(), uuidstr.c_str());
7079+
}
7080+
const bool address_is_slide = true;
7081+
bool changed = false;
7082+
module_sp->SetLoadAddress(process.GetTarget(), 0, address_is_slide,
7083+
changed);
70207084
}
70217085
}
70227086
}
7023-
return added_images;
7087+
if (added_modules.GetSize() > 0) {
7088+
process.GetTarget().ModulesDidLoad(added_modules);
7089+
process.Flush();
7090+
return true;
7091+
}
7092+
return false;
70247093
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,8 @@ class ObjectFileMachO : public lldb_private::ObjectFile {
225225
std::string filename;
226226
lldb_private::UUID uuid;
227227
lldb::addr_t load_address = LLDB_INVALID_ADDRESS;
228-
bool currently_executing;
228+
lldb::addr_t slide = 0;
229+
bool currently_executing = false;
229230
std::vector<std::tuple<lldb_private::ConstString, lldb::addr_t>>
230231
segment_load_addresses;
231232
};

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)