Skip to content

Add support for firmware/standalone LC_NOTE "main bin spec" corefiles #1864

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions lldb/include/lldb/Symbol/ObjectFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,17 @@ class ObjectFile : public std::enable_shared_from_this<ObjectFile>,
eStrataJIT
};

/// If we have a corefile binary hint, this enum
/// specifies the binary type which we can use to
/// select the correct DynamicLoader plugin.
enum BinaryType {
eBinaryTypeInvalid = 0,
eBinaryTypeUnknown,
eBinaryTypeKernel, /// kernel binary
eBinaryTypeUser, /// user process binary
eBinaryTypeStandalone /// standalone binary / firmware
};

struct LoadableData {
lldb::addr_t Dest;
llvm::ArrayRef<uint8_t> Contents;
Expand Down Expand Up @@ -500,12 +511,17 @@ class ObjectFile : public std::enable_shared_from_this<ObjectFile>,
/// If the uuid of the binary is specified, this will be set.
/// If no UUID is available, will be cleared.
///
/// \param[out] type
/// Return the type of the binary, which will dictate which
/// DynamicLoader plugin should be used.
///
/// \return
/// Returns true if either address or uuid has been set.
virtual bool GetCorefileMainBinaryInfo (lldb::addr_t &address, UUID &uuid) {
address = LLDB_INVALID_ADDRESS;
uuid.Clear();
return false;
virtual bool GetCorefileMainBinaryInfo(lldb::addr_t &address, UUID &uuid,
ObjectFile::BinaryType &type) {
address = LLDB_INVALID_ADDRESS;
uuid.Clear();
return false;
}

virtual lldb::RegisterContextSP
Expand Down
36 changes: 28 additions & 8 deletions lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5519,7 +5519,8 @@ std::string ObjectFileMachO::GetIdentifierString() {
return result;
}

bool ObjectFileMachO::GetCorefileMainBinaryInfo(addr_t &address, UUID &uuid) {
bool ObjectFileMachO::GetCorefileMainBinaryInfo(addr_t &address, UUID &uuid,
ObjectFile::BinaryType &type) {
address = LLDB_INVALID_ADDRESS;
uuid.Clear();
ModuleSP module_sp(GetModule());
Expand All @@ -5542,24 +5543,43 @@ bool ObjectFileMachO::GetCorefileMainBinaryInfo(addr_t &address, UUID &uuid) {
// "main bin spec" (main binary specification) data payload is
// formatted:
// uint32_t version [currently 1]
// uint32_t type [0 == unspecified, 1 == kernel, 2 == user
// process] uint64_t address [ UINT64_MAX if address not
// specified ] uuid_t uuid [ all zero's if uuid not
// specified ] uint32_t log2_pagesize [ process page size in log base
// 2, e.g. 4k pages are 12. 0 for unspecified ]
// uint32_t type [0 == unspecified, 1 == kernel,
// 2 == user process, 3 == firmware ]
// uint64_t address [ UINT64_MAX if address not specified ]
// uuid_t uuid [ all zero's if uuid not specified ]
// uint32_t log2_pagesize [ process page size in log base
// 2, e.g. 4k pages are 12.
// 0 for unspecified ]
// uint32_t unused [ for alignment ]

if (strcmp("main bin spec", data_owner) == 0 && size >= 32) {
offset = fileoff;
uint32_t version;
if (m_data.GetU32(&offset, &version, 1) != nullptr && version == 1) {
uint32_t type = 0;
uint32_t binspec_type = 0;
uuid_t raw_uuid;
memset(raw_uuid, 0, sizeof(uuid_t));

if (m_data.GetU32(&offset, &type, 1) &&
if (m_data.GetU32(&offset, &binspec_type, 1) &&
m_data.GetU64(&offset, &address, 1) &&
m_data.CopyData(offset, sizeof(uuid_t), raw_uuid) != 0) {
uuid = UUID::fromOptionalData(raw_uuid, sizeof(uuid_t));
// convert the "main bin spec" type into our
// ObjectFile::BinaryType enum
switch (binspec_type) {
case 0:
type = eBinaryTypeUnknown;
break;
case 1:
type = eBinaryTypeKernel;
break;
case 2:
type = eBinaryTypeUser;
break;
case 3:
type = eBinaryTypeStandalone;
break;
}
return true;
}
}
Expand Down
4 changes: 3 additions & 1 deletion lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,9 @@ class ObjectFileMachO : public lldb_private::ObjectFile {

std::string GetIdentifierString() override;

bool GetCorefileMainBinaryInfo (lldb::addr_t &address, lldb_private::UUID &uuid) override;
bool GetCorefileMainBinaryInfo(lldb::addr_t &address,
lldb_private::UUID &uuid,
ObjectFile::BinaryType &type) override;

lldb::RegisterContextSP
GetThreadContextAtIndex(uint32_t idx, lldb_private::Thread &thread) override;
Expand Down
181 changes: 103 additions & 78 deletions lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -281,8 +281,9 @@ Status ProcessMachCore::DoLoadCore() {

addr_t objfile_binary_addr;
UUID objfile_binary_uuid;
if (core_objfile->GetCorefileMainBinaryInfo (objfile_binary_addr, objfile_binary_uuid))
{
ObjectFile::BinaryType type;
if (core_objfile->GetCorefileMainBinaryInfo(objfile_binary_addr,
objfile_binary_uuid, type)) {
if (objfile_binary_addr != LLDB_INVALID_ADDRESS)
{
m_mach_kernel_addr = objfile_binary_addr;
Expand All @@ -293,7 +294,7 @@ Status ProcessMachCore::DoLoadCore() {
m_mach_kernel_addr);
}
}

// This checks for the presence of an LC_IDENT string in a core file;
// LC_IDENT is very obsolete and should not be used in new code, but if the
// load command is present, let's use the contents.
Expand Down Expand Up @@ -326,58 +327,80 @@ Status ProcessMachCore::DoLoadCore() {
addr, corefile_identifier.c_str());
}
}
if (found_main_binary_definitively == false
&& corefile_identifier.find("EFI ") != std::string::npos) {
UUID uuid;

// In the case where we have an LC_NOTE specifying a standalone
// binary with only a UUID (and no load address) (iBoot, EFI, etc),
// then let's try to force a load of the binary and set its
// load address to 0-offset.
//
// The two forms this can come in is either a
// 'kern ver str' LC_NOTE with "EFI UUID=...."
// 'main bin spec' LC_NOTE with UUID and no load address.

if (found_main_binary_definitively == false &&
(corefile_identifier.find("EFI ") != std::string::npos ||
(objfile_binary_uuid.IsValid() &&
objfile_binary_addr == LLDB_INVALID_ADDRESS))) {
UUID uuid;
if (objfile_binary_uuid.IsValid()) {
uuid = objfile_binary_uuid;
LLDB_LOGF(log,
"ProcessMachCore::DoLoadCore: Using the main bin spec "
"LC_NOTE with UUID %s and no load address",
uuid.GetAsString().c_str());
} else {
if (corefile_identifier.find("UUID=") != std::string::npos) {
size_t p = corefile_identifier.find("UUID=") + strlen("UUID=");
std::string uuid_str = corefile_identifier.substr(p, 36);
uuid.SetFromStringRef(uuid_str);
size_t p = corefile_identifier.find("UUID=") + strlen("UUID=");
std::string uuid_str = corefile_identifier.substr(p, 36);
uuid.SetFromStringRef(uuid_str);
if (uuid.IsValid()) {
LLDB_LOGF(log,
"ProcessMachCore::DoLoadCore: Using the EFI "
"from LC_IDENT/LC_NOTE 'kern ver str' string: '%s'",
corefile_identifier.c_str());
}
}
if (uuid.IsValid()) {
LLDB_LOGF(log,
"ProcessMachCore::DoLoadCore: Using the EFI "
"from LC_IDENT/LC_NOTE 'kern ver str' string: '%s'",
corefile_identifier.c_str());

// We're only given a UUID here, not a load address.
// But there are python scripts in the EFI binary's dSYM which
// know how to relocate the binary to the correct load address.
// lldb only needs to locate & load the binary + dSYM.
ModuleSpec module_spec;
module_spec.GetUUID() = uuid;
module_spec.GetArchitecture() = GetTarget().GetArchitecture();

// Lookup UUID locally, before attempting dsymForUUID like action
FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths();
module_spec.GetSymbolFileSpec() =
Symbols::LocateExecutableSymbolFile(module_spec, search_paths);
if (module_spec.GetSymbolFileSpec()) {
ModuleSpec executable_module_spec =
Symbols::LocateExecutableObjectFile(module_spec);
if (FileSystem::Instance().Exists(
executable_module_spec.GetFileSpec())) {
module_spec.GetFileSpec() = executable_module_spec.GetFileSpec();
}
}

if (uuid.IsValid()) {
ModuleSpec module_spec;
module_spec.GetUUID() = uuid;
module_spec.GetArchitecture() = GetTarget().GetArchitecture();

// Lookup UUID locally, before attempting dsymForUUID-like action
FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths();
module_spec.GetSymbolFileSpec() =
Symbols::LocateExecutableSymbolFile(module_spec, search_paths);
if (module_spec.GetSymbolFileSpec()) {
ModuleSpec executable_module_spec =
Symbols::LocateExecutableObjectFile(module_spec);
if (FileSystem::Instance().Exists(
executable_module_spec.GetFileSpec())) {
module_spec.GetFileSpec() = executable_module_spec.GetFileSpec();
}
}

// Force a a dsymForUUID lookup, if that tool is available.
if (!module_spec.GetSymbolFileSpec())
Symbols::DownloadObjectAndSymbolFile(module_spec, true);

if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) {
ModuleSP module_sp(new Module(module_spec));
if (module_sp.get() && module_sp->GetObjectFile()) {
// Get the current target executable
ModuleSP exe_module_sp(GetTarget().GetExecutableModule());

// Make sure you don't already have the right module loaded
// and they will be uniqued
if (exe_module_sp.get() != module_sp.get())
GetTarget().SetExecutableModule(module_sp, eLoadDependentsNo);
}
// Force a a dsymForUUID lookup, if that tool is available.
if (!module_spec.GetSymbolFileSpec())
Symbols::DownloadObjectAndSymbolFile(module_spec, true);

// If we found a binary, load it at offset 0 and set our
// dyld_plugin to be the static plugin.
if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) {
ModuleSP module_sp(new Module(module_spec));
if (module_sp.get() && module_sp->GetObjectFile()) {
GetTarget().GetImages().AppendIfNeeded(module_sp, true);
GetTarget().SetExecutableModule(module_sp, eLoadDependentsNo);
found_main_binary_definitively = true;
bool changed = true;
module_sp->SetLoadAddress(GetTarget(), 0, true, changed);
ModuleList added_module;
added_module.Append(module_sp, false);
GetTarget().ModulesDidLoad(added_module);
m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic();
}
}
}
}

if (!found_main_binary_definitively &&
Expand Down Expand Up @@ -440,35 +463,37 @@ Status ProcessMachCore::DoLoadCore() {
}
}

// If we found both a user-process dyld and a kernel binary, we need to
// decide which to prefer.
if (GetCorefilePreference() == eKernelCorefile) {
if (m_mach_kernel_addr != LLDB_INVALID_ADDRESS) {
LLDB_LOGF(log,
"ProcessMachCore::DoLoadCore: Using kernel corefile image "
"at 0x%" PRIx64,
m_mach_kernel_addr);
m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic();
} else if (m_dyld_addr != LLDB_INVALID_ADDRESS) {
LLDB_LOGF(log,
"ProcessMachCore::DoLoadCore: Using user process dyld "
"image at 0x%" PRIx64,
m_dyld_addr);
m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic();
}
} else {
if (m_dyld_addr != LLDB_INVALID_ADDRESS) {
LLDB_LOGF(log,
"ProcessMachCore::DoLoadCore: Using user process dyld "
"image at 0x%" PRIx64,
m_dyld_addr);
m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic();
} else if (m_mach_kernel_addr != LLDB_INVALID_ADDRESS) {
LLDB_LOGF(log,
"ProcessMachCore::DoLoadCore: Using kernel corefile image "
"at 0x%" PRIx64,
m_mach_kernel_addr);
m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic();
if (m_dyld_plugin_name.IsEmpty()) {
// If we found both a user-process dyld and a kernel binary, we need to
// decide which to prefer.
if (GetCorefilePreference() == eKernelCorefile) {
if (m_mach_kernel_addr != LLDB_INVALID_ADDRESS) {
LLDB_LOGF(log,
"ProcessMachCore::DoLoadCore: Using kernel corefile image "
"at 0x%" PRIx64,
m_mach_kernel_addr);
m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic();
} else if (m_dyld_addr != LLDB_INVALID_ADDRESS) {
LLDB_LOGF(log,
"ProcessMachCore::DoLoadCore: Using user process dyld "
"image at 0x%" PRIx64,
m_dyld_addr);
m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic();
}
} else {
if (m_dyld_addr != LLDB_INVALID_ADDRESS) {
LLDB_LOGF(log,
"ProcessMachCore::DoLoadCore: Using user process dyld "
"image at 0x%" PRIx64,
m_dyld_addr);
m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic();
} else if (m_mach_kernel_addr != LLDB_INVALID_ADDRESS) {
LLDB_LOGF(log,
"ProcessMachCore::DoLoadCore: Using kernel corefile image "
"at 0x%" PRIx64,
m_mach_kernel_addr);
m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic();
}
}
}

Expand Down
14 changes: 14 additions & 0 deletions lldb/test/API/macosx/lc-note/firmware-corefile/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
MAKE_DSYM := NO
C_SOURCES := main.c
CFLAGS_EXTRAS := -Wl,-random_uuid

all: a.out b.out create-empty-corefile

create-empty-corefile:
$(MAKE) -f $(MAKEFILE_RULES) EXE=create-empty-corefile \
C_SOURCES=create-empty-corefile.c

b.out:
$(MAKE) VPATH=$(SRCDIR)/$* -I $(SRCDIR) -f $(SRCDIR)/bout.mk

include Makefile.rules
Loading