summaryrefslogtreecommitdiffstats
path: root/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp
diff options
context:
space:
mode:
authorJason Molenda <jmolenda@apple.com>2013-01-30 04:39:32 +0000
committerJason Molenda <jmolenda@apple.com>2013-01-30 04:39:32 +0000
commit6ba6d3d1790bf6615e8ea867ea9ff6947bda63a3 (patch)
treefd91e72f96ac098b78dcec1127cc60d29494aeb8 /lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp
parent26127bd746144842e245e7ecaa734a0219c34dbc (diff)
downloadbcm5719-llvm-6ba6d3d1790bf6615e8ea867ea9ff6947bda63a3.tar.gz
bcm5719-llvm-6ba6d3d1790bf6615e8ea867ea9ff6947bda63a3.zip
<rdar://problem/12491235>
Enhance lldb so it can search for a kernel in memory when attaching to a remote system. Remove some of the code that was doing this from ProcessMachCore and ProcessGDBRemote and put it in DynamicLoaderDarwinKernel. I've added a new setting, plugin.dynamic-loader.darwin-kernel.scan-type which can be set to none - for environments where reading random memory can cause a device crash basic - look at one fixed location in memory for a kernel load address, plus the contents of that address fast-scan - the default, tries "basic" and then looks for the kernel's mach header near the current pc value when lldb connects exhaustive-scan - on 32-bit targets, step through the entire range where the kernel can be loaded, looking for the kernel binary I don't have the setting set up correctly right now, I'm getting back unexpected values from the Property system, but I'll figure that out tomorrow and fix. Besides that, all of the different communication methods / types of kernels appear to be working correctly with these changes. llvm-svn: 173891
Diffstat (limited to 'lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp')
-rw-r--r--lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp349
1 files changed, 326 insertions, 23 deletions
diff --git a/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp b/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp
index 5aa8869091f..c9d338370ea 100644
--- a/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp
+++ b/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp
@@ -41,15 +41,39 @@
using namespace lldb;
using namespace lldb_private;
+// Progressively greater amounts of scanning we will allow
+// For some targets very early in startup, we can't do any random reads of memory or we can crash the device
+// so a setting is needed that can completely disable the KASLR scans.
+
+enum KASLRScanType
+{
+ eKASLRScanNone = 0, // No reading into the inferior at all
+ eKASLRScanLowgloAddresses, // Check one word of memory for a possible kernel addr, then see if a kernel is there
+ eKASLRScanNearPC, // Scan backwards from the current $pc looking for kernel; checking at 64 locations total
+ eKASLRScanExhaustiveScan // Scan through the entire possible kernel address range looking for a kernel
+};
+
+OptionEnumValueElement
+g_kaslr_kernel_scan_enum_values[] =
+{
+ { eKASLRScanNone, "none", "Do not read memory looking for a Darwin kernel when attaching." },
+ { eKASLRScanLowgloAddresses, "basic", "Check for the Darwin kernel's load addr in the lowglo page (boot-args=debug) only." },
+ { eKASLRScanNearPC, "fast-scan", "Scan near the pc value on attach to find the Darwin kernel's load address."},
+ { eKASLRScanExhaustiveScan, "exhaustive-scan", "Scan through the entire potential address range of Darwin kernel (only on 32-bit targets)."},
+ { 0, NULL, NULL }
+};
+
static PropertyDefinition
g_properties[] =
{
{ "load-kexts" , OptionValue::eTypeBoolean, true, true, NULL, NULL, "Automatically loads kext images when attaching to a kernel." },
+ { "scan-type", OptionValue::eTypeEnum, true, eKASLRScanNearPC, NULL, g_kaslr_kernel_scan_enum_values, "Control how many reads lldb will make while searching for a Darwin kernel on attach." },
{ NULL , OptionValue::eTypeInvalid, false, 0 , NULL, NULL, NULL }
};
enum {
- ePropertyLoadKexts
+ ePropertyLoadKexts,
+ ePropertyScanType
};
class DynamicLoaderDarwinKernelProperties : public Properties
@@ -74,14 +98,22 @@ public:
~DynamicLoaderDarwinKernelProperties()
{
}
-
+
bool
GetLoadKexts() const
{
const uint32_t idx = ePropertyLoadKexts;
return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0);
}
-
+
+ KASLRScanType
+ GetScanType() const
+ {
+ const uint32_t idx = ePropertyScanType;
+ return (KASLRScanType) m_collection_sp->SetPropertyAtIndexAsEnumeration (NULL, idx, g_properties[idx].default_uint_value);
+ }
+
+
};
typedef STD_SHARED_PTR(DynamicLoaderDarwinKernelProperties) DynamicLoaderDarwinKernelPropertiesSP;
@@ -103,49 +135,320 @@ GetGlobalProperties()
DynamicLoader *
DynamicLoaderDarwinKernel::CreateInstance (Process* process, bool force)
{
- bool create = force;
- if (!create)
+ if (!force)
{
+ // If the user provided an executable binary and it is not a kernel,
+ // this plugin should not create an instance.
Module* exe_module = process->GetTarget().GetExecutableModulePointer();
if (exe_module)
{
ObjectFile *object_file = exe_module->GetObjectFile();
if (object_file)
{
- create = (object_file->GetStrata() == ObjectFile::eStrataKernel);
+ if (object_file->GetStrata() != ObjectFile::eStrataKernel)
+ {
+ return NULL;
+ }
}
}
-
- if (create)
+
+ // If the target's architecture does not look like an Apple environment,
+ // this plugin should not create an instance.
+ const llvm::Triple &triple_ref = process->GetTarget().GetArchitecture().GetTriple();
+ switch (triple_ref.getOS())
{
- const llvm::Triple &triple_ref = process->GetTarget().GetArchitecture().GetTriple();
- switch (triple_ref.getOS())
+ case llvm::Triple::Darwin:
+ case llvm::Triple::MacOSX:
+ case llvm::Triple::IOS:
+ if (triple_ref.getVendor() != llvm::Triple::Apple)
+ {
+ return NULL;
+ }
+ break;
+ // If we have triple like armv7-unknown-unknown, we should try looking for a Darwin kernel.
+ case llvm::Triple::UnknownOS:
+ break;
+ default:
+ return NULL;
+ break;
+ }
+ }
+
+ // At this point if there is an ExecutableModule, it is a kernel and the Target is some variant of an Apple system.
+ // If the Process hasn't provided the kernel load address, we need to look around in memory to find it.
+
+ addr_t kernel_load_address = process->GetImageInfoAddress();
+ if (kernel_load_address == LLDB_INVALID_ADDRESS)
+ {
+ kernel_load_address = SearchForKernelAtSameLoadAddr (process);
+ if (kernel_load_address == LLDB_INVALID_ADDRESS)
+ {
+ kernel_load_address = SearchForKernelWithDebugHints (process);
+ if (kernel_load_address == LLDB_INVALID_ADDRESS)
{
- case llvm::Triple::Darwin:
- case llvm::Triple::MacOSX:
- case llvm::Triple::IOS:
- create = triple_ref.getVendor() == llvm::Triple::Apple;
- break;
- default:
- create = false;
- break;
+ kernel_load_address = SearchForKernelNearPC (process);
+ if (kernel_load_address == LLDB_INVALID_ADDRESS)
+ {
+ kernel_load_address = SearchForKernelViaExhaustiveSearch (process);
+ }
}
}
}
-
- if (create)
+
+ if (kernel_load_address != LLDB_INVALID_ADDRESS)
{
process->SetCanJIT(false);
- return new DynamicLoaderDarwinKernel (process);
+ return new DynamicLoaderDarwinKernel (process, kernel_load_address);
}
return NULL;
}
//----------------------------------------------------------------------
+// Check if the kernel binary is loaded in memory without a slide.
+// First verify that the ExecutableModule is a kernel before we proceed.
+// Returns the address of the kernel if one was found, else LLDB_INVALID_ADDRESS.
+//----------------------------------------------------------------------
+lldb::addr_t
+DynamicLoaderDarwinKernel::SearchForKernelAtSameLoadAddr (Process *process)
+{
+ Module *exe_module = process->GetTarget().GetExecutableModulePointer();
+ if (exe_module == NULL)
+ return LLDB_INVALID_ADDRESS;
+
+ ObjectFile *exe_objfile = exe_module->GetObjectFile();
+ if (exe_objfile == NULL)
+ return LLDB_INVALID_ADDRESS;
+
+ if (exe_objfile->GetType() != ObjectFile::eTypeExecutable || exe_objfile->GetStrata() != ObjectFile::eStrataKernel)
+ return LLDB_INVALID_ADDRESS;
+
+ if (!exe_objfile->GetHeaderAddress().IsValid())
+ return LLDB_INVALID_ADDRESS;
+
+ if (CheckForKernelImageAtAddress (exe_objfile->GetHeaderAddress().GetFileAddress(), process) == exe_module->GetUUID())
+ return exe_objfile->GetHeaderAddress().GetFileAddress();
+
+ return LLDB_INVALID_ADDRESS;
+}
+
+//----------------------------------------------------------------------
+// If the debug flag is included in the boot-args nvram setting, the kernel's load address
+// will be noted in the lowglo page at a fixed address
+// Returns the address of the kernel if one was found, else LLDB_INVALID_ADDRESS.
+//----------------------------------------------------------------------
+lldb::addr_t
+DynamicLoaderDarwinKernel::SearchForKernelWithDebugHints (Process *process)
+{
+#if 0
+ if (GetGlobalProperties()->GetScanType() == eKASLRScanNone)
+ return LLDB_INVALID_ADDRESS;
+#endif
+
+ Error read_err;
+ addr_t addr = LLDB_INVALID_ADDRESS;
+ if (process->GetTarget().GetArchitecture().GetAddressByteSize() == 8)
+ {
+ addr = process->ReadUnsignedIntegerFromMemory (0xffffff8000002010ULL, 8, LLDB_INVALID_ADDRESS, read_err);
+ }
+ else
+ {
+ addr = process->ReadUnsignedIntegerFromMemory (0xffff0110, 4, LLDB_INVALID_ADDRESS, read_err);
+ }
+
+ if (addr == 0)
+ addr = LLDB_INVALID_ADDRESS;
+
+ if (addr != LLDB_INVALID_ADDRESS)
+ {
+ if (CheckForKernelImageAtAddress (addr, process).IsValid())
+ return addr;
+ }
+
+ return LLDB_INVALID_ADDRESS;
+}
+
+//----------------------------------------------------------------------
+// If the kernel is currently executing when lldb attaches, and we don't have
+// a better way of finding the kernel's load address, try searching backwards
+// from the current pc value looking for the kernel's Mach header in memory.
+// Returns the address of the kernel if one was found, else LLDB_INVALID_ADDRESS.
+//----------------------------------------------------------------------
+lldb::addr_t
+DynamicLoaderDarwinKernel::SearchForKernelNearPC (Process *process)
+{
+#if 0
+ if (GetGlobalProperties()->GetScanType() == eKASLRScanNone
+ || GetGlobalProperties()->GetScanType() == eKASLRScanLowgloAddresses)
+ {
+ return LLDB_INVALID_ADDRESS;
+ }
+#endif
+
+ ThreadSP thread = process->GetThreadList().GetSelectedThread ();
+ if (thread.get() == NULL)
+ return LLDB_INVALID_ADDRESS;
+ addr_t pc = thread->GetRegisterContext ()->GetPC(LLDB_INVALID_ADDRESS);
+
+ if (pc == LLDB_INVALID_ADDRESS)
+ return LLDB_INVALID_ADDRESS;
+
+ addr_t kernel_range_low, kernel_range_high;
+ if (process->GetTarget().GetArchitecture().GetAddressByteSize() == 8)
+ {
+ kernel_range_low = 1ULL << 63;
+ kernel_range_high = UINT64_MAX;
+ }
+ else
+ {
+ kernel_range_low = 1ULL << 31;
+ kernel_range_high = UINT32_MAX;
+ }
+
+ // Outside the normal kernel address range, this is probably userland code running right now
+ if (pc < kernel_range_low)
+ LLDB_INVALID_ADDRESS;
+
+ // The kernel will load at at one megabyte boundary (0x100000), or at that boundary plus
+ // an offset of one page (0x1000) or two, depending on the device.
+
+ // Round the current pc down to the nearest one megabyte boundary - the place where we will start searching.
+ addr_t addr = pc & ~0xfffff;
+
+ int i = 0;
+ while (i < 32 && pc >= kernel_range_low)
+ {
+ if (CheckForKernelImageAtAddress (addr, process).IsValid())
+ return addr;
+ if (CheckForKernelImageAtAddress (addr + 0x1000, process).IsValid())
+ return addr + 0x1000;
+ if (CheckForKernelImageAtAddress (addr + 0x2000, process).IsValid())
+ return addr + 0x2000;
+ i++;
+ addr -= 0x100000;
+ }
+
+ return LLDB_INVALID_ADDRESS;
+}
+
+//----------------------------------------------------------------------
+// Scan through the valid address range for a kernel binary.
+// This is uselessly slow in 64-bit environments so we don't even try it.
+// This scan is not enabled by default even for 32-bit targets.
+// Returns the address of the kernel if one was found, else LLDB_INVALID_ADDRESS.
+//----------------------------------------------------------------------
+lldb::addr_t
+DynamicLoaderDarwinKernel::SearchForKernelViaExhaustiveSearch (Process *process)
+{
+#if 0
+ if (GetGlobalProperties()->GetScanType() != eKASLRScanExhaustiveScan)
+ {
+ return LLDB_INVALID_ADDRESS;
+ }
+#endif
+
+ addr_t kernel_range_low, kernel_range_high;
+ if (process->GetTarget().GetArchitecture().GetAddressByteSize() == 8)
+ {
+ kernel_range_low = 1ULL << 63;
+ kernel_range_high = UINT64_MAX;
+ }
+ else
+ {
+ kernel_range_low = 1ULL << 31;
+ kernel_range_high = UINT32_MAX;
+ }
+
+ // Stepping through memory at one-megabyte resolution looking for a kernel
+ // rarely works (fast enough) with a 64-bit address space -- for now, let's
+ // not even bother. We may be attaching to something which *isn't* a kernel
+ // and we don't want to spin for minutes on-end looking for a kernel.
+ if (process->GetTarget().GetArchitecture().GetAddressByteSize() == 8)
+ return LLDB_INVALID_ADDRESS;
+
+ addr_t addr = kernel_range_low;
+
+ while (addr >= kernel_range_low && addr < kernel_range_high)
+ {
+ if (CheckForKernelImageAtAddress (addr, process).IsValid())
+ return addr;
+ if (CheckForKernelImageAtAddress (addr + 0x1000, process).IsValid())
+ return addr + 0x1000;
+ if (CheckForKernelImageAtAddress (addr + 0x2000, process).IsValid())
+ return addr + 0x2000;
+ addr += 0x100000;
+ }
+ return LLDB_INVALID_ADDRESS;
+}
+
+//----------------------------------------------------------------------
+// Given an address in memory, look to see if there is a kernel image at that
+// address.
+// Returns a UUID; if a kernel was not found at that address, UUID.IsValid() will be false.
+//----------------------------------------------------------------------
+lldb_private::UUID
+DynamicLoaderDarwinKernel::CheckForKernelImageAtAddress (lldb::addr_t addr, Process *process)
+{
+ if (addr == LLDB_INVALID_ADDRESS)
+ return UUID();
+
+ // First try a quick test -- read the first 4 bytes and see if there is a valid Mach-O magic field there
+ // (the first field of the mach_header/mach_header_64 struct).
+
+ Error read_error;
+ uint64_t result = process->ReadUnsignedIntegerFromMemory (addr, 4, LLDB_INVALID_ADDRESS, read_error);
+ if (result != llvm::MachO::HeaderMagic64
+ && result != llvm::MachO::HeaderMagic32
+ && result != llvm::MachO::HeaderMagic32Swapped
+ && result != llvm::MachO::HeaderMagic64Swapped)
+ {
+ return UUID();
+ }
+
+ // Read the mach header and see whether it looks like a kernel
+ llvm::MachO::mach_header header;
+ if (process->DoReadMemory (addr, &header, sizeof(header), read_error) != sizeof(header))
+ return UUID();
+
+ if (header.magic == llvm::MachO::HeaderMagic32Swapped ||
+ header.magic == llvm::MachO::HeaderMagic64Swapped)
+ {
+ header.magic = llvm::ByteSwap_32(header.magic);
+ header.cputype = llvm::ByteSwap_32(header.cputype);
+ header.cpusubtype = llvm::ByteSwap_32(header.cpusubtype);
+ header.filetype = llvm::ByteSwap_32(header.filetype);
+ header.ncmds = llvm::ByteSwap_32(header.ncmds);
+ header.sizeofcmds = llvm::ByteSwap_32(header.sizeofcmds);
+ header.flags = llvm::ByteSwap_32(header.flags);
+ }
+
+ // A kernel is an executable which does not have the dynamic link object flag set.
+ if (header.filetype == llvm::MachO::HeaderFileTypeExecutable
+ && (header.flags & llvm::MachO::HeaderFlagBitIsDynamicLinkObject) == 0)
+ {
+ // Create a full module to get the UUID
+ ModuleSP memory_module_sp = process->ReadModuleFromMemory (FileSpec ("temp_mach_kernel", false), addr, false, false);
+ if (!memory_module_sp.get())
+ return UUID();
+
+ ObjectFile *exe_objfile = memory_module_sp->GetObjectFile();
+ if (exe_objfile == NULL)
+ return UUID();
+
+ if (exe_objfile->GetType() == ObjectFile::eTypeExecutable && exe_objfile->GetStrata() == ObjectFile::eStrataKernel)
+ {
+ return memory_module_sp->GetUUID();
+ }
+ }
+
+ return UUID();
+}
+
+//----------------------------------------------------------------------
// Constructor
//----------------------------------------------------------------------
-DynamicLoaderDarwinKernel::DynamicLoaderDarwinKernel (Process* process) :
+DynamicLoaderDarwinKernel::DynamicLoaderDarwinKernel (Process* process, lldb::addr_t kernel_addr) :
DynamicLoader(process),
+ m_kernel_load_address (kernel_addr),
m_kernel(),
m_kext_summary_header_ptr_addr (),
m_kext_summary_header_addr (),
@@ -492,7 +795,7 @@ DynamicLoaderDarwinKernel::LoadKernelModuleIfNeeded()
if (m_kernel.address == LLDB_INVALID_ADDRESS)
{
- m_kernel.address = m_process->GetImageInfoAddress ();
+ m_kernel.address = m_kernel_load_address;
if (m_kernel.address == LLDB_INVALID_ADDRESS && m_kernel.module_sp)
{
// We didn't get a hint from the process, so we will
OpenPOWER on IntegriCloud