
Background: dyld binaries often have extra symbols in their symbol table like "malloc" and "free" for the early bringup of dyld and we often don't want to set breakpoints in dynamic linker binaries. We also don't want to call the "malloc" or "free" function in dyld when a user writes an expression like "(void *)malloc(123)" so we need to avoid doing name lookups in dyld. We mark Modules as being dynamic link editors and this helps do correct lookups for breakpoints by name and function lookups. <rdar://problem/19716267> llvm-svn: 228261
5765 lines
287 KiB
C++
5765 lines
287 KiB
C++
//===-- ObjectFileMachO.cpp -------------------------------------*- C++ -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/ADT/StringRef.h"
|
|
|
|
#include "lldb/lldb-private-log.h"
|
|
#include "lldb/Core/ArchSpec.h"
|
|
#include "lldb/Core/DataBuffer.h"
|
|
#include "lldb/Core/Debugger.h"
|
|
#include "lldb/Core/Error.h"
|
|
#include "lldb/Core/FileSpecList.h"
|
|
#include "lldb/Core/Log.h"
|
|
#include "lldb/Core/Module.h"
|
|
#include "lldb/Core/ModuleSpec.h"
|
|
#include "lldb/Core/PluginManager.h"
|
|
#include "lldb/Core/RangeMap.h"
|
|
#include "lldb/Core/Section.h"
|
|
#include "lldb/Core/StreamFile.h"
|
|
#include "lldb/Core/StreamString.h"
|
|
#include "lldb/Core/Timer.h"
|
|
#include "lldb/Core/UUID.h"
|
|
#include "lldb/Host/Host.h"
|
|
#include "lldb/Host/FileSpec.h"
|
|
#include "lldb/Symbol/ClangNamespaceDecl.h"
|
|
#include "lldb/Symbol/DWARFCallFrameInfo.h"
|
|
#include "lldb/Symbol/ObjectFile.h"
|
|
#include "lldb/Target/Platform.h"
|
|
#include "lldb/Target/Process.h"
|
|
#include "lldb/Target/SectionLoadList.h"
|
|
#include "lldb/Target/Target.h"
|
|
#include "lldb/Target/Thread.h"
|
|
#include "lldb/Target/ThreadList.h"
|
|
#include "Plugins/Process/Utility/RegisterContextDarwin_arm.h"
|
|
#include "Plugins/Process/Utility/RegisterContextDarwin_arm64.h"
|
|
#include "Plugins/Process/Utility/RegisterContextDarwin_i386.h"
|
|
#include "Plugins/Process/Utility/RegisterContextDarwin_x86_64.h"
|
|
|
|
#include "lldb/Utility/SafeMachO.h"
|
|
|
|
#include "ObjectFileMachO.h"
|
|
|
|
#if defined (__APPLE__) && (defined (__arm__) || defined (__arm64__) || defined (__aarch64__))
|
|
// GetLLDBSharedCacheUUID() needs to call dlsym()
|
|
#include <dlfcn.h>
|
|
#endif
|
|
|
|
#ifndef __APPLE__
|
|
#include "Utility/UuidCompatibility.h"
|
|
#endif
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
using namespace llvm::MachO;
|
|
|
|
class RegisterContextDarwin_x86_64_Mach : public RegisterContextDarwin_x86_64
|
|
{
|
|
public:
|
|
RegisterContextDarwin_x86_64_Mach (lldb_private::Thread &thread, const DataExtractor &data) :
|
|
RegisterContextDarwin_x86_64 (thread, 0)
|
|
{
|
|
SetRegisterDataFrom_LC_THREAD (data);
|
|
}
|
|
|
|
virtual void
|
|
InvalidateAllRegisters ()
|
|
{
|
|
// Do nothing... registers are always valid...
|
|
}
|
|
|
|
void
|
|
SetRegisterDataFrom_LC_THREAD (const DataExtractor &data)
|
|
{
|
|
lldb::offset_t offset = 0;
|
|
SetError (GPRRegSet, Read, -1);
|
|
SetError (FPURegSet, Read, -1);
|
|
SetError (EXCRegSet, Read, -1);
|
|
bool done = false;
|
|
|
|
while (!done)
|
|
{
|
|
int flavor = data.GetU32 (&offset);
|
|
if (flavor == 0)
|
|
done = true;
|
|
else
|
|
{
|
|
uint32_t i;
|
|
uint32_t count = data.GetU32 (&offset);
|
|
switch (flavor)
|
|
{
|
|
case GPRRegSet:
|
|
for (i=0; i<count; ++i)
|
|
(&gpr.rax)[i] = data.GetU64(&offset);
|
|
SetError (GPRRegSet, Read, 0);
|
|
done = true;
|
|
|
|
break;
|
|
case FPURegSet:
|
|
// TODO: fill in FPU regs....
|
|
//SetError (FPURegSet, Read, -1);
|
|
done = true;
|
|
|
|
break;
|
|
case EXCRegSet:
|
|
exc.trapno = data.GetU32(&offset);
|
|
exc.err = data.GetU32(&offset);
|
|
exc.faultvaddr = data.GetU64(&offset);
|
|
SetError (EXCRegSet, Read, 0);
|
|
done = true;
|
|
break;
|
|
case 7:
|
|
case 8:
|
|
case 9:
|
|
// fancy flavors that encapsulate of the above
|
|
// flavors...
|
|
break;
|
|
|
|
default:
|
|
done = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static size_t
|
|
WriteRegister (RegisterContext *reg_ctx, const char *name, const char *alt_name, size_t reg_byte_size, Stream &data)
|
|
{
|
|
const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(name);
|
|
if (reg_info == NULL)
|
|
reg_info = reg_ctx->GetRegisterInfoByName(alt_name);
|
|
if (reg_info)
|
|
{
|
|
lldb_private::RegisterValue reg_value;
|
|
if (reg_ctx->ReadRegister(reg_info, reg_value))
|
|
{
|
|
if (reg_info->byte_size >= reg_byte_size)
|
|
data.Write(reg_value.GetBytes(), reg_byte_size);
|
|
else
|
|
{
|
|
data.Write(reg_value.GetBytes(), reg_info->byte_size);
|
|
for (size_t i=0, n = reg_byte_size - reg_info->byte_size; i<n; ++ i)
|
|
data.PutChar(0);
|
|
}
|
|
return reg_byte_size;
|
|
}
|
|
}
|
|
// Just write zeros if all else fails
|
|
for (size_t i=0; i<reg_byte_size; ++ i)
|
|
data.PutChar(0);
|
|
return reg_byte_size;
|
|
}
|
|
|
|
static bool
|
|
Create_LC_THREAD (Thread *thread, Stream &data)
|
|
{
|
|
RegisterContextSP reg_ctx_sp (thread->GetRegisterContext());
|
|
if (reg_ctx_sp)
|
|
{
|
|
RegisterContext *reg_ctx = reg_ctx_sp.get();
|
|
|
|
data.PutHex32 (GPRRegSet); // Flavor
|
|
data.PutHex32 (GPRWordCount);
|
|
WriteRegister (reg_ctx, "rax", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "rbx", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "rcx", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "rdx", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "rdi", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "rsi", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "rbp", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "rsp", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "r8", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "r9", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "r10", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "r11", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "r12", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "r13", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "r14", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "r15", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "rip", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "rflags", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "cs", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "fs", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "gs", NULL, 8, data);
|
|
|
|
// // Write out the FPU registers
|
|
// const size_t fpu_byte_size = sizeof(FPU);
|
|
// size_t bytes_written = 0;
|
|
// data.PutHex32 (FPURegSet);
|
|
// data.PutHex32 (fpu_byte_size/sizeof(uint64_t));
|
|
// bytes_written += data.PutHex32(0); // uint32_t pad[0]
|
|
// bytes_written += data.PutHex32(0); // uint32_t pad[1]
|
|
// bytes_written += WriteRegister (reg_ctx, "fcw", "fctrl", 2, data); // uint16_t fcw; // "fctrl"
|
|
// bytes_written += WriteRegister (reg_ctx, "fsw" , "fstat", 2, data); // uint16_t fsw; // "fstat"
|
|
// bytes_written += WriteRegister (reg_ctx, "ftw" , "ftag", 1, data); // uint8_t ftw; // "ftag"
|
|
// bytes_written += data.PutHex8 (0); // uint8_t pad1;
|
|
// bytes_written += WriteRegister (reg_ctx, "fop" , NULL, 2, data); // uint16_t fop; // "fop"
|
|
// bytes_written += WriteRegister (reg_ctx, "fioff", "ip", 4, data); // uint32_t ip; // "fioff"
|
|
// bytes_written += WriteRegister (reg_ctx, "fiseg", NULL, 2, data); // uint16_t cs; // "fiseg"
|
|
// bytes_written += data.PutHex16 (0); // uint16_t pad2;
|
|
// bytes_written += WriteRegister (reg_ctx, "dp", "fooff" , 4, data); // uint32_t dp; // "fooff"
|
|
// bytes_written += WriteRegister (reg_ctx, "foseg", NULL, 2, data); // uint16_t ds; // "foseg"
|
|
// bytes_written += data.PutHex16 (0); // uint16_t pad3;
|
|
// bytes_written += WriteRegister (reg_ctx, "mxcsr", NULL, 4, data); // uint32_t mxcsr;
|
|
// bytes_written += WriteRegister (reg_ctx, "mxcsrmask", NULL, 4, data);// uint32_t mxcsrmask;
|
|
// bytes_written += WriteRegister (reg_ctx, "stmm0", NULL, sizeof(MMSReg), data);
|
|
// bytes_written += WriteRegister (reg_ctx, "stmm1", NULL, sizeof(MMSReg), data);
|
|
// bytes_written += WriteRegister (reg_ctx, "stmm2", NULL, sizeof(MMSReg), data);
|
|
// bytes_written += WriteRegister (reg_ctx, "stmm3", NULL, sizeof(MMSReg), data);
|
|
// bytes_written += WriteRegister (reg_ctx, "stmm4", NULL, sizeof(MMSReg), data);
|
|
// bytes_written += WriteRegister (reg_ctx, "stmm5", NULL, sizeof(MMSReg), data);
|
|
// bytes_written += WriteRegister (reg_ctx, "stmm6", NULL, sizeof(MMSReg), data);
|
|
// bytes_written += WriteRegister (reg_ctx, "stmm7", NULL, sizeof(MMSReg), data);
|
|
// bytes_written += WriteRegister (reg_ctx, "xmm0" , NULL, sizeof(XMMReg), data);
|
|
// bytes_written += WriteRegister (reg_ctx, "xmm1" , NULL, sizeof(XMMReg), data);
|
|
// bytes_written += WriteRegister (reg_ctx, "xmm2" , NULL, sizeof(XMMReg), data);
|
|
// bytes_written += WriteRegister (reg_ctx, "xmm3" , NULL, sizeof(XMMReg), data);
|
|
// bytes_written += WriteRegister (reg_ctx, "xmm4" , NULL, sizeof(XMMReg), data);
|
|
// bytes_written += WriteRegister (reg_ctx, "xmm5" , NULL, sizeof(XMMReg), data);
|
|
// bytes_written += WriteRegister (reg_ctx, "xmm6" , NULL, sizeof(XMMReg), data);
|
|
// bytes_written += WriteRegister (reg_ctx, "xmm7" , NULL, sizeof(XMMReg), data);
|
|
// bytes_written += WriteRegister (reg_ctx, "xmm8" , NULL, sizeof(XMMReg), data);
|
|
// bytes_written += WriteRegister (reg_ctx, "xmm9" , NULL, sizeof(XMMReg), data);
|
|
// bytes_written += WriteRegister (reg_ctx, "xmm10", NULL, sizeof(XMMReg), data);
|
|
// bytes_written += WriteRegister (reg_ctx, "xmm11", NULL, sizeof(XMMReg), data);
|
|
// bytes_written += WriteRegister (reg_ctx, "xmm12", NULL, sizeof(XMMReg), data);
|
|
// bytes_written += WriteRegister (reg_ctx, "xmm13", NULL, sizeof(XMMReg), data);
|
|
// bytes_written += WriteRegister (reg_ctx, "xmm14", NULL, sizeof(XMMReg), data);
|
|
// bytes_written += WriteRegister (reg_ctx, "xmm15", NULL, sizeof(XMMReg), data);
|
|
//
|
|
// // Fill rest with zeros
|
|
// for (size_t i=0, n = fpu_byte_size - bytes_written; i<n; ++ i)
|
|
// data.PutChar(0);
|
|
|
|
// Write out the EXC registers
|
|
data.PutHex32 (EXCRegSet);
|
|
data.PutHex32 (EXCWordCount);
|
|
WriteRegister (reg_ctx, "trapno", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "err", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "faultvaddr", NULL, 8, data);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
protected:
|
|
virtual int
|
|
DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
virtual int
|
|
DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
virtual int
|
|
DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
virtual int
|
|
DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
virtual int
|
|
DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
virtual int
|
|
DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc)
|
|
{
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
|
|
class RegisterContextDarwin_i386_Mach : public RegisterContextDarwin_i386
|
|
{
|
|
public:
|
|
RegisterContextDarwin_i386_Mach (lldb_private::Thread &thread, const DataExtractor &data) :
|
|
RegisterContextDarwin_i386 (thread, 0)
|
|
{
|
|
SetRegisterDataFrom_LC_THREAD (data);
|
|
}
|
|
|
|
virtual void
|
|
InvalidateAllRegisters ()
|
|
{
|
|
// Do nothing... registers are always valid...
|
|
}
|
|
|
|
void
|
|
SetRegisterDataFrom_LC_THREAD (const DataExtractor &data)
|
|
{
|
|
lldb::offset_t offset = 0;
|
|
SetError (GPRRegSet, Read, -1);
|
|
SetError (FPURegSet, Read, -1);
|
|
SetError (EXCRegSet, Read, -1);
|
|
bool done = false;
|
|
|
|
while (!done)
|
|
{
|
|
int flavor = data.GetU32 (&offset);
|
|
if (flavor == 0)
|
|
done = true;
|
|
else
|
|
{
|
|
uint32_t i;
|
|
uint32_t count = data.GetU32 (&offset);
|
|
switch (flavor)
|
|
{
|
|
case GPRRegSet:
|
|
for (i=0; i<count; ++i)
|
|
(&gpr.eax)[i] = data.GetU32(&offset);
|
|
SetError (GPRRegSet, Read, 0);
|
|
done = true;
|
|
|
|
break;
|
|
case FPURegSet:
|
|
// TODO: fill in FPU regs....
|
|
//SetError (FPURegSet, Read, -1);
|
|
done = true;
|
|
|
|
break;
|
|
case EXCRegSet:
|
|
exc.trapno = data.GetU32(&offset);
|
|
exc.err = data.GetU32(&offset);
|
|
exc.faultvaddr = data.GetU32(&offset);
|
|
SetError (EXCRegSet, Read, 0);
|
|
done = true;
|
|
break;
|
|
case 7:
|
|
case 8:
|
|
case 9:
|
|
// fancy flavors that encapsulate of the above
|
|
// flavors...
|
|
break;
|
|
|
|
default:
|
|
done = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static size_t
|
|
WriteRegister (RegisterContext *reg_ctx, const char *name, const char *alt_name, size_t reg_byte_size, Stream &data)
|
|
{
|
|
const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(name);
|
|
if (reg_info == NULL)
|
|
reg_info = reg_ctx->GetRegisterInfoByName(alt_name);
|
|
if (reg_info)
|
|
{
|
|
lldb_private::RegisterValue reg_value;
|
|
if (reg_ctx->ReadRegister(reg_info, reg_value))
|
|
{
|
|
if (reg_info->byte_size >= reg_byte_size)
|
|
data.Write(reg_value.GetBytes(), reg_byte_size);
|
|
else
|
|
{
|
|
data.Write(reg_value.GetBytes(), reg_info->byte_size);
|
|
for (size_t i=0, n = reg_byte_size - reg_info->byte_size; i<n; ++ i)
|
|
data.PutChar(0);
|
|
}
|
|
return reg_byte_size;
|
|
}
|
|
}
|
|
// Just write zeros if all else fails
|
|
for (size_t i=0; i<reg_byte_size; ++ i)
|
|
data.PutChar(0);
|
|
return reg_byte_size;
|
|
}
|
|
|
|
static bool
|
|
Create_LC_THREAD (Thread *thread, Stream &data)
|
|
{
|
|
RegisterContextSP reg_ctx_sp (thread->GetRegisterContext());
|
|
if (reg_ctx_sp)
|
|
{
|
|
RegisterContext *reg_ctx = reg_ctx_sp.get();
|
|
|
|
data.PutHex32 (GPRRegSet); // Flavor
|
|
data.PutHex32 (GPRWordCount);
|
|
WriteRegister (reg_ctx, "eax", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "ebx", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "ecx", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "edx", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "edi", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "esi", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "ebp", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "esp", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "ss", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "eflags", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "eip", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "cs", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "ds", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "es", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "fs", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "gs", NULL, 4, data);
|
|
|
|
// Write out the EXC registers
|
|
data.PutHex32 (EXCRegSet);
|
|
data.PutHex32 (EXCWordCount);
|
|
WriteRegister (reg_ctx, "trapno", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "err", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "faultvaddr", NULL, 4, data);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
protected:
|
|
virtual int
|
|
DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
virtual int
|
|
DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
virtual int
|
|
DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
virtual int
|
|
DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
virtual int
|
|
DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
virtual int
|
|
DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc)
|
|
{
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
class RegisterContextDarwin_arm_Mach : public RegisterContextDarwin_arm
|
|
{
|
|
public:
|
|
RegisterContextDarwin_arm_Mach (lldb_private::Thread &thread, const DataExtractor &data) :
|
|
RegisterContextDarwin_arm (thread, 0)
|
|
{
|
|
SetRegisterDataFrom_LC_THREAD (data);
|
|
}
|
|
|
|
virtual void
|
|
InvalidateAllRegisters ()
|
|
{
|
|
// Do nothing... registers are always valid...
|
|
}
|
|
|
|
void
|
|
SetRegisterDataFrom_LC_THREAD (const DataExtractor &data)
|
|
{
|
|
lldb::offset_t offset = 0;
|
|
SetError (GPRRegSet, Read, -1);
|
|
SetError (FPURegSet, Read, -1);
|
|
SetError (EXCRegSet, Read, -1);
|
|
bool done = false;
|
|
|
|
while (!done)
|
|
{
|
|
int flavor = data.GetU32 (&offset);
|
|
uint32_t count = data.GetU32 (&offset);
|
|
lldb::offset_t next_thread_state = offset + (count * 4);
|
|
switch (flavor)
|
|
{
|
|
case GPRRegSet:
|
|
for (uint32_t i=0; i<count; ++i)
|
|
{
|
|
gpr.r[i] = data.GetU32(&offset);
|
|
}
|
|
|
|
// Note that gpr.cpsr is also copied by the above loop; this loop technically extends
|
|
// one element past the end of the gpr.r[] array.
|
|
|
|
SetError (GPRRegSet, Read, 0);
|
|
offset = next_thread_state;
|
|
break;
|
|
|
|
case FPURegSet:
|
|
{
|
|
uint8_t *fpu_reg_buf = (uint8_t*) &fpu.floats.s[0];
|
|
const int fpu_reg_buf_size = sizeof (fpu.floats);
|
|
if (data.ExtractBytes (offset, fpu_reg_buf_size, eByteOrderLittle, fpu_reg_buf) == fpu_reg_buf_size)
|
|
{
|
|
offset += fpu_reg_buf_size;
|
|
fpu.fpscr = data.GetU32(&offset);
|
|
SetError (FPURegSet, Read, 0);
|
|
}
|
|
else
|
|
{
|
|
done = true;
|
|
}
|
|
}
|
|
offset = next_thread_state;
|
|
break;
|
|
|
|
case EXCRegSet:
|
|
if (count == 3)
|
|
{
|
|
exc.exception = data.GetU32(&offset);
|
|
exc.fsr = data.GetU32(&offset);
|
|
exc.far = data.GetU32(&offset);
|
|
SetError (EXCRegSet, Read, 0);
|
|
}
|
|
done = true;
|
|
offset = next_thread_state;
|
|
break;
|
|
|
|
// Unknown register set flavor, stop trying to parse.
|
|
default:
|
|
done = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
static size_t
|
|
WriteRegister (RegisterContext *reg_ctx, const char *name, const char *alt_name, size_t reg_byte_size, Stream &data)
|
|
{
|
|
const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(name);
|
|
if (reg_info == NULL)
|
|
reg_info = reg_ctx->GetRegisterInfoByName(alt_name);
|
|
if (reg_info)
|
|
{
|
|
lldb_private::RegisterValue reg_value;
|
|
if (reg_ctx->ReadRegister(reg_info, reg_value))
|
|
{
|
|
if (reg_info->byte_size >= reg_byte_size)
|
|
data.Write(reg_value.GetBytes(), reg_byte_size);
|
|
else
|
|
{
|
|
data.Write(reg_value.GetBytes(), reg_info->byte_size);
|
|
for (size_t i=0, n = reg_byte_size - reg_info->byte_size; i<n; ++ i)
|
|
data.PutChar(0);
|
|
}
|
|
return reg_byte_size;
|
|
}
|
|
}
|
|
// Just write zeros if all else fails
|
|
for (size_t i=0; i<reg_byte_size; ++ i)
|
|
data.PutChar(0);
|
|
return reg_byte_size;
|
|
}
|
|
|
|
static bool
|
|
Create_LC_THREAD (Thread *thread, Stream &data)
|
|
{
|
|
RegisterContextSP reg_ctx_sp (thread->GetRegisterContext());
|
|
if (reg_ctx_sp)
|
|
{
|
|
RegisterContext *reg_ctx = reg_ctx_sp.get();
|
|
|
|
data.PutHex32 (GPRRegSet); // Flavor
|
|
data.PutHex32 (GPRWordCount);
|
|
WriteRegister (reg_ctx, "r0", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "r1", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "r2", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "r3", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "r4", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "r5", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "r6", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "r7", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "r8", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "r9", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "r10", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "r11", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "r12", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "sp", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "lr", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "pc", NULL, 4, data);
|
|
WriteRegister (reg_ctx, "cpsr", NULL, 4, data);
|
|
|
|
// Write out the EXC registers
|
|
// data.PutHex32 (EXCRegSet);
|
|
// data.PutHex32 (EXCWordCount);
|
|
// WriteRegister (reg_ctx, "exception", NULL, 4, data);
|
|
// WriteRegister (reg_ctx, "fsr", NULL, 4, data);
|
|
// WriteRegister (reg_ctx, "far", NULL, 4, data);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
protected:
|
|
virtual int
|
|
DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
virtual int
|
|
DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
virtual int
|
|
DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
virtual int
|
|
DoReadDBG (lldb::tid_t tid, int flavor, DBG &dbg)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
virtual int
|
|
DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
virtual int
|
|
DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
virtual int
|
|
DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
virtual int
|
|
DoWriteDBG (lldb::tid_t tid, int flavor, const DBG &dbg)
|
|
{
|
|
return -1;
|
|
}
|
|
};
|
|
|
|
class RegisterContextDarwin_arm64_Mach : public RegisterContextDarwin_arm64
|
|
{
|
|
public:
|
|
RegisterContextDarwin_arm64_Mach (lldb_private::Thread &thread, const DataExtractor &data) :
|
|
RegisterContextDarwin_arm64 (thread, 0)
|
|
{
|
|
SetRegisterDataFrom_LC_THREAD (data);
|
|
}
|
|
|
|
virtual void
|
|
InvalidateAllRegisters ()
|
|
{
|
|
// Do nothing... registers are always valid...
|
|
}
|
|
|
|
void
|
|
SetRegisterDataFrom_LC_THREAD (const DataExtractor &data)
|
|
{
|
|
lldb::offset_t offset = 0;
|
|
SetError (GPRRegSet, Read, -1);
|
|
SetError (FPURegSet, Read, -1);
|
|
SetError (EXCRegSet, Read, -1);
|
|
bool done = false;
|
|
while (!done)
|
|
{
|
|
int flavor = data.GetU32 (&offset);
|
|
uint32_t count = data.GetU32 (&offset);
|
|
lldb::offset_t next_thread_state = offset + (count * 4);
|
|
switch (flavor)
|
|
{
|
|
case GPRRegSet:
|
|
// x0-x29 + fp + lr + sp + pc (== 33 64-bit registers) plus cpsr (1 32-bit register)
|
|
if (count >= (33 * 2) + 1)
|
|
{
|
|
for (uint32_t i=0; i<33; ++i)
|
|
gpr.x[i] = data.GetU64(&offset);
|
|
gpr.cpsr = data.GetU32(&offset);
|
|
SetError (GPRRegSet, Read, 0);
|
|
}
|
|
offset = next_thread_state;
|
|
break;
|
|
case FPURegSet:
|
|
{
|
|
uint8_t *fpu_reg_buf = (uint8_t*) &fpu.v[0];
|
|
const int fpu_reg_buf_size = sizeof (fpu);
|
|
if (fpu_reg_buf_size == count
|
|
&& data.ExtractBytes (offset, fpu_reg_buf_size, eByteOrderLittle, fpu_reg_buf) == fpu_reg_buf_size)
|
|
{
|
|
SetError (FPURegSet, Read, 0);
|
|
}
|
|
else
|
|
{
|
|
done = true;
|
|
}
|
|
}
|
|
offset = next_thread_state;
|
|
break;
|
|
case EXCRegSet:
|
|
if (count == 4)
|
|
{
|
|
exc.far = data.GetU64(&offset);
|
|
exc.esr = data.GetU32(&offset);
|
|
exc.exception = data.GetU32(&offset);
|
|
SetError (EXCRegSet, Read, 0);
|
|
}
|
|
offset = next_thread_state;
|
|
break;
|
|
default:
|
|
done = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static size_t
|
|
WriteRegister (RegisterContext *reg_ctx, const char *name, const char *alt_name, size_t reg_byte_size, Stream &data)
|
|
{
|
|
const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(name);
|
|
if (reg_info == NULL)
|
|
reg_info = reg_ctx->GetRegisterInfoByName(alt_name);
|
|
if (reg_info)
|
|
{
|
|
lldb_private::RegisterValue reg_value;
|
|
if (reg_ctx->ReadRegister(reg_info, reg_value))
|
|
{
|
|
if (reg_info->byte_size >= reg_byte_size)
|
|
data.Write(reg_value.GetBytes(), reg_byte_size);
|
|
else
|
|
{
|
|
data.Write(reg_value.GetBytes(), reg_info->byte_size);
|
|
for (size_t i=0, n = reg_byte_size - reg_info->byte_size; i<n; ++ i)
|
|
data.PutChar(0);
|
|
}
|
|
return reg_byte_size;
|
|
}
|
|
}
|
|
// Just write zeros if all else fails
|
|
for (size_t i=0; i<reg_byte_size; ++ i)
|
|
data.PutChar(0);
|
|
return reg_byte_size;
|
|
}
|
|
|
|
static bool
|
|
Create_LC_THREAD (Thread *thread, Stream &data)
|
|
{
|
|
RegisterContextSP reg_ctx_sp (thread->GetRegisterContext());
|
|
if (reg_ctx_sp)
|
|
{
|
|
RegisterContext *reg_ctx = reg_ctx_sp.get();
|
|
|
|
data.PutHex32 (GPRRegSet); // Flavor
|
|
data.PutHex32 (GPRWordCount);
|
|
WriteRegister (reg_ctx, "x0", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "x1", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "x2", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "x3", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "x4", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "x5", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "x6", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "x7", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "x8", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "x9", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "x10", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "x11", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "x12", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "x13", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "x14", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "x15", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "x16", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "x17", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "x18", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "x19", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "x20", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "x21", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "x22", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "x23", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "x24", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "x25", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "x26", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "x27", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "x28", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "fp", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "lr", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "sp", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "pc", NULL, 8, data);
|
|
WriteRegister (reg_ctx, "cpsr", NULL, 4, data);
|
|
|
|
// Write out the EXC registers
|
|
// data.PutHex32 (EXCRegSet);
|
|
// data.PutHex32 (EXCWordCount);
|
|
// WriteRegister (reg_ctx, "far", NULL, 8, data);
|
|
// WriteRegister (reg_ctx, "esr", NULL, 4, data);
|
|
// WriteRegister (reg_ctx, "exception", NULL, 4, data);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
protected:
|
|
virtual int
|
|
DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
virtual int
|
|
DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
virtual int
|
|
DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
virtual int
|
|
DoReadDBG (lldb::tid_t tid, int flavor, DBG &dbg)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
virtual int
|
|
DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
virtual int
|
|
DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
virtual int
|
|
DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
virtual int
|
|
DoWriteDBG (lldb::tid_t tid, int flavor, const DBG &dbg)
|
|
{
|
|
return -1;
|
|
}
|
|
};
|
|
|
|
static uint32_t
|
|
MachHeaderSizeFromMagic(uint32_t magic)
|
|
{
|
|
switch (magic)
|
|
{
|
|
case MH_MAGIC:
|
|
case MH_CIGAM:
|
|
return sizeof(struct mach_header);
|
|
|
|
case MH_MAGIC_64:
|
|
case MH_CIGAM_64:
|
|
return sizeof(struct mach_header_64);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#define MACHO_NLIST_ARM_SYMBOL_IS_THUMB 0x0008
|
|
|
|
void
|
|
ObjectFileMachO::Initialize()
|
|
{
|
|
PluginManager::RegisterPlugin (GetPluginNameStatic(),
|
|
GetPluginDescriptionStatic(),
|
|
CreateInstance,
|
|
CreateMemoryInstance,
|
|
GetModuleSpecifications,
|
|
SaveCore);
|
|
}
|
|
|
|
void
|
|
ObjectFileMachO::Terminate()
|
|
{
|
|
PluginManager::UnregisterPlugin (CreateInstance);
|
|
}
|
|
|
|
|
|
lldb_private::ConstString
|
|
ObjectFileMachO::GetPluginNameStatic()
|
|
{
|
|
static ConstString g_name("mach-o");
|
|
return g_name;
|
|
}
|
|
|
|
const char *
|
|
ObjectFileMachO::GetPluginDescriptionStatic()
|
|
{
|
|
return "Mach-o object file reader (32 and 64 bit)";
|
|
}
|
|
|
|
ObjectFile *
|
|
ObjectFileMachO::CreateInstance (const lldb::ModuleSP &module_sp,
|
|
DataBufferSP& data_sp,
|
|
lldb::offset_t data_offset,
|
|
const FileSpec* file,
|
|
lldb::offset_t file_offset,
|
|
lldb::offset_t length)
|
|
{
|
|
if (!data_sp)
|
|
{
|
|
data_sp = file->MemoryMapFileContents(file_offset, length);
|
|
data_offset = 0;
|
|
}
|
|
|
|
if (ObjectFileMachO::MagicBytesMatch(data_sp, data_offset, length))
|
|
{
|
|
// Update the data to contain the entire file if it doesn't already
|
|
if (data_sp->GetByteSize() < length)
|
|
{
|
|
data_sp = file->MemoryMapFileContents(file_offset, length);
|
|
data_offset = 0;
|
|
}
|
|
std::unique_ptr<ObjectFile> objfile_ap(new ObjectFileMachO (module_sp, data_sp, data_offset, file, file_offset, length));
|
|
if (objfile_ap.get() && objfile_ap->ParseHeader())
|
|
return objfile_ap.release();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
ObjectFile *
|
|
ObjectFileMachO::CreateMemoryInstance (const lldb::ModuleSP &module_sp,
|
|
DataBufferSP& data_sp,
|
|
const ProcessSP &process_sp,
|
|
lldb::addr_t header_addr)
|
|
{
|
|
if (ObjectFileMachO::MagicBytesMatch(data_sp, 0, data_sp->GetByteSize()))
|
|
{
|
|
std::unique_ptr<ObjectFile> objfile_ap(new ObjectFileMachO (module_sp, data_sp, process_sp, header_addr));
|
|
if (objfile_ap.get() && objfile_ap->ParseHeader())
|
|
return objfile_ap.release();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
size_t
|
|
ObjectFileMachO::GetModuleSpecifications (const lldb_private::FileSpec& file,
|
|
lldb::DataBufferSP& data_sp,
|
|
lldb::offset_t data_offset,
|
|
lldb::offset_t file_offset,
|
|
lldb::offset_t length,
|
|
lldb_private::ModuleSpecList &specs)
|
|
{
|
|
const size_t initial_count = specs.GetSize();
|
|
|
|
if (ObjectFileMachO::MagicBytesMatch(data_sp, 0, data_sp->GetByteSize()))
|
|
{
|
|
DataExtractor data;
|
|
data.SetData(data_sp);
|
|
llvm::MachO::mach_header header;
|
|
if (ParseHeader (data, &data_offset, header))
|
|
{
|
|
size_t header_and_load_cmds = header.sizeofcmds + MachHeaderSizeFromMagic(header.magic);
|
|
if (header_and_load_cmds >= data_sp->GetByteSize())
|
|
{
|
|
data_sp = file.ReadFileContents(file_offset, header_and_load_cmds);
|
|
data.SetData(data_sp);
|
|
data_offset = MachHeaderSizeFromMagic(header.magic);
|
|
}
|
|
if (data_sp)
|
|
{
|
|
ModuleSpec spec;
|
|
spec.GetFileSpec() = file;
|
|
spec.SetObjectOffset(file_offset);
|
|
|
|
if (GetArchitecture (header, data, data_offset, spec.GetArchitecture()))
|
|
{
|
|
if (spec.GetArchitecture().IsValid())
|
|
{
|
|
GetUUID (header, data, data_offset, spec.GetUUID());
|
|
specs.Append(spec);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return specs.GetSize() - initial_count;
|
|
}
|
|
|
|
|
|
|
|
const ConstString &
|
|
ObjectFileMachO::GetSegmentNameTEXT()
|
|
{
|
|
static ConstString g_segment_name_TEXT ("__TEXT");
|
|
return g_segment_name_TEXT;
|
|
}
|
|
|
|
const ConstString &
|
|
ObjectFileMachO::GetSegmentNameDATA()
|
|
{
|
|
static ConstString g_segment_name_DATA ("__DATA");
|
|
return g_segment_name_DATA;
|
|
}
|
|
|
|
const ConstString &
|
|
ObjectFileMachO::GetSegmentNameOBJC()
|
|
{
|
|
static ConstString g_segment_name_OBJC ("__OBJC");
|
|
return g_segment_name_OBJC;
|
|
}
|
|
|
|
const ConstString &
|
|
ObjectFileMachO::GetSegmentNameLINKEDIT()
|
|
{
|
|
static ConstString g_section_name_LINKEDIT ("__LINKEDIT");
|
|
return g_section_name_LINKEDIT;
|
|
}
|
|
|
|
const ConstString &
|
|
ObjectFileMachO::GetSectionNameEHFrame()
|
|
{
|
|
static ConstString g_section_name_eh_frame ("__eh_frame");
|
|
return g_section_name_eh_frame;
|
|
}
|
|
|
|
bool
|
|
ObjectFileMachO::MagicBytesMatch (DataBufferSP& data_sp,
|
|
lldb::addr_t data_offset,
|
|
lldb::addr_t data_length)
|
|
{
|
|
DataExtractor data;
|
|
data.SetData (data_sp, data_offset, data_length);
|
|
lldb::offset_t offset = 0;
|
|
uint32_t magic = data.GetU32(&offset);
|
|
return MachHeaderSizeFromMagic(magic) != 0;
|
|
}
|
|
|
|
|
|
ObjectFileMachO::ObjectFileMachO(const lldb::ModuleSP &module_sp,
|
|
DataBufferSP& data_sp,
|
|
lldb::offset_t data_offset,
|
|
const FileSpec* file,
|
|
lldb::offset_t file_offset,
|
|
lldb::offset_t length) :
|
|
ObjectFile(module_sp, file, file_offset, length, data_sp, data_offset),
|
|
m_mach_segments(),
|
|
m_mach_sections(),
|
|
m_entry_point_address(),
|
|
m_thread_context_offsets(),
|
|
m_thread_context_offsets_valid(false)
|
|
{
|
|
::memset (&m_header, 0, sizeof(m_header));
|
|
::memset (&m_dysymtab, 0, sizeof(m_dysymtab));
|
|
}
|
|
|
|
ObjectFileMachO::ObjectFileMachO (const lldb::ModuleSP &module_sp,
|
|
lldb::DataBufferSP& header_data_sp,
|
|
const lldb::ProcessSP &process_sp,
|
|
lldb::addr_t header_addr) :
|
|
ObjectFile(module_sp, process_sp, header_addr, header_data_sp),
|
|
m_mach_segments(),
|
|
m_mach_sections(),
|
|
m_entry_point_address(),
|
|
m_thread_context_offsets(),
|
|
m_thread_context_offsets_valid(false)
|
|
{
|
|
::memset (&m_header, 0, sizeof(m_header));
|
|
::memset (&m_dysymtab, 0, sizeof(m_dysymtab));
|
|
}
|
|
|
|
ObjectFileMachO::~ObjectFileMachO()
|
|
{
|
|
}
|
|
|
|
bool
|
|
ObjectFileMachO::ParseHeader (DataExtractor &data,
|
|
lldb::offset_t *data_offset_ptr,
|
|
llvm::MachO::mach_header &header)
|
|
{
|
|
data.SetByteOrder (lldb::endian::InlHostByteOrder());
|
|
// Leave magic in the original byte order
|
|
header.magic = data.GetU32(data_offset_ptr);
|
|
bool can_parse = false;
|
|
bool is_64_bit = false;
|
|
switch (header.magic)
|
|
{
|
|
case MH_MAGIC:
|
|
data.SetByteOrder (lldb::endian::InlHostByteOrder());
|
|
data.SetAddressByteSize(4);
|
|
can_parse = true;
|
|
break;
|
|
|
|
case MH_MAGIC_64:
|
|
data.SetByteOrder (lldb::endian::InlHostByteOrder());
|
|
data.SetAddressByteSize(8);
|
|
can_parse = true;
|
|
is_64_bit = true;
|
|
break;
|
|
|
|
case MH_CIGAM:
|
|
data.SetByteOrder(lldb::endian::InlHostByteOrder() == eByteOrderBig ? eByteOrderLittle : eByteOrderBig);
|
|
data.SetAddressByteSize(4);
|
|
can_parse = true;
|
|
break;
|
|
|
|
case MH_CIGAM_64:
|
|
data.SetByteOrder(lldb::endian::InlHostByteOrder() == eByteOrderBig ? eByteOrderLittle : eByteOrderBig);
|
|
data.SetAddressByteSize(8);
|
|
is_64_bit = true;
|
|
can_parse = true;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (can_parse)
|
|
{
|
|
data.GetU32(data_offset_ptr, &header.cputype, 6);
|
|
if (is_64_bit)
|
|
*data_offset_ptr += 4;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
memset(&header, 0, sizeof(header));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
ObjectFileMachO::ParseHeader ()
|
|
{
|
|
ModuleSP module_sp(GetModule());
|
|
if (module_sp)
|
|
{
|
|
lldb_private::Mutex::Locker locker(module_sp->GetMutex());
|
|
bool can_parse = false;
|
|
lldb::offset_t offset = 0;
|
|
m_data.SetByteOrder (lldb::endian::InlHostByteOrder());
|
|
// Leave magic in the original byte order
|
|
m_header.magic = m_data.GetU32(&offset);
|
|
switch (m_header.magic)
|
|
{
|
|
case MH_MAGIC:
|
|
m_data.SetByteOrder (lldb::endian::InlHostByteOrder());
|
|
m_data.SetAddressByteSize(4);
|
|
can_parse = true;
|
|
break;
|
|
|
|
case MH_MAGIC_64:
|
|
m_data.SetByteOrder (lldb::endian::InlHostByteOrder());
|
|
m_data.SetAddressByteSize(8);
|
|
can_parse = true;
|
|
break;
|
|
|
|
case MH_CIGAM:
|
|
m_data.SetByteOrder(lldb::endian::InlHostByteOrder() == eByteOrderBig ? eByteOrderLittle : eByteOrderBig);
|
|
m_data.SetAddressByteSize(4);
|
|
can_parse = true;
|
|
break;
|
|
|
|
case MH_CIGAM_64:
|
|
m_data.SetByteOrder(lldb::endian::InlHostByteOrder() == eByteOrderBig ? eByteOrderLittle : eByteOrderBig);
|
|
m_data.SetAddressByteSize(8);
|
|
can_parse = true;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (can_parse)
|
|
{
|
|
m_data.GetU32(&offset, &m_header.cputype, 6);
|
|
|
|
|
|
ArchSpec mach_arch;
|
|
|
|
if (GetArchitecture (mach_arch))
|
|
{
|
|
// Check if the module has a required architecture
|
|
const ArchSpec &module_arch = module_sp->GetArchitecture();
|
|
if (module_arch.IsValid() && !module_arch.IsCompatibleMatch(mach_arch))
|
|
return false;
|
|
|
|
if (SetModulesArchitecture (mach_arch))
|
|
{
|
|
const size_t header_and_lc_size = m_header.sizeofcmds + MachHeaderSizeFromMagic(m_header.magic);
|
|
if (m_data.GetByteSize() < header_and_lc_size)
|
|
{
|
|
DataBufferSP data_sp;
|
|
ProcessSP process_sp (m_process_wp.lock());
|
|
if (process_sp)
|
|
{
|
|
data_sp = ReadMemory (process_sp, m_memory_addr, header_and_lc_size);
|
|
}
|
|
else
|
|
{
|
|
// Read in all only the load command data from the file on disk
|
|
data_sp = m_file.ReadFileContents(m_file_offset, header_and_lc_size);
|
|
if (data_sp->GetByteSize() != header_and_lc_size)
|
|
return false;
|
|
}
|
|
if (data_sp)
|
|
m_data.SetData (data_sp);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
memset(&m_header, 0, sizeof(struct mach_header));
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
ByteOrder
|
|
ObjectFileMachO::GetByteOrder () const
|
|
{
|
|
return m_data.GetByteOrder ();
|
|
}
|
|
|
|
bool
|
|
ObjectFileMachO::IsExecutable() const
|
|
{
|
|
return m_header.filetype == MH_EXECUTE;
|
|
}
|
|
|
|
uint32_t
|
|
ObjectFileMachO::GetAddressByteSize () const
|
|
{
|
|
return m_data.GetAddressByteSize ();
|
|
}
|
|
|
|
AddressClass
|
|
ObjectFileMachO::GetAddressClass (lldb::addr_t file_addr)
|
|
{
|
|
Symtab *symtab = GetSymtab();
|
|
if (symtab)
|
|
{
|
|
Symbol *symbol = symtab->FindSymbolContainingFileAddress(file_addr);
|
|
if (symbol)
|
|
{
|
|
if (symbol->ValueIsAddress())
|
|
{
|
|
SectionSP section_sp (symbol->GetAddress().GetSection());
|
|
if (section_sp)
|
|
{
|
|
const lldb::SectionType section_type = section_sp->GetType();
|
|
switch (section_type)
|
|
{
|
|
case eSectionTypeInvalid:
|
|
return eAddressClassUnknown;
|
|
|
|
case eSectionTypeCode:
|
|
if (m_header.cputype == llvm::MachO::CPU_TYPE_ARM)
|
|
{
|
|
// For ARM we have a bit in the n_desc field of the symbol
|
|
// that tells us ARM/Thumb which is bit 0x0008.
|
|
if (symbol->GetFlags() & MACHO_NLIST_ARM_SYMBOL_IS_THUMB)
|
|
return eAddressClassCodeAlternateISA;
|
|
}
|
|
return eAddressClassCode;
|
|
|
|
case eSectionTypeContainer:
|
|
return eAddressClassUnknown;
|
|
|
|
case eSectionTypeData:
|
|
case eSectionTypeDataCString:
|
|
case eSectionTypeDataCStringPointers:
|
|
case eSectionTypeDataSymbolAddress:
|
|
case eSectionTypeData4:
|
|
case eSectionTypeData8:
|
|
case eSectionTypeData16:
|
|
case eSectionTypeDataPointers:
|
|
case eSectionTypeZeroFill:
|
|
case eSectionTypeDataObjCMessageRefs:
|
|
case eSectionTypeDataObjCCFStrings:
|
|
return eAddressClassData;
|
|
|
|
case eSectionTypeDebug:
|
|
case eSectionTypeDWARFDebugAbbrev:
|
|
case eSectionTypeDWARFDebugAranges:
|
|
case eSectionTypeDWARFDebugFrame:
|
|
case eSectionTypeDWARFDebugInfo:
|
|
case eSectionTypeDWARFDebugLine:
|
|
case eSectionTypeDWARFDebugLoc:
|
|
case eSectionTypeDWARFDebugMacInfo:
|
|
case eSectionTypeDWARFDebugPubNames:
|
|
case eSectionTypeDWARFDebugPubTypes:
|
|
case eSectionTypeDWARFDebugRanges:
|
|
case eSectionTypeDWARFDebugStr:
|
|
case eSectionTypeDWARFAppleNames:
|
|
case eSectionTypeDWARFAppleTypes:
|
|
case eSectionTypeDWARFAppleNamespaces:
|
|
case eSectionTypeDWARFAppleObjC:
|
|
return eAddressClassDebug;
|
|
|
|
case eSectionTypeEHFrame:
|
|
case eSectionTypeCompactUnwind:
|
|
return eAddressClassRuntime;
|
|
|
|
case eSectionTypeELFSymbolTable:
|
|
case eSectionTypeELFDynamicSymbols:
|
|
case eSectionTypeELFRelocationEntries:
|
|
case eSectionTypeELFDynamicLinkInfo:
|
|
case eSectionTypeOther:
|
|
return eAddressClassUnknown;
|
|
}
|
|
}
|
|
}
|
|
|
|
const SymbolType symbol_type = symbol->GetType();
|
|
switch (symbol_type)
|
|
{
|
|
case eSymbolTypeAny: return eAddressClassUnknown;
|
|
case eSymbolTypeAbsolute: return eAddressClassUnknown;
|
|
|
|
case eSymbolTypeCode:
|
|
case eSymbolTypeTrampoline:
|
|
case eSymbolTypeResolver:
|
|
if (m_header.cputype == llvm::MachO::CPU_TYPE_ARM)
|
|
{
|
|
// For ARM we have a bit in the n_desc field of the symbol
|
|
// that tells us ARM/Thumb which is bit 0x0008.
|
|
if (symbol->GetFlags() & MACHO_NLIST_ARM_SYMBOL_IS_THUMB)
|
|
return eAddressClassCodeAlternateISA;
|
|
}
|
|
return eAddressClassCode;
|
|
|
|
case eSymbolTypeData: return eAddressClassData;
|
|
case eSymbolTypeRuntime: return eAddressClassRuntime;
|
|
case eSymbolTypeException: return eAddressClassRuntime;
|
|
case eSymbolTypeSourceFile: return eAddressClassDebug;
|
|
case eSymbolTypeHeaderFile: return eAddressClassDebug;
|
|
case eSymbolTypeObjectFile: return eAddressClassDebug;
|
|
case eSymbolTypeCommonBlock: return eAddressClassDebug;
|
|
case eSymbolTypeBlock: return eAddressClassDebug;
|
|
case eSymbolTypeLocal: return eAddressClassData;
|
|
case eSymbolTypeParam: return eAddressClassData;
|
|
case eSymbolTypeVariable: return eAddressClassData;
|
|
case eSymbolTypeVariableType: return eAddressClassDebug;
|
|
case eSymbolTypeLineEntry: return eAddressClassDebug;
|
|
case eSymbolTypeLineHeader: return eAddressClassDebug;
|
|
case eSymbolTypeScopeBegin: return eAddressClassDebug;
|
|
case eSymbolTypeScopeEnd: return eAddressClassDebug;
|
|
case eSymbolTypeAdditional: return eAddressClassUnknown;
|
|
case eSymbolTypeCompiler: return eAddressClassDebug;
|
|
case eSymbolTypeInstrumentation:return eAddressClassDebug;
|
|
case eSymbolTypeUndefined: return eAddressClassUnknown;
|
|
case eSymbolTypeObjCClass: return eAddressClassRuntime;
|
|
case eSymbolTypeObjCMetaClass: return eAddressClassRuntime;
|
|
case eSymbolTypeObjCIVar: return eAddressClassRuntime;
|
|
case eSymbolTypeReExported: return eAddressClassRuntime;
|
|
}
|
|
}
|
|
}
|
|
return eAddressClassUnknown;
|
|
}
|
|
|
|
Symtab *
|
|
ObjectFileMachO::GetSymtab()
|
|
{
|
|
ModuleSP module_sp(GetModule());
|
|
if (module_sp)
|
|
{
|
|
lldb_private::Mutex::Locker locker(module_sp->GetMutex());
|
|
if (m_symtab_ap.get() == NULL)
|
|
{
|
|
m_symtab_ap.reset(new Symtab(this));
|
|
Mutex::Locker symtab_locker (m_symtab_ap->GetMutex());
|
|
ParseSymtab ();
|
|
m_symtab_ap->Finalize ();
|
|
}
|
|
}
|
|
return m_symtab_ap.get();
|
|
}
|
|
|
|
bool
|
|
ObjectFileMachO::IsStripped ()
|
|
{
|
|
if (m_dysymtab.cmd == 0)
|
|
{
|
|
ModuleSP module_sp(GetModule());
|
|
if (module_sp)
|
|
{
|
|
lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic);
|
|
for (uint32_t i=0; i<m_header.ncmds; ++i)
|
|
{
|
|
const lldb::offset_t load_cmd_offset = offset;
|
|
|
|
load_command lc;
|
|
if (m_data.GetU32(&offset, &lc.cmd, 2) == NULL)
|
|
break;
|
|
if (lc.cmd == LC_DYSYMTAB)
|
|
{
|
|
m_dysymtab.cmd = lc.cmd;
|
|
m_dysymtab.cmdsize = lc.cmdsize;
|
|
if (m_data.GetU32 (&offset, &m_dysymtab.ilocalsym, (sizeof(m_dysymtab) / sizeof(uint32_t)) - 2) == NULL)
|
|
{
|
|
// Clear m_dysymtab if we were unable to read all items from the load command
|
|
::memset (&m_dysymtab, 0, sizeof(m_dysymtab));
|
|
}
|
|
}
|
|
offset = load_cmd_offset + lc.cmdsize;
|
|
}
|
|
}
|
|
}
|
|
if (m_dysymtab.cmd)
|
|
return m_dysymtab.nlocalsym <= 1;
|
|
return false;
|
|
}
|
|
|
|
void
|
|
ObjectFileMachO::CreateSections (SectionList &unified_section_list)
|
|
{
|
|
if (!m_sections_ap.get())
|
|
{
|
|
m_sections_ap.reset(new SectionList());
|
|
|
|
const bool is_dsym = (m_header.filetype == MH_DSYM);
|
|
lldb::user_id_t segID = 0;
|
|
lldb::user_id_t sectID = 0;
|
|
lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic);
|
|
uint32_t i;
|
|
const bool is_core = GetType() == eTypeCoreFile;
|
|
//bool dump_sections = false;
|
|
ModuleSP module_sp (GetModule());
|
|
// First look up any LC_ENCRYPTION_INFO load commands
|
|
typedef RangeArray<uint32_t, uint32_t, 8> EncryptedFileRanges;
|
|
EncryptedFileRanges encrypted_file_ranges;
|
|
encryption_info_command encryption_cmd;
|
|
for (i=0; i<m_header.ncmds; ++i)
|
|
{
|
|
const lldb::offset_t load_cmd_offset = offset;
|
|
if (m_data.GetU32(&offset, &encryption_cmd, 2) == NULL)
|
|
break;
|
|
|
|
if (encryption_cmd.cmd == LC_ENCRYPTION_INFO)
|
|
{
|
|
if (m_data.GetU32(&offset, &encryption_cmd.cryptoff, 3))
|
|
{
|
|
if (encryption_cmd.cryptid != 0)
|
|
{
|
|
EncryptedFileRanges::Entry entry;
|
|
entry.SetRangeBase(encryption_cmd.cryptoff);
|
|
entry.SetByteSize(encryption_cmd.cryptsize);
|
|
encrypted_file_ranges.Append(entry);
|
|
}
|
|
}
|
|
}
|
|
offset = load_cmd_offset + encryption_cmd.cmdsize;
|
|
}
|
|
|
|
bool section_file_addresses_changed = false;
|
|
|
|
offset = MachHeaderSizeFromMagic(m_header.magic);
|
|
|
|
struct segment_command_64 load_cmd;
|
|
for (i=0; i<m_header.ncmds; ++i)
|
|
{
|
|
const lldb::offset_t load_cmd_offset = offset;
|
|
if (m_data.GetU32(&offset, &load_cmd, 2) == NULL)
|
|
break;
|
|
|
|
if (load_cmd.cmd == LC_SEGMENT || load_cmd.cmd == LC_SEGMENT_64)
|
|
{
|
|
if (m_data.GetU8(&offset, (uint8_t*)load_cmd.segname, 16))
|
|
{
|
|
bool add_section = true;
|
|
bool add_to_unified = true;
|
|
ConstString const_segname (load_cmd.segname, std::min<size_t>(strlen(load_cmd.segname), sizeof(load_cmd.segname)));
|
|
|
|
SectionSP unified_section_sp(unified_section_list.FindSectionByName(const_segname));
|
|
if (is_dsym && unified_section_sp)
|
|
{
|
|
if (const_segname == GetSegmentNameLINKEDIT())
|
|
{
|
|
// We need to keep the __LINKEDIT segment private to this object file only
|
|
add_to_unified = false;
|
|
}
|
|
else
|
|
{
|
|
// This is the dSYM file and this section has already been created by
|
|
// the object file, no need to create it.
|
|
add_section = false;
|
|
}
|
|
}
|
|
load_cmd.vmaddr = m_data.GetAddress(&offset);
|
|
load_cmd.vmsize = m_data.GetAddress(&offset);
|
|
load_cmd.fileoff = m_data.GetAddress(&offset);
|
|
load_cmd.filesize = m_data.GetAddress(&offset);
|
|
if (m_length != 0 && load_cmd.filesize != 0)
|
|
{
|
|
if (load_cmd.fileoff > m_length)
|
|
{
|
|
// We have a load command that says it extends past the end of the file. This is likely
|
|
// a corrupt file. We don't have any way to return an error condition here (this method
|
|
// was likely invoked from something like ObjectFile::GetSectionList()) -- all we can do
|
|
// is null out the SectionList vector and if a process has been set up, dump a message
|
|
// to stdout. The most common case here is core file debugging with a truncated file.
|
|
const char *lc_segment_name = load_cmd.cmd == LC_SEGMENT_64 ? "LC_SEGMENT_64" : "LC_SEGMENT";
|
|
module_sp->ReportWarning("load command %u %s has a fileoff (0x%" PRIx64 ") that extends beyond the end of the file (0x%" PRIx64 "), ignoring this section",
|
|
i,
|
|
lc_segment_name,
|
|
load_cmd.fileoff,
|
|
m_length);
|
|
|
|
load_cmd.fileoff = 0;
|
|
load_cmd.filesize = 0;
|
|
}
|
|
|
|
if (load_cmd.fileoff + load_cmd.filesize > m_length)
|
|
{
|
|
// We have a load command that says it extends past the end of the file. This is likely
|
|
// a corrupt file. We don't have any way to return an error condition here (this method
|
|
// was likely invoked from something like ObjectFile::GetSectionList()) -- all we can do
|
|
// is null out the SectionList vector and if a process has been set up, dump a message
|
|
// to stdout. The most common case here is core file debugging with a truncated file.
|
|
const char *lc_segment_name = load_cmd.cmd == LC_SEGMENT_64 ? "LC_SEGMENT_64" : "LC_SEGMENT";
|
|
GetModule()->ReportWarning("load command %u %s has a fileoff + filesize (0x%" PRIx64 ") that extends beyond the end of the file (0x%" PRIx64 "), the segment will be truncated to match",
|
|
i,
|
|
lc_segment_name,
|
|
load_cmd.fileoff + load_cmd.filesize,
|
|
m_length);
|
|
|
|
// Tuncase the length
|
|
load_cmd.filesize = m_length - load_cmd.fileoff;
|
|
}
|
|
}
|
|
if (m_data.GetU32(&offset, &load_cmd.maxprot, 4))
|
|
{
|
|
|
|
const bool segment_is_encrypted = (load_cmd.flags & SG_PROTECTED_VERSION_1) != 0;
|
|
|
|
// Keep a list of mach segments around in case we need to
|
|
// get at data that isn't stored in the abstracted Sections.
|
|
m_mach_segments.push_back (load_cmd);
|
|
|
|
// Use a segment ID of the segment index shifted left by 8 so they
|
|
// never conflict with any of the sections.
|
|
SectionSP segment_sp;
|
|
if (add_section && (const_segname || is_core))
|
|
{
|
|
segment_sp.reset(new Section (module_sp, // Module to which this section belongs
|
|
this, // Object file to which this sections belongs
|
|
++segID << 8, // Section ID is the 1 based segment index shifted right by 8 bits as not to collide with any of the 256 section IDs that are possible
|
|
const_segname, // Name of this section
|
|
eSectionTypeContainer, // This section is a container of other sections.
|
|
load_cmd.vmaddr, // File VM address == addresses as they are found in the object file
|
|
load_cmd.vmsize, // VM size in bytes of this section
|
|
load_cmd.fileoff, // Offset to the data for this section in the file
|
|
load_cmd.filesize, // Size in bytes of this section as found in the file
|
|
0, // Segments have no alignment information
|
|
load_cmd.flags)); // Flags for this section
|
|
|
|
segment_sp->SetIsEncrypted (segment_is_encrypted);
|
|
m_sections_ap->AddSection(segment_sp);
|
|
if (add_to_unified)
|
|
unified_section_list.AddSection(segment_sp);
|
|
}
|
|
else if (unified_section_sp)
|
|
{
|
|
if (is_dsym && unified_section_sp->GetFileAddress() != load_cmd.vmaddr)
|
|
{
|
|
// Check to see if the module was read from memory?
|
|
if (module_sp->GetObjectFile()->GetHeaderAddress().IsValid())
|
|
{
|
|
// We have a module that is in memory and needs to have its
|
|
// file address adjusted. We need to do this because when we
|
|
// load a file from memory, its addresses will be slid already,
|
|
// yet the addresses in the new symbol file will still be unslid.
|
|
// Since everything is stored as section offset, this shouldn't
|
|
// cause any problems.
|
|
|
|
// Make sure we've parsed the symbol table from the
|
|
// ObjectFile before we go around changing its Sections.
|
|
module_sp->GetObjectFile()->GetSymtab();
|
|
// eh_frame would present the same problems but we parse that on
|
|
// a per-function basis as-needed so it's more difficult to
|
|
// remove its use of the Sections. Realistically, the environments
|
|
// where this code path will be taken will not have eh_frame sections.
|
|
|
|
unified_section_sp->SetFileAddress(load_cmd.vmaddr);
|
|
|
|
// Notify the module that the section addresses have been changed once
|
|
// we're done so any file-address caches can be updated.
|
|
section_file_addresses_changed = true;
|
|
}
|
|
}
|
|
m_sections_ap->AddSection(unified_section_sp);
|
|
}
|
|
|
|
struct section_64 sect64;
|
|
::memset (§64, 0, sizeof(sect64));
|
|
// Push a section into our mach sections for the section at
|
|
// index zero (NO_SECT) if we don't have any mach sections yet...
|
|
if (m_mach_sections.empty())
|
|
m_mach_sections.push_back(sect64);
|
|
uint32_t segment_sect_idx;
|
|
const lldb::user_id_t first_segment_sectID = sectID + 1;
|
|
|
|
|
|
const uint32_t num_u32s = load_cmd.cmd == LC_SEGMENT ? 7 : 8;
|
|
for (segment_sect_idx=0; segment_sect_idx<load_cmd.nsects; ++segment_sect_idx)
|
|
{
|
|
if (m_data.GetU8(&offset, (uint8_t*)sect64.sectname, sizeof(sect64.sectname)) == NULL)
|
|
break;
|
|
if (m_data.GetU8(&offset, (uint8_t*)sect64.segname, sizeof(sect64.segname)) == NULL)
|
|
break;
|
|
sect64.addr = m_data.GetAddress(&offset);
|
|
sect64.size = m_data.GetAddress(&offset);
|
|
|
|
if (m_data.GetU32(&offset, §64.offset, num_u32s) == NULL)
|
|
break;
|
|
|
|
// Keep a list of mach sections around in case we need to
|
|
// get at data that isn't stored in the abstracted Sections.
|
|
m_mach_sections.push_back (sect64);
|
|
|
|
if (add_section)
|
|
{
|
|
ConstString section_name (sect64.sectname, std::min<size_t>(strlen(sect64.sectname), sizeof(sect64.sectname)));
|
|
if (!const_segname)
|
|
{
|
|
// We have a segment with no name so we need to conjure up
|
|
// segments that correspond to the section's segname if there
|
|
// isn't already such a section. If there is such a section,
|
|
// we resize the section so that it spans all sections.
|
|
// We also mark these sections as fake so address matches don't
|
|
// hit if they land in the gaps between the child sections.
|
|
const_segname.SetTrimmedCStringWithLength(sect64.segname, sizeof(sect64.segname));
|
|
segment_sp = unified_section_list.FindSectionByName (const_segname);
|
|
if (segment_sp.get())
|
|
{
|
|
Section *segment = segment_sp.get();
|
|
// Grow the section size as needed.
|
|
const lldb::addr_t sect64_min_addr = sect64.addr;
|
|
const lldb::addr_t sect64_max_addr = sect64_min_addr + sect64.size;
|
|
const lldb::addr_t curr_seg_byte_size = segment->GetByteSize();
|
|
const lldb::addr_t curr_seg_min_addr = segment->GetFileAddress();
|
|
const lldb::addr_t curr_seg_max_addr = curr_seg_min_addr + curr_seg_byte_size;
|
|
if (sect64_min_addr >= curr_seg_min_addr)
|
|
{
|
|
const lldb::addr_t new_seg_byte_size = sect64_max_addr - curr_seg_min_addr;
|
|
// Only grow the section size if needed
|
|
if (new_seg_byte_size > curr_seg_byte_size)
|
|
segment->SetByteSize (new_seg_byte_size);
|
|
}
|
|
else
|
|
{
|
|
// We need to change the base address of the segment and
|
|
// adjust the child section offsets for all existing children.
|
|
const lldb::addr_t slide_amount = sect64_min_addr - curr_seg_min_addr;
|
|
segment->Slide(slide_amount, false);
|
|
segment->GetChildren().Slide(-slide_amount, false);
|
|
segment->SetByteSize (curr_seg_max_addr - sect64_min_addr);
|
|
}
|
|
|
|
// Grow the section size as needed.
|
|
if (sect64.offset)
|
|
{
|
|
const lldb::addr_t segment_min_file_offset = segment->GetFileOffset();
|
|
const lldb::addr_t segment_max_file_offset = segment_min_file_offset + segment->GetFileSize();
|
|
|
|
const lldb::addr_t section_min_file_offset = sect64.offset;
|
|
const lldb::addr_t section_max_file_offset = section_min_file_offset + sect64.size;
|
|
const lldb::addr_t new_file_offset = std::min (section_min_file_offset, segment_min_file_offset);
|
|
const lldb::addr_t new_file_size = std::max (section_max_file_offset, segment_max_file_offset) - new_file_offset;
|
|
segment->SetFileOffset (new_file_offset);
|
|
segment->SetFileSize (new_file_size);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Create a fake section for the section's named segment
|
|
segment_sp.reset(new Section (segment_sp, // Parent section
|
|
module_sp, // Module to which this section belongs
|
|
this, // Object file to which this section belongs
|
|
++segID << 8, // Section ID is the 1 based segment index shifted right by 8 bits as not to collide with any of the 256 section IDs that are possible
|
|
const_segname, // Name of this section
|
|
eSectionTypeContainer, // This section is a container of other sections.
|
|
sect64.addr, // File VM address == addresses as they are found in the object file
|
|
sect64.size, // VM size in bytes of this section
|
|
sect64.offset, // Offset to the data for this section in the file
|
|
sect64.offset ? sect64.size : 0, // Size in bytes of this section as found in the file
|
|
sect64.align,
|
|
load_cmd.flags)); // Flags for this section
|
|
segment_sp->SetIsFake(true);
|
|
|
|
m_sections_ap->AddSection(segment_sp);
|
|
if (add_to_unified)
|
|
unified_section_list.AddSection(segment_sp);
|
|
segment_sp->SetIsEncrypted (segment_is_encrypted);
|
|
}
|
|
}
|
|
assert (segment_sp.get());
|
|
|
|
lldb::SectionType sect_type = eSectionTypeOther;
|
|
|
|
if (sect64.flags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS))
|
|
sect_type = eSectionTypeCode;
|
|
else
|
|
{
|
|
uint32_t mach_sect_type = sect64.flags & SECTION_TYPE;
|
|
static ConstString g_sect_name_objc_data ("__objc_data");
|
|
static ConstString g_sect_name_objc_msgrefs ("__objc_msgrefs");
|
|
static ConstString g_sect_name_objc_selrefs ("__objc_selrefs");
|
|
static ConstString g_sect_name_objc_classrefs ("__objc_classrefs");
|
|
static ConstString g_sect_name_objc_superrefs ("__objc_superrefs");
|
|
static ConstString g_sect_name_objc_const ("__objc_const");
|
|
static ConstString g_sect_name_objc_classlist ("__objc_classlist");
|
|
static ConstString g_sect_name_cfstring ("__cfstring");
|
|
|
|
static ConstString g_sect_name_dwarf_debug_abbrev ("__debug_abbrev");
|
|
static ConstString g_sect_name_dwarf_debug_aranges ("__debug_aranges");
|
|
static ConstString g_sect_name_dwarf_debug_frame ("__debug_frame");
|
|
static ConstString g_sect_name_dwarf_debug_info ("__debug_info");
|
|
static ConstString g_sect_name_dwarf_debug_line ("__debug_line");
|
|
static ConstString g_sect_name_dwarf_debug_loc ("__debug_loc");
|
|
static ConstString g_sect_name_dwarf_debug_macinfo ("__debug_macinfo");
|
|
static ConstString g_sect_name_dwarf_debug_pubnames ("__debug_pubnames");
|
|
static ConstString g_sect_name_dwarf_debug_pubtypes ("__debug_pubtypes");
|
|
static ConstString g_sect_name_dwarf_debug_ranges ("__debug_ranges");
|
|
static ConstString g_sect_name_dwarf_debug_str ("__debug_str");
|
|
static ConstString g_sect_name_dwarf_apple_names ("__apple_names");
|
|
static ConstString g_sect_name_dwarf_apple_types ("__apple_types");
|
|
static ConstString g_sect_name_dwarf_apple_namespaces ("__apple_namespac");
|
|
static ConstString g_sect_name_dwarf_apple_objc ("__apple_objc");
|
|
static ConstString g_sect_name_eh_frame ("__eh_frame");
|
|
static ConstString g_sect_name_compact_unwind ("__unwind_info");
|
|
static ConstString g_sect_name_text ("__text");
|
|
static ConstString g_sect_name_data ("__data");
|
|
|
|
|
|
if (section_name == g_sect_name_dwarf_debug_abbrev)
|
|
sect_type = eSectionTypeDWARFDebugAbbrev;
|
|
else if (section_name == g_sect_name_dwarf_debug_aranges)
|
|
sect_type = eSectionTypeDWARFDebugAranges;
|
|
else if (section_name == g_sect_name_dwarf_debug_frame)
|
|
sect_type = eSectionTypeDWARFDebugFrame;
|
|
else if (section_name == g_sect_name_dwarf_debug_info)
|
|
sect_type = eSectionTypeDWARFDebugInfo;
|
|
else if (section_name == g_sect_name_dwarf_debug_line)
|
|
sect_type = eSectionTypeDWARFDebugLine;
|
|
else if (section_name == g_sect_name_dwarf_debug_loc)
|
|
sect_type = eSectionTypeDWARFDebugLoc;
|
|
else if (section_name == g_sect_name_dwarf_debug_macinfo)
|
|
sect_type = eSectionTypeDWARFDebugMacInfo;
|
|
else if (section_name == g_sect_name_dwarf_debug_pubnames)
|
|
sect_type = eSectionTypeDWARFDebugPubNames;
|
|
else if (section_name == g_sect_name_dwarf_debug_pubtypes)
|
|
sect_type = eSectionTypeDWARFDebugPubTypes;
|
|
else if (section_name == g_sect_name_dwarf_debug_ranges)
|
|
sect_type = eSectionTypeDWARFDebugRanges;
|
|
else if (section_name == g_sect_name_dwarf_debug_str)
|
|
sect_type = eSectionTypeDWARFDebugStr;
|
|
else if (section_name == g_sect_name_dwarf_apple_names)
|
|
sect_type = eSectionTypeDWARFAppleNames;
|
|
else if (section_name == g_sect_name_dwarf_apple_types)
|
|
sect_type = eSectionTypeDWARFAppleTypes;
|
|
else if (section_name == g_sect_name_dwarf_apple_namespaces)
|
|
sect_type = eSectionTypeDWARFAppleNamespaces;
|
|
else if (section_name == g_sect_name_dwarf_apple_objc)
|
|
sect_type = eSectionTypeDWARFAppleObjC;
|
|
else if (section_name == g_sect_name_objc_selrefs)
|
|
sect_type = eSectionTypeDataCStringPointers;
|
|
else if (section_name == g_sect_name_objc_msgrefs)
|
|
sect_type = eSectionTypeDataObjCMessageRefs;
|
|
else if (section_name == g_sect_name_eh_frame)
|
|
sect_type = eSectionTypeEHFrame;
|
|
else if (section_name == g_sect_name_compact_unwind)
|
|
sect_type = eSectionTypeCompactUnwind;
|
|
else if (section_name == g_sect_name_cfstring)
|
|
sect_type = eSectionTypeDataObjCCFStrings;
|
|
else if (section_name == g_sect_name_objc_data ||
|
|
section_name == g_sect_name_objc_classrefs ||
|
|
section_name == g_sect_name_objc_superrefs ||
|
|
section_name == g_sect_name_objc_const ||
|
|
section_name == g_sect_name_objc_classlist)
|
|
{
|
|
sect_type = eSectionTypeDataPointers;
|
|
}
|
|
|
|
if (sect_type == eSectionTypeOther)
|
|
{
|
|
switch (mach_sect_type)
|
|
{
|
|
// TODO: categorize sections by other flags for regular sections
|
|
case S_REGULAR:
|
|
if (section_name == g_sect_name_text)
|
|
sect_type = eSectionTypeCode;
|
|
else if (section_name == g_sect_name_data)
|
|
sect_type = eSectionTypeData;
|
|
else
|
|
sect_type = eSectionTypeOther;
|
|
break;
|
|
case S_ZEROFILL: sect_type = eSectionTypeZeroFill; break;
|
|
case S_CSTRING_LITERALS: sect_type = eSectionTypeDataCString; break; // section with only literal C strings
|
|
case S_4BYTE_LITERALS: sect_type = eSectionTypeData4; break; // section with only 4 byte literals
|
|
case S_8BYTE_LITERALS: sect_type = eSectionTypeData8; break; // section with only 8 byte literals
|
|
case S_LITERAL_POINTERS: sect_type = eSectionTypeDataPointers; break; // section with only pointers to literals
|
|
case S_NON_LAZY_SYMBOL_POINTERS: sect_type = eSectionTypeDataPointers; break; // section with only non-lazy symbol pointers
|
|
case S_LAZY_SYMBOL_POINTERS: sect_type = eSectionTypeDataPointers; break; // section with only lazy symbol pointers
|
|
case S_SYMBOL_STUBS: sect_type = eSectionTypeCode; break; // section with only symbol stubs, byte size of stub in the reserved2 field
|
|
case S_MOD_INIT_FUNC_POINTERS: sect_type = eSectionTypeDataPointers; break; // section with only function pointers for initialization
|
|
case S_MOD_TERM_FUNC_POINTERS: sect_type = eSectionTypeDataPointers; break; // section with only function pointers for termination
|
|
case S_COALESCED: sect_type = eSectionTypeOther; break;
|
|
case S_GB_ZEROFILL: sect_type = eSectionTypeZeroFill; break;
|
|
case S_INTERPOSING: sect_type = eSectionTypeCode; break; // section with only pairs of function pointers for interposing
|
|
case S_16BYTE_LITERALS: sect_type = eSectionTypeData16; break; // section with only 16 byte literals
|
|
case S_DTRACE_DOF: sect_type = eSectionTypeDebug; break;
|
|
case S_LAZY_DYLIB_SYMBOL_POINTERS: sect_type = eSectionTypeDataPointers; break;
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
|
|
SectionSP section_sp(new Section (segment_sp,
|
|
module_sp,
|
|
this,
|
|
++sectID,
|
|
section_name,
|
|
sect_type,
|
|
sect64.addr - segment_sp->GetFileAddress(),
|
|
sect64.size,
|
|
sect64.offset,
|
|
sect64.offset == 0 ? 0 : sect64.size,
|
|
sect64.align,
|
|
sect64.flags));
|
|
// Set the section to be encrypted to match the segment
|
|
|
|
bool section_is_encrypted = false;
|
|
if (!segment_is_encrypted && load_cmd.filesize != 0)
|
|
section_is_encrypted = encrypted_file_ranges.FindEntryThatContains(sect64.offset) != NULL;
|
|
|
|
section_sp->SetIsEncrypted (segment_is_encrypted || section_is_encrypted);
|
|
segment_sp->GetChildren().AddSection(section_sp);
|
|
|
|
if (segment_sp->IsFake())
|
|
{
|
|
segment_sp.reset();
|
|
const_segname.Clear();
|
|
}
|
|
}
|
|
}
|
|
if (segment_sp && is_dsym)
|
|
{
|
|
if (first_segment_sectID <= sectID)
|
|
{
|
|
lldb::user_id_t sect_uid;
|
|
for (sect_uid = first_segment_sectID; sect_uid <= sectID; ++sect_uid)
|
|
{
|
|
SectionSP curr_section_sp(segment_sp->GetChildren().FindSectionByID (sect_uid));
|
|
SectionSP next_section_sp;
|
|
if (sect_uid + 1 <= sectID)
|
|
next_section_sp = segment_sp->GetChildren().FindSectionByID (sect_uid+1);
|
|
|
|
if (curr_section_sp.get())
|
|
{
|
|
if (curr_section_sp->GetByteSize() == 0)
|
|
{
|
|
if (next_section_sp.get() != NULL)
|
|
curr_section_sp->SetByteSize ( next_section_sp->GetFileAddress() - curr_section_sp->GetFileAddress() );
|
|
else
|
|
curr_section_sp->SetByteSize ( load_cmd.vmsize );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (load_cmd.cmd == LC_DYSYMTAB)
|
|
{
|
|
m_dysymtab.cmd = load_cmd.cmd;
|
|
m_dysymtab.cmdsize = load_cmd.cmdsize;
|
|
m_data.GetU32 (&offset, &m_dysymtab.ilocalsym, (sizeof(m_dysymtab) / sizeof(uint32_t)) - 2);
|
|
}
|
|
|
|
offset = load_cmd_offset + load_cmd.cmdsize;
|
|
}
|
|
|
|
|
|
if (section_file_addresses_changed && module_sp.get())
|
|
{
|
|
module_sp->SectionFileAddressesChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
class MachSymtabSectionInfo
|
|
{
|
|
public:
|
|
|
|
MachSymtabSectionInfo (SectionList *section_list) :
|
|
m_section_list (section_list),
|
|
m_section_infos()
|
|
{
|
|
// Get the number of sections down to a depth of 1 to include
|
|
// all segments and their sections, but no other sections that
|
|
// may be added for debug map or
|
|
m_section_infos.resize(section_list->GetNumSections(1));
|
|
}
|
|
|
|
|
|
SectionSP
|
|
GetSection (uint8_t n_sect, addr_t file_addr)
|
|
{
|
|
if (n_sect == 0)
|
|
return SectionSP();
|
|
if (n_sect < m_section_infos.size())
|
|
{
|
|
if (!m_section_infos[n_sect].section_sp)
|
|
{
|
|
SectionSP section_sp (m_section_list->FindSectionByID (n_sect));
|
|
m_section_infos[n_sect].section_sp = section_sp;
|
|
if (section_sp)
|
|
{
|
|
m_section_infos[n_sect].vm_range.SetBaseAddress (section_sp->GetFileAddress());
|
|
m_section_infos[n_sect].vm_range.SetByteSize (section_sp->GetByteSize());
|
|
}
|
|
else
|
|
{
|
|
Host::SystemLog (Host::eSystemLogError, "error: unable to find section for section %u\n", n_sect);
|
|
}
|
|
}
|
|
if (m_section_infos[n_sect].vm_range.Contains(file_addr))
|
|
{
|
|
// Symbol is in section.
|
|
return m_section_infos[n_sect].section_sp;
|
|
}
|
|
else if (m_section_infos[n_sect].vm_range.GetByteSize () == 0 &&
|
|
m_section_infos[n_sect].vm_range.GetBaseAddress() == file_addr)
|
|
{
|
|
// Symbol is in section with zero size, but has the same start
|
|
// address as the section. This can happen with linker symbols
|
|
// (symbols that start with the letter 'l' or 'L'.
|
|
return m_section_infos[n_sect].section_sp;
|
|
}
|
|
}
|
|
return m_section_list->FindSectionContainingFileAddress(file_addr);
|
|
}
|
|
|
|
protected:
|
|
struct SectionInfo
|
|
{
|
|
SectionInfo () :
|
|
vm_range(),
|
|
section_sp ()
|
|
{
|
|
}
|
|
|
|
VMRange vm_range;
|
|
SectionSP section_sp;
|
|
};
|
|
SectionList *m_section_list;
|
|
std::vector<SectionInfo> m_section_infos;
|
|
};
|
|
|
|
struct TrieEntry
|
|
{
|
|
TrieEntry () :
|
|
name(),
|
|
address(LLDB_INVALID_ADDRESS),
|
|
flags (0),
|
|
other(0),
|
|
import_name()
|
|
{
|
|
}
|
|
|
|
void
|
|
Clear ()
|
|
{
|
|
name.Clear();
|
|
address = LLDB_INVALID_ADDRESS;
|
|
flags = 0;
|
|
other = 0;
|
|
import_name.Clear();
|
|
}
|
|
|
|
void
|
|
Dump () const
|
|
{
|
|
printf ("0x%16.16llx 0x%16.16llx 0x%16.16llx \"%s\"",
|
|
static_cast<unsigned long long>(address),
|
|
static_cast<unsigned long long>(flags),
|
|
static_cast<unsigned long long>(other), name.GetCString());
|
|
if (import_name)
|
|
printf (" -> \"%s\"\n", import_name.GetCString());
|
|
else
|
|
printf ("\n");
|
|
}
|
|
ConstString name;
|
|
uint64_t address;
|
|
uint64_t flags;
|
|
uint64_t other;
|
|
ConstString import_name;
|
|
};
|
|
|
|
struct TrieEntryWithOffset
|
|
{
|
|
lldb::offset_t nodeOffset;
|
|
TrieEntry entry;
|
|
|
|
TrieEntryWithOffset (lldb::offset_t offset) :
|
|
nodeOffset (offset),
|
|
entry()
|
|
{
|
|
}
|
|
|
|
void
|
|
Dump (uint32_t idx) const
|
|
{
|
|
printf ("[%3u] 0x%16.16llx: ", idx,
|
|
static_cast<unsigned long long>(nodeOffset));
|
|
entry.Dump();
|
|
}
|
|
|
|
bool
|
|
operator<(const TrieEntryWithOffset& other) const
|
|
{
|
|
return ( nodeOffset < other.nodeOffset );
|
|
}
|
|
};
|
|
|
|
static void
|
|
ParseTrieEntries (DataExtractor &data,
|
|
lldb::offset_t offset,
|
|
std::vector<llvm::StringRef> &nameSlices,
|
|
std::set<lldb::addr_t> &resolver_addresses,
|
|
std::vector<TrieEntryWithOffset>& output)
|
|
{
|
|
if (!data.ValidOffset(offset))
|
|
return;
|
|
|
|
const uint64_t terminalSize = data.GetULEB128(&offset);
|
|
lldb::offset_t children_offset = offset + terminalSize;
|
|
if ( terminalSize != 0 ) {
|
|
TrieEntryWithOffset e (offset);
|
|
e.entry.flags = data.GetULEB128(&offset);
|
|
const char *import_name = NULL;
|
|
if ( e.entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) {
|
|
e.entry.address = 0;
|
|
e.entry.other = data.GetULEB128(&offset); // dylib ordinal
|
|
import_name = data.GetCStr(&offset);
|
|
}
|
|
else {
|
|
e.entry.address = data.GetULEB128(&offset);
|
|
if ( e.entry.flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER )
|
|
{
|
|
//resolver_addresses.insert(e.entry.address);
|
|
e.entry.other = data.GetULEB128(&offset);
|
|
resolver_addresses.insert(e.entry.other);
|
|
}
|
|
else
|
|
e.entry.other = 0;
|
|
}
|
|
// Only add symbols that are reexport symbols with a valid import name
|
|
if (EXPORT_SYMBOL_FLAGS_REEXPORT & e.entry.flags && import_name && import_name[0])
|
|
{
|
|
std::string name;
|
|
if (!nameSlices.empty())
|
|
{
|
|
for (auto name_slice: nameSlices)
|
|
name.append(name_slice.data(), name_slice.size());
|
|
}
|
|
if (name.size() > 1)
|
|
{
|
|
// Skip the leading '_'
|
|
e.entry.name.SetCStringWithLength(name.c_str() + 1,name.size() - 1);
|
|
}
|
|
if (import_name)
|
|
{
|
|
// Skip the leading '_'
|
|
e.entry.import_name.SetCString(import_name+1);
|
|
}
|
|
output.push_back(e);
|
|
}
|
|
}
|
|
|
|
const uint8_t childrenCount = data.GetU8(&children_offset);
|
|
for (uint8_t i=0; i < childrenCount; ++i) {
|
|
nameSlices.push_back(data.GetCStr(&children_offset));
|
|
lldb::offset_t childNodeOffset = data.GetULEB128(&children_offset);
|
|
if (childNodeOffset)
|
|
{
|
|
ParseTrieEntries(data,
|
|
childNodeOffset,
|
|
nameSlices,
|
|
resolver_addresses,
|
|
output);
|
|
}
|
|
nameSlices.pop_back();
|
|
}
|
|
}
|
|
|
|
size_t
|
|
ObjectFileMachO::ParseSymtab ()
|
|
{
|
|
Timer scoped_timer(__PRETTY_FUNCTION__,
|
|
"ObjectFileMachO::ParseSymtab () module = %s",
|
|
m_file.GetFilename().AsCString(""));
|
|
ModuleSP module_sp (GetModule());
|
|
if (!module_sp)
|
|
return 0;
|
|
|
|
struct symtab_command symtab_load_command = { 0, 0, 0, 0, 0, 0 };
|
|
struct linkedit_data_command function_starts_load_command = { 0, 0, 0, 0 };
|
|
struct dyld_info_command dyld_info = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
typedef AddressDataArray<lldb::addr_t, bool, 100> FunctionStarts;
|
|
FunctionStarts function_starts;
|
|
lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic);
|
|
uint32_t i;
|
|
FileSpecList dylib_files;
|
|
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SYMBOLS));
|
|
|
|
for (i=0; i<m_header.ncmds; ++i)
|
|
{
|
|
const lldb::offset_t cmd_offset = offset;
|
|
// Read in the load command and load command size
|
|
struct load_command lc;
|
|
if (m_data.GetU32(&offset, &lc, 2) == NULL)
|
|
break;
|
|
// Watch for the symbol table load command
|
|
switch (lc.cmd)
|
|
{
|
|
case LC_SYMTAB:
|
|
symtab_load_command.cmd = lc.cmd;
|
|
symtab_load_command.cmdsize = lc.cmdsize;
|
|
// Read in the rest of the symtab load command
|
|
if (m_data.GetU32(&offset, &symtab_load_command.symoff, 4) == 0) // fill in symoff, nsyms, stroff, strsize fields
|
|
return 0;
|
|
if (symtab_load_command.symoff == 0)
|
|
{
|
|
if (log)
|
|
module_sp->LogMessage(log, "LC_SYMTAB.symoff == 0");
|
|
return 0;
|
|
}
|
|
|
|
if (symtab_load_command.stroff == 0)
|
|
{
|
|
if (log)
|
|
module_sp->LogMessage(log, "LC_SYMTAB.stroff == 0");
|
|
return 0;
|
|
}
|
|
|
|
if (symtab_load_command.nsyms == 0)
|
|
{
|
|
if (log)
|
|
module_sp->LogMessage(log, "LC_SYMTAB.nsyms == 0");
|
|
return 0;
|
|
}
|
|
|
|
if (symtab_load_command.strsize == 0)
|
|
{
|
|
if (log)
|
|
module_sp->LogMessage(log, "LC_SYMTAB.strsize == 0");
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case LC_DYLD_INFO:
|
|
case LC_DYLD_INFO_ONLY:
|
|
if (m_data.GetU32(&offset, &dyld_info.rebase_off, 10))
|
|
{
|
|
dyld_info.cmd = lc.cmd;
|
|
dyld_info.cmdsize = lc.cmdsize;
|
|
}
|
|
else
|
|
{
|
|
memset (&dyld_info, 0, sizeof(dyld_info));
|
|
}
|
|
break;
|
|
|
|
case LC_LOAD_DYLIB:
|
|
case LC_LOAD_WEAK_DYLIB:
|
|
case LC_REEXPORT_DYLIB:
|
|
case LC_LOADFVMLIB:
|
|
case LC_LOAD_UPWARD_DYLIB:
|
|
{
|
|
uint32_t name_offset = cmd_offset + m_data.GetU32(&offset);
|
|
const char *path = m_data.PeekCStr(name_offset);
|
|
if (path)
|
|
{
|
|
FileSpec file_spec(path, false);
|
|
// Strip the path if there is @rpath, @executable, etc so we just use the basename
|
|
if (path[0] == '@')
|
|
file_spec.GetDirectory().Clear();
|
|
|
|
if (lc.cmd == LC_REEXPORT_DYLIB)
|
|
{
|
|
m_reexported_dylibs.AppendIfUnique(file_spec);
|
|
}
|
|
|
|
dylib_files.Append(file_spec);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case LC_FUNCTION_STARTS:
|
|
function_starts_load_command.cmd = lc.cmd;
|
|
function_starts_load_command.cmdsize = lc.cmdsize;
|
|
if (m_data.GetU32(&offset, &function_starts_load_command.dataoff, 2) == NULL) // fill in symoff, nsyms, stroff, strsize fields
|
|
memset (&function_starts_load_command, 0, sizeof(function_starts_load_command));
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
offset = cmd_offset + lc.cmdsize;
|
|
}
|
|
|
|
if (symtab_load_command.cmd)
|
|
{
|
|
Symtab *symtab = m_symtab_ap.get();
|
|
SectionList *section_list = GetSectionList();
|
|
if (section_list == NULL)
|
|
return 0;
|
|
|
|
const uint32_t addr_byte_size = m_data.GetAddressByteSize();
|
|
const ByteOrder byte_order = m_data.GetByteOrder();
|
|
bool bit_width_32 = addr_byte_size == 4;
|
|
const size_t nlist_byte_size = bit_width_32 ? sizeof(struct nlist) : sizeof(struct nlist_64);
|
|
|
|
DataExtractor nlist_data (NULL, 0, byte_order, addr_byte_size);
|
|
DataExtractor strtab_data (NULL, 0, byte_order, addr_byte_size);
|
|
DataExtractor function_starts_data (NULL, 0, byte_order, addr_byte_size);
|
|
DataExtractor indirect_symbol_index_data (NULL, 0, byte_order, addr_byte_size);
|
|
DataExtractor dyld_trie_data (NULL, 0, byte_order, addr_byte_size);
|
|
|
|
const addr_t nlist_data_byte_size = symtab_load_command.nsyms * nlist_byte_size;
|
|
const addr_t strtab_data_byte_size = symtab_load_command.strsize;
|
|
addr_t strtab_addr = LLDB_INVALID_ADDRESS;
|
|
|
|
ProcessSP process_sp (m_process_wp.lock());
|
|
Process *process = process_sp.get();
|
|
|
|
uint32_t memory_module_load_level = eMemoryModuleLoadLevelComplete;
|
|
|
|
if (process && m_header.filetype != llvm::MachO::MH_OBJECT)
|
|
{
|
|
Target &target = process->GetTarget();
|
|
|
|
memory_module_load_level = target.GetMemoryModuleLoadLevel();
|
|
|
|
SectionSP linkedit_section_sp(section_list->FindSectionByName(GetSegmentNameLINKEDIT()));
|
|
// Reading mach file from memory in a process or core file...
|
|
|
|
if (linkedit_section_sp)
|
|
{
|
|
const addr_t linkedit_load_addr = linkedit_section_sp->GetLoadBaseAddress(&target);
|
|
const addr_t linkedit_file_offset = linkedit_section_sp->GetFileOffset();
|
|
const addr_t symoff_addr = linkedit_load_addr + symtab_load_command.symoff - linkedit_file_offset;
|
|
strtab_addr = linkedit_load_addr + symtab_load_command.stroff - linkedit_file_offset;
|
|
|
|
bool data_was_read = false;
|
|
|
|
#if defined (__APPLE__) && (defined (__arm__) || defined (__arm64__) || defined (__aarch64__))
|
|
if (m_header.flags & 0x80000000u && process->GetAddressByteSize() == sizeof (void*))
|
|
{
|
|
// This mach-o memory file is in the dyld shared cache. If this
|
|
// program is not remote and this is iOS, then this process will
|
|
// share the same shared cache as the process we are debugging and
|
|
// we can read the entire __LINKEDIT from the address space in this
|
|
// process. This is a needed optimization that is used for local iOS
|
|
// debugging only since all shared libraries in the shared cache do
|
|
// not have corresponding files that exist in the file system of the
|
|
// device. They have been combined into a single file. This means we
|
|
// always have to load these files from memory. All of the symbol and
|
|
// string tables from all of the __LINKEDIT sections from the shared
|
|
// libraries in the shared cache have been merged into a single large
|
|
// symbol and string table. Reading all of this symbol and string table
|
|
// data across can slow down debug launch times, so we optimize this by
|
|
// reading the memory for the __LINKEDIT section from this process.
|
|
|
|
UUID lldb_shared_cache(GetLLDBSharedCacheUUID());
|
|
UUID process_shared_cache(GetProcessSharedCacheUUID(process));
|
|
bool use_lldb_cache = true;
|
|
if (lldb_shared_cache.IsValid() && process_shared_cache.IsValid() && lldb_shared_cache != process_shared_cache)
|
|
{
|
|
use_lldb_cache = false;
|
|
ModuleSP module_sp (GetModule());
|
|
if (module_sp)
|
|
module_sp->ReportWarning ("shared cache in process does not match lldb's own shared cache, startup will be slow.");
|
|
|
|
}
|
|
|
|
PlatformSP platform_sp (target.GetPlatform());
|
|
if (platform_sp && platform_sp->IsHost() && use_lldb_cache)
|
|
{
|
|
data_was_read = true;
|
|
nlist_data.SetData((void *)symoff_addr, nlist_data_byte_size, eByteOrderLittle);
|
|
strtab_data.SetData((void *)strtab_addr, strtab_data_byte_size, eByteOrderLittle);
|
|
if (function_starts_load_command.cmd)
|
|
{
|
|
const addr_t func_start_addr = linkedit_load_addr + function_starts_load_command.dataoff - linkedit_file_offset;
|
|
function_starts_data.SetData ((void *)func_start_addr, function_starts_load_command.datasize, eByteOrderLittle);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (!data_was_read)
|
|
{
|
|
if (memory_module_load_level == eMemoryModuleLoadLevelComplete)
|
|
{
|
|
DataBufferSP nlist_data_sp (ReadMemory (process_sp, symoff_addr, nlist_data_byte_size));
|
|
if (nlist_data_sp)
|
|
nlist_data.SetData (nlist_data_sp, 0, nlist_data_sp->GetByteSize());
|
|
// Load strings individually from memory when loading from memory since shared cache
|
|
// string tables contain strings for all symbols from all shared cached libraries
|
|
//DataBufferSP strtab_data_sp (ReadMemory (process_sp, strtab_addr, strtab_data_byte_size));
|
|
//if (strtab_data_sp)
|
|
// strtab_data.SetData (strtab_data_sp, 0, strtab_data_sp->GetByteSize());
|
|
if (m_dysymtab.nindirectsyms != 0)
|
|
{
|
|
const addr_t indirect_syms_addr = linkedit_load_addr + m_dysymtab.indirectsymoff - linkedit_file_offset;
|
|
DataBufferSP indirect_syms_data_sp (ReadMemory (process_sp, indirect_syms_addr, m_dysymtab.nindirectsyms * 4));
|
|
if (indirect_syms_data_sp)
|
|
indirect_symbol_index_data.SetData (indirect_syms_data_sp, 0, indirect_syms_data_sp->GetByteSize());
|
|
}
|
|
}
|
|
|
|
if (memory_module_load_level >= eMemoryModuleLoadLevelPartial)
|
|
{
|
|
if (function_starts_load_command.cmd)
|
|
{
|
|
const addr_t func_start_addr = linkedit_load_addr + function_starts_load_command.dataoff - linkedit_file_offset;
|
|
DataBufferSP func_start_data_sp (ReadMemory (process_sp, func_start_addr, function_starts_load_command.datasize));
|
|
if (func_start_data_sp)
|
|
function_starts_data.SetData (func_start_data_sp, 0, func_start_data_sp->GetByteSize());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nlist_data.SetData (m_data,
|
|
symtab_load_command.symoff,
|
|
nlist_data_byte_size);
|
|
strtab_data.SetData (m_data,
|
|
symtab_load_command.stroff,
|
|
strtab_data_byte_size);
|
|
|
|
if (dyld_info.export_size > 0)
|
|
{
|
|
dyld_trie_data.SetData (m_data,
|
|
dyld_info.export_off,
|
|
dyld_info.export_size);
|
|
}
|
|
|
|
if (m_dysymtab.nindirectsyms != 0)
|
|
{
|
|
indirect_symbol_index_data.SetData (m_data,
|
|
m_dysymtab.indirectsymoff,
|
|
m_dysymtab.nindirectsyms * 4);
|
|
}
|
|
if (function_starts_load_command.cmd)
|
|
{
|
|
function_starts_data.SetData (m_data,
|
|
function_starts_load_command.dataoff,
|
|
function_starts_load_command.datasize);
|
|
}
|
|
}
|
|
|
|
if (nlist_data.GetByteSize() == 0 && memory_module_load_level == eMemoryModuleLoadLevelComplete)
|
|
{
|
|
if (log)
|
|
module_sp->LogMessage(log, "failed to read nlist data");
|
|
return 0;
|
|
}
|
|
|
|
|
|
const bool have_strtab_data = strtab_data.GetByteSize() > 0;
|
|
if (!have_strtab_data)
|
|
{
|
|
if (process)
|
|
{
|
|
if (strtab_addr == LLDB_INVALID_ADDRESS)
|
|
{
|
|
if (log)
|
|
module_sp->LogMessage(log, "failed to locate the strtab in memory");
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (log)
|
|
module_sp->LogMessage(log, "failed to read strtab data");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
const ConstString &g_segment_name_TEXT = GetSegmentNameTEXT();
|
|
const ConstString &g_segment_name_DATA = GetSegmentNameDATA();
|
|
const ConstString &g_segment_name_OBJC = GetSegmentNameOBJC();
|
|
const ConstString &g_section_name_eh_frame = GetSectionNameEHFrame();
|
|
SectionSP text_section_sp(section_list->FindSectionByName(g_segment_name_TEXT));
|
|
SectionSP data_section_sp(section_list->FindSectionByName(g_segment_name_DATA));
|
|
SectionSP objc_section_sp(section_list->FindSectionByName(g_segment_name_OBJC));
|
|
SectionSP eh_frame_section_sp;
|
|
if (text_section_sp.get())
|
|
eh_frame_section_sp = text_section_sp->GetChildren().FindSectionByName (g_section_name_eh_frame);
|
|
else
|
|
eh_frame_section_sp = section_list->FindSectionByName (g_section_name_eh_frame);
|
|
|
|
const bool is_arm = (m_header.cputype == llvm::MachO::CPU_TYPE_ARM);
|
|
|
|
// lldb works best if it knows the start address of all functions in a module.
|
|
// Linker symbols or debug info are normally the best source of information for start addr / size but
|
|
// they may be stripped in a released binary.
|
|
// Two additional sources of information exist in Mach-O binaries:
|
|
// LC_FUNCTION_STARTS - a list of ULEB128 encoded offsets of each function's start address in the
|
|
// binary, relative to the text section.
|
|
// eh_frame - the eh_frame FDEs have the start addr & size of each function
|
|
// LC_FUNCTION_STARTS is the fastest source to read in, and is present on all modern binaries.
|
|
// Binaries built to run on older releases may need to use eh_frame information.
|
|
|
|
if (text_section_sp && function_starts_data.GetByteSize())
|
|
{
|
|
FunctionStarts::Entry function_start_entry;
|
|
function_start_entry.data = false;
|
|
lldb::offset_t function_start_offset = 0;
|
|
function_start_entry.addr = text_section_sp->GetFileAddress();
|
|
uint64_t delta;
|
|
while ((delta = function_starts_data.GetULEB128(&function_start_offset)) > 0)
|
|
{
|
|
// Now append the current entry
|
|
function_start_entry.addr += delta;
|
|
function_starts.Append(function_start_entry);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If m_type is eTypeDebugInfo, then this is a dSYM - it will have the load command claiming an eh_frame
|
|
// but it doesn't actually have the eh_frame content. And if we have a dSYM, we don't need to do any
|
|
// of this fill-in-the-missing-symbols works anyway - the debug info should give us all the functions in
|
|
// the module.
|
|
if (text_section_sp.get() && eh_frame_section_sp.get() && m_type != eTypeDebugInfo)
|
|
{
|
|
DWARFCallFrameInfo eh_frame(*this, eh_frame_section_sp, eRegisterKindGCC, true);
|
|
DWARFCallFrameInfo::FunctionAddressAndSizeVector functions;
|
|
eh_frame.GetFunctionAddressAndSizeVector (functions);
|
|
addr_t text_base_addr = text_section_sp->GetFileAddress();
|
|
size_t count = functions.GetSize();
|
|
for (size_t i = 0; i < count; ++i)
|
|
{
|
|
const DWARFCallFrameInfo::FunctionAddressAndSizeVector::Entry *func = functions.GetEntryAtIndex (i);
|
|
if (func)
|
|
{
|
|
FunctionStarts::Entry function_start_entry;
|
|
function_start_entry.addr = func->base - text_base_addr;
|
|
function_starts.Append(function_start_entry);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const size_t function_starts_count = function_starts.GetSize();
|
|
|
|
const user_id_t TEXT_eh_frame_sectID =
|
|
eh_frame_section_sp.get() ? eh_frame_section_sp->GetID()
|
|
: static_cast<user_id_t>(NO_SECT);
|
|
|
|
lldb::offset_t nlist_data_offset = 0;
|
|
|
|
uint32_t N_SO_index = UINT32_MAX;
|
|
|
|
MachSymtabSectionInfo section_info (section_list);
|
|
std::vector<uint32_t> N_FUN_indexes;
|
|
std::vector<uint32_t> N_NSYM_indexes;
|
|
std::vector<uint32_t> N_INCL_indexes;
|
|
std::vector<uint32_t> N_BRAC_indexes;
|
|
std::vector<uint32_t> N_COMM_indexes;
|
|
typedef std::multimap <uint64_t, uint32_t> ValueToSymbolIndexMap;
|
|
typedef std::map <uint32_t, uint32_t> NListIndexToSymbolIndexMap;
|
|
typedef std::map <const char *, uint32_t> ConstNameToSymbolIndexMap;
|
|
ValueToSymbolIndexMap N_FUN_addr_to_sym_idx;
|
|
ValueToSymbolIndexMap N_STSYM_addr_to_sym_idx;
|
|
ConstNameToSymbolIndexMap N_GSYM_name_to_sym_idx;
|
|
// Any symbols that get merged into another will get an entry
|
|
// in this map so we know
|
|
NListIndexToSymbolIndexMap m_nlist_idx_to_sym_idx;
|
|
uint32_t nlist_idx = 0;
|
|
Symbol *symbol_ptr = NULL;
|
|
|
|
uint32_t sym_idx = 0;
|
|
Symbol *sym = NULL;
|
|
size_t num_syms = 0;
|
|
std::string memory_symbol_name;
|
|
uint32_t unmapped_local_symbols_found = 0;
|
|
|
|
std::vector<TrieEntryWithOffset> trie_entries;
|
|
std::set<lldb::addr_t> resolver_addresses;
|
|
|
|
if (dyld_trie_data.GetByteSize() > 0)
|
|
{
|
|
std::vector<llvm::StringRef> nameSlices;
|
|
ParseTrieEntries (dyld_trie_data,
|
|
0,
|
|
nameSlices,
|
|
resolver_addresses,
|
|
trie_entries);
|
|
|
|
ConstString text_segment_name ("__TEXT");
|
|
SectionSP text_segment_sp = GetSectionList()->FindSectionByName(text_segment_name);
|
|
if (text_segment_sp)
|
|
{
|
|
const lldb::addr_t text_segment_file_addr = text_segment_sp->GetFileAddress();
|
|
if (text_segment_file_addr != LLDB_INVALID_ADDRESS)
|
|
{
|
|
for (auto &e : trie_entries)
|
|
e.entry.address += text_segment_file_addr;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined (__APPLE__) && (defined (__arm__) || defined (__arm64__) || defined (__aarch64__))
|
|
|
|
// Some recent builds of the dyld_shared_cache (hereafter: DSC) have been optimized by moving LOCAL
|
|
// symbols out of the memory mapped portion of the DSC. The symbol information has all been retained,
|
|
// but it isn't available in the normal nlist data. However, there *are* duplicate entries of *some*
|
|
// LOCAL symbols in the normal nlist data. To handle this situation correctly, we must first attempt
|
|
// to parse any DSC unmapped symbol information. If we find any, we set a flag that tells the normal
|
|
// nlist parser to ignore all LOCAL symbols.
|
|
|
|
if (m_header.flags & 0x80000000u)
|
|
{
|
|
// Before we can start mapping the DSC, we need to make certain the target process is actually
|
|
// using the cache we can find.
|
|
|
|
// Next we need to determine the correct path for the dyld shared cache.
|
|
|
|
ArchSpec header_arch;
|
|
GetArchitecture(header_arch);
|
|
char dsc_path[PATH_MAX];
|
|
|
|
snprintf(dsc_path, sizeof(dsc_path), "%s%s%s",
|
|
"/System/Library/Caches/com.apple.dyld/", /* IPHONE_DYLD_SHARED_CACHE_DIR */
|
|
"dyld_shared_cache_", /* DYLD_SHARED_CACHE_BASE_NAME */
|
|
header_arch.GetArchitectureName());
|
|
|
|
FileSpec dsc_filespec(dsc_path, false);
|
|
|
|
// We need definitions of two structures in the on-disk DSC, copy them here manually
|
|
struct lldb_copy_dyld_cache_header_v0
|
|
{
|
|
char magic[16]; // e.g. "dyld_v0 i386", "dyld_v1 armv7", etc.
|
|
uint32_t mappingOffset; // file offset to first dyld_cache_mapping_info
|
|
uint32_t mappingCount; // number of dyld_cache_mapping_info entries
|
|
uint32_t imagesOffset;
|
|
uint32_t imagesCount;
|
|
uint64_t dyldBaseAddress;
|
|
uint64_t codeSignatureOffset;
|
|
uint64_t codeSignatureSize;
|
|
uint64_t slideInfoOffset;
|
|
uint64_t slideInfoSize;
|
|
uint64_t localSymbolsOffset; // file offset of where local symbols are stored
|
|
uint64_t localSymbolsSize; // size of local symbols information
|
|
};
|
|
struct lldb_copy_dyld_cache_header_v1
|
|
{
|
|
char magic[16]; // e.g. "dyld_v0 i386", "dyld_v1 armv7", etc.
|
|
uint32_t mappingOffset; // file offset to first dyld_cache_mapping_info
|
|
uint32_t mappingCount; // number of dyld_cache_mapping_info entries
|
|
uint32_t imagesOffset;
|
|
uint32_t imagesCount;
|
|
uint64_t dyldBaseAddress;
|
|
uint64_t codeSignatureOffset;
|
|
uint64_t codeSignatureSize;
|
|
uint64_t slideInfoOffset;
|
|
uint64_t slideInfoSize;
|
|
uint64_t localSymbolsOffset;
|
|
uint64_t localSymbolsSize;
|
|
uint8_t uuid[16]; // v1 and above, also recorded in dyld_all_image_infos v13 and later
|
|
};
|
|
|
|
struct lldb_copy_dyld_cache_mapping_info
|
|
{
|
|
uint64_t address;
|
|
uint64_t size;
|
|
uint64_t fileOffset;
|
|
uint32_t maxProt;
|
|
uint32_t initProt;
|
|
};
|
|
|
|
struct lldb_copy_dyld_cache_local_symbols_info
|
|
{
|
|
uint32_t nlistOffset;
|
|
uint32_t nlistCount;
|
|
uint32_t stringsOffset;
|
|
uint32_t stringsSize;
|
|
uint32_t entriesOffset;
|
|
uint32_t entriesCount;
|
|
};
|
|
struct lldb_copy_dyld_cache_local_symbols_entry
|
|
{
|
|
uint32_t dylibOffset;
|
|
uint32_t nlistStartIndex;
|
|
uint32_t nlistCount;
|
|
};
|
|
|
|
/* The dyld_cache_header has a pointer to the dyld_cache_local_symbols_info structure (localSymbolsOffset).
|
|
The dyld_cache_local_symbols_info structure gives us three things:
|
|
1. The start and count of the nlist records in the dyld_shared_cache file
|
|
2. The start and size of the strings for these nlist records
|
|
3. The start and count of dyld_cache_local_symbols_entry entries
|
|
|
|
There is one dyld_cache_local_symbols_entry per dylib/framework in the dyld shared cache.
|
|
The "dylibOffset" field is the Mach-O header of this dylib/framework in the dyld shared cache.
|
|
The dyld_cache_local_symbols_entry also lists the start of this dylib/framework's nlist records
|
|
and the count of how many nlist records there are for this dylib/framework.
|
|
*/
|
|
|
|
// Process the dsc header to find the unmapped symbols
|
|
//
|
|
// Save some VM space, do not map the entire cache in one shot.
|
|
|
|
DataBufferSP dsc_data_sp;
|
|
dsc_data_sp = dsc_filespec.MemoryMapFileContents(0, sizeof(struct lldb_copy_dyld_cache_header_v1));
|
|
|
|
if (dsc_data_sp)
|
|
{
|
|
DataExtractor dsc_header_data(dsc_data_sp, byte_order, addr_byte_size);
|
|
|
|
char version_str[17];
|
|
int version = -1;
|
|
lldb::offset_t offset = 0;
|
|
memcpy (version_str, dsc_header_data.GetData (&offset, 16), 16);
|
|
version_str[16] = '\0';
|
|
if (strncmp (version_str, "dyld_v", 6) == 0 && isdigit (version_str[6]))
|
|
{
|
|
int v;
|
|
if (::sscanf (version_str + 6, "%d", &v) == 1)
|
|
{
|
|
version = v;
|
|
}
|
|
}
|
|
|
|
UUID dsc_uuid;
|
|
if (version >= 1)
|
|
{
|
|
offset = offsetof (struct lldb_copy_dyld_cache_header_v1, uuid);
|
|
uint8_t uuid_bytes[sizeof (uuid_t)];
|
|
memcpy (uuid_bytes, dsc_header_data.GetData (&offset, sizeof (uuid_t)), sizeof (uuid_t));
|
|
dsc_uuid.SetBytes (uuid_bytes);
|
|
}
|
|
|
|
bool uuid_match = true;
|
|
if (dsc_uuid.IsValid() && process)
|
|
{
|
|
UUID shared_cache_uuid(GetProcessSharedCacheUUID(process));
|
|
|
|
if (shared_cache_uuid.IsValid() && dsc_uuid != shared_cache_uuid)
|
|
{
|
|
// The on-disk dyld_shared_cache file is not the same as the one in this
|
|
// process' memory, don't use it.
|
|
uuid_match = false;
|
|
ModuleSP module_sp (GetModule());
|
|
if (module_sp)
|
|
module_sp->ReportWarning ("process shared cache does not match on-disk dyld_shared_cache file, some symbol names will be missing.");
|
|
}
|
|
}
|
|
|
|
offset = offsetof (struct lldb_copy_dyld_cache_header_v1, mappingOffset);
|
|
|
|
uint32_t mappingOffset = dsc_header_data.GetU32(&offset);
|
|
|
|
// If the mappingOffset points to a location inside the header, we've
|
|
// opened an old dyld shared cache, and should not proceed further.
|
|
if (uuid_match && mappingOffset >= sizeof(struct lldb_copy_dyld_cache_header_v0))
|
|
{
|
|
|
|
DataBufferSP dsc_mapping_info_data_sp = dsc_filespec.MemoryMapFileContents(mappingOffset, sizeof (struct lldb_copy_dyld_cache_mapping_info));
|
|
DataExtractor dsc_mapping_info_data(dsc_mapping_info_data_sp, byte_order, addr_byte_size);
|
|
offset = 0;
|
|
|
|
// The File addresses (from the in-memory Mach-O load commands) for the shared libraries
|
|
// in the shared library cache need to be adjusted by an offset to match up with the
|
|
// dylibOffset identifying field in the dyld_cache_local_symbol_entry's. This offset is
|
|
// recorded in mapping_offset_value.
|
|
const uint64_t mapping_offset_value = dsc_mapping_info_data.GetU64(&offset);
|
|
|
|
offset = offsetof (struct lldb_copy_dyld_cache_header_v1, localSymbolsOffset);
|
|
uint64_t localSymbolsOffset = dsc_header_data.GetU64(&offset);
|
|
uint64_t localSymbolsSize = dsc_header_data.GetU64(&offset);
|
|
|
|
if (localSymbolsOffset && localSymbolsSize)
|
|
{
|
|
// Map the local symbols
|
|
if (DataBufferSP dsc_local_symbols_data_sp = dsc_filespec.MemoryMapFileContents(localSymbolsOffset, localSymbolsSize))
|
|
{
|
|
DataExtractor dsc_local_symbols_data(dsc_local_symbols_data_sp, byte_order, addr_byte_size);
|
|
|
|
offset = 0;
|
|
|
|
// Read the local_symbols_infos struct in one shot
|
|
struct lldb_copy_dyld_cache_local_symbols_info local_symbols_info;
|
|
dsc_local_symbols_data.GetU32(&offset, &local_symbols_info.nlistOffset, 6);
|
|
|
|
SectionSP text_section_sp(section_list->FindSectionByName(GetSegmentNameTEXT()));
|
|
|
|
uint32_t header_file_offset = (text_section_sp->GetFileAddress() - mapping_offset_value);
|
|
|
|
offset = local_symbols_info.entriesOffset;
|
|
for (uint32_t entry_index = 0; entry_index < local_symbols_info.entriesCount; entry_index++)
|
|
{
|
|
struct lldb_copy_dyld_cache_local_symbols_entry local_symbols_entry;
|
|
local_symbols_entry.dylibOffset = dsc_local_symbols_data.GetU32(&offset);
|
|
local_symbols_entry.nlistStartIndex = dsc_local_symbols_data.GetU32(&offset);
|
|
local_symbols_entry.nlistCount = dsc_local_symbols_data.GetU32(&offset);
|
|
|
|
if (header_file_offset == local_symbols_entry.dylibOffset)
|
|
{
|
|
unmapped_local_symbols_found = local_symbols_entry.nlistCount;
|
|
|
|
// The normal nlist code cannot correctly size the Symbols array, we need to allocate it here.
|
|
sym = symtab->Resize (symtab_load_command.nsyms + m_dysymtab.nindirectsyms + unmapped_local_symbols_found - m_dysymtab.nlocalsym);
|
|
num_syms = symtab->GetNumSymbols();
|
|
|
|
nlist_data_offset = local_symbols_info.nlistOffset + (nlist_byte_size * local_symbols_entry.nlistStartIndex);
|
|
uint32_t string_table_offset = local_symbols_info.stringsOffset;
|
|
|
|
for (uint32_t nlist_index = 0; nlist_index < local_symbols_entry.nlistCount; nlist_index++)
|
|
{
|
|
/////////////////////////////
|
|
{
|
|
struct nlist_64 nlist;
|
|
if (!dsc_local_symbols_data.ValidOffsetForDataOfSize(nlist_data_offset, nlist_byte_size))
|
|
break;
|
|
|
|
nlist.n_strx = dsc_local_symbols_data.GetU32_unchecked(&nlist_data_offset);
|
|
nlist.n_type = dsc_local_symbols_data.GetU8_unchecked (&nlist_data_offset);
|
|
nlist.n_sect = dsc_local_symbols_data.GetU8_unchecked (&nlist_data_offset);
|
|
nlist.n_desc = dsc_local_symbols_data.GetU16_unchecked (&nlist_data_offset);
|
|
nlist.n_value = dsc_local_symbols_data.GetAddress_unchecked (&nlist_data_offset);
|
|
|
|
SymbolType type = eSymbolTypeInvalid;
|
|
const char *symbol_name = dsc_local_symbols_data.PeekCStr(string_table_offset + nlist.n_strx);
|
|
|
|
if (symbol_name == NULL)
|
|
{
|
|
// No symbol should be NULL, even the symbols with no
|
|
// string values should have an offset zero which points
|
|
// to an empty C-string
|
|
Host::SystemLog (Host::eSystemLogError,
|
|
"error: DSC unmapped local symbol[%u] has invalid string table offset 0x%x in %s, ignoring symbol\n",
|
|
entry_index,
|
|
nlist.n_strx,
|
|
module_sp->GetFileSpec().GetPath().c_str());
|
|
continue;
|
|
}
|
|
if (symbol_name[0] == '\0')
|
|
symbol_name = NULL;
|
|
|
|
const char *symbol_name_non_abi_mangled = NULL;
|
|
|
|
SectionSP symbol_section;
|
|
uint32_t symbol_byte_size = 0;
|
|
bool add_nlist = true;
|
|
bool is_debug = ((nlist.n_type & N_STAB) != 0);
|
|
bool demangled_is_synthesized = false;
|
|
bool is_gsym = false;
|
|
|
|
assert (sym_idx < num_syms);
|
|
|
|
sym[sym_idx].SetDebug (is_debug);
|
|
|
|
if (is_debug)
|
|
{
|
|
switch (nlist.n_type)
|
|
{
|
|
case N_GSYM:
|
|
// global symbol: name,,NO_SECT,type,0
|
|
// Sometimes the N_GSYM value contains the address.
|
|
|
|
// FIXME: In the .o files, we have a GSYM and a debug symbol for all the ObjC data. They
|
|
// have the same address, but we want to ensure that we always find only the real symbol,
|
|
// 'cause we don't currently correctly attribute the GSYM one to the ObjCClass/Ivar/MetaClass
|
|
// symbol type. This is a temporary hack to make sure the ObjectiveC symbols get treated
|
|
// correctly. To do this right, we should coalesce all the GSYM & global symbols that have the
|
|
// same address.
|
|
|
|
if (symbol_name && symbol_name[0] == '_' && symbol_name[1] == 'O'
|
|
&& (strncmp (symbol_name, "_OBJC_IVAR_$_", strlen ("_OBJC_IVAR_$_")) == 0
|
|
|| strncmp (symbol_name, "_OBJC_CLASS_$_", strlen ("_OBJC_CLASS_$_")) == 0
|
|
|| strncmp (symbol_name, "_OBJC_METACLASS_$_", strlen ("_OBJC_METACLASS_$_")) == 0))
|
|
add_nlist = false;
|
|
else
|
|
{
|
|
is_gsym = true;
|
|
sym[sym_idx].SetExternal(true);
|
|
if (nlist.n_value != 0)
|
|
symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value);
|
|
type = eSymbolTypeData;
|
|
}
|
|
break;
|
|
|
|
case N_FNAME:
|
|
// procedure name (f77 kludge): name,,NO_SECT,0,0
|
|
type = eSymbolTypeCompiler;
|
|
break;
|
|
|
|
case N_FUN:
|
|
// procedure: name,,n_sect,linenumber,address
|
|
if (symbol_name)
|
|
{
|
|
type = eSymbolTypeCode;
|
|
symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value);
|
|
|
|
N_FUN_addr_to_sym_idx.insert(std::make_pair(nlist.n_value, sym_idx));
|
|
// We use the current number of symbols in the symbol table in lieu of
|
|
// using nlist_idx in case we ever start trimming entries out
|
|
N_FUN_indexes.push_back(sym_idx);
|
|
}
|
|
else
|
|
{
|
|
type = eSymbolTypeCompiler;
|
|
|
|
if ( !N_FUN_indexes.empty() )
|
|
{
|
|
// Copy the size of the function into the original STAB entry so we don't have
|
|
// to hunt for it later
|
|
symtab->SymbolAtIndex(N_FUN_indexes.back())->SetByteSize(nlist.n_value);
|
|
N_FUN_indexes.pop_back();
|
|
// We don't really need the end function STAB as it contains the size which
|
|
// we already placed with the original symbol, so don't add it if we want a
|
|
// minimal symbol table
|
|
add_nlist = false;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case N_STSYM:
|
|
// static symbol: name,,n_sect,type,address
|
|
N_STSYM_addr_to_sym_idx.insert(std::make_pair(nlist.n_value, sym_idx));
|
|
symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value);
|
|
type = eSymbolTypeData;
|
|
break;
|
|
|
|
case N_LCSYM:
|
|
// .lcomm symbol: name,,n_sect,type,address
|
|
symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value);
|
|
type = eSymbolTypeCommonBlock;
|
|
break;
|
|
|
|
case N_BNSYM:
|
|
// We use the current number of symbols in the symbol table in lieu of
|
|
// using nlist_idx in case we ever start trimming entries out
|
|
// Skip these if we want minimal symbol tables
|
|
add_nlist = false;
|
|
break;
|
|
|
|
case N_ENSYM:
|
|
// Set the size of the N_BNSYM to the terminating index of this N_ENSYM
|
|
// so that we can always skip the entire symbol if we need to navigate
|
|
// more quickly at the source level when parsing STABS
|
|
// Skip these if we want minimal symbol tables
|
|
add_nlist = false;
|
|
break;
|
|
|
|
|
|
case N_OPT:
|
|
// emitted with gcc2_compiled and in gcc source
|
|
type = eSymbolTypeCompiler;
|
|
break;
|
|
|
|
case N_RSYM:
|
|
// register sym: name,,NO_SECT,type,register
|
|
type = eSymbolTypeVariable;
|
|
break;
|
|
|
|
case N_SLINE:
|
|
// src line: 0,,n_sect,linenumber,address
|
|
symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value);
|
|
type = eSymbolTypeLineEntry;
|
|
break;
|
|
|
|
case N_SSYM:
|
|
// structure elt: name,,NO_SECT,type,struct_offset
|
|
type = eSymbolTypeVariableType;
|
|
break;
|
|
|
|
case N_SO:
|
|
// source file name
|
|
type = eSymbolTypeSourceFile;
|
|
if (symbol_name == NULL)
|
|
{
|
|
add_nlist = false;
|
|
if (N_SO_index != UINT32_MAX)
|
|
{
|
|
// Set the size of the N_SO to the terminating index of this N_SO
|
|
// so that we can always skip the entire N_SO if we need to navigate
|
|
// more quickly at the source level when parsing STABS
|
|
symbol_ptr = symtab->SymbolAtIndex(N_SO_index);
|
|
symbol_ptr->SetByteSize(sym_idx);
|
|
symbol_ptr->SetSizeIsSibling(true);
|
|
}
|
|
N_NSYM_indexes.clear();
|
|
N_INCL_indexes.clear();
|
|
N_BRAC_indexes.clear();
|
|
N_COMM_indexes.clear();
|
|
N_FUN_indexes.clear();
|
|
N_SO_index = UINT32_MAX;
|
|
}
|
|
else
|
|
{
|
|
// We use the current number of symbols in the symbol table in lieu of
|
|
// using nlist_idx in case we ever start trimming entries out
|
|
const bool N_SO_has_full_path = symbol_name[0] == '/';
|
|
if (N_SO_has_full_path)
|
|
{
|
|
if ((N_SO_index == sym_idx - 1) && ((sym_idx - 1) < num_syms))
|
|
{
|
|
// We have two consecutive N_SO entries where the first contains a directory
|
|
// and the second contains a full path.
|
|
sym[sym_idx - 1].GetMangled().SetValue(ConstString(symbol_name), false);
|
|
m_nlist_idx_to_sym_idx[nlist_idx] = sym_idx - 1;
|
|
add_nlist = false;
|
|
}
|
|
else
|
|
{
|
|
// This is the first entry in a N_SO that contains a directory or
|
|
// a full path to the source file
|
|
N_SO_index = sym_idx;
|
|
}
|
|
}
|
|
else if ((N_SO_index == sym_idx - 1) && ((sym_idx - 1) < num_syms))
|
|
{
|
|
// This is usually the second N_SO entry that contains just the filename,
|
|
// so here we combine it with the first one if we are minimizing the symbol table
|
|
const char *so_path = sym[sym_idx - 1].GetMangled().GetDemangledName().AsCString();
|
|
if (so_path && so_path[0])
|
|
{
|
|
std::string full_so_path (so_path);
|
|
const size_t double_slash_pos = full_so_path.find("//");
|
|
if (double_slash_pos != std::string::npos)
|
|
{
|
|
// The linker has been generating bad N_SO entries with doubled up paths
|
|
// in the format "%s%s" where the first string in the DW_AT_comp_dir,
|
|
// and the second is the directory for the source file so you end up with
|
|
// a path that looks like "/tmp/src//tmp/src/"
|
|
FileSpec so_dir(so_path, false);
|
|
if (!so_dir.Exists())
|
|
{
|
|
so_dir.SetFile(&full_so_path[double_slash_pos + 1], false);
|
|
if (so_dir.Exists())
|
|
{
|
|
// Trim off the incorrect path
|
|
full_so_path.erase(0, double_slash_pos + 1);
|
|
}
|
|
}
|
|
}
|
|
if (*full_so_path.rbegin() != '/')
|
|
full_so_path += '/';
|
|
full_so_path += symbol_name;
|
|
sym[sym_idx - 1].GetMangled().SetValue(ConstString(full_so_path.c_str()), false);
|
|
add_nlist = false;
|
|
m_nlist_idx_to_sym_idx[nlist_idx] = sym_idx - 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This could be a relative path to a N_SO
|
|
N_SO_index = sym_idx;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case N_OSO:
|
|
// object file name: name,,0,0,st_mtime
|
|
type = eSymbolTypeObjectFile;
|
|
break;
|
|
|
|
case N_LSYM:
|
|
// local sym: name,,NO_SECT,type,offset
|
|
type = eSymbolTypeLocal;
|
|
break;
|
|
|
|
//----------------------------------------------------------------------
|
|
// INCL scopes
|
|
//----------------------------------------------------------------------
|
|
case N_BINCL:
|
|
// include file beginning: name,,NO_SECT,0,sum
|
|
// We use the current number of symbols in the symbol table in lieu of
|
|
// using nlist_idx in case we ever start trimming entries out
|
|
N_INCL_indexes.push_back(sym_idx);
|
|
type = eSymbolTypeScopeBegin;
|
|
break;
|
|
|
|
case N_EINCL:
|
|
// include file end: name,,NO_SECT,0,0
|
|
// Set the size of the N_BINCL to the terminating index of this N_EINCL
|
|
// so that we can always skip the entire symbol if we need to navigate
|
|
// more quickly at the source level when parsing STABS
|
|
if ( !N_INCL_indexes.empty() )
|
|
{
|
|
symbol_ptr = symtab->SymbolAtIndex(N_INCL_indexes.back());
|
|
symbol_ptr->SetByteSize(sym_idx + 1);
|
|
symbol_ptr->SetSizeIsSibling(true);
|
|
N_INCL_indexes.pop_back();
|
|
}
|
|
type = eSymbolTypeScopeEnd;
|
|
break;
|
|
|
|
case N_SOL:
|
|
// #included file name: name,,n_sect,0,address
|
|
type = eSymbolTypeHeaderFile;
|
|
|
|
// We currently don't use the header files on darwin
|
|
add_nlist = false;
|
|
break;
|
|
|
|
case N_PARAMS:
|
|
// compiler parameters: name,,NO_SECT,0,0
|
|
type = eSymbolTypeCompiler;
|
|
break;
|
|
|
|
case N_VERSION:
|
|
// compiler version: name,,NO_SECT,0,0
|
|
type = eSymbolTypeCompiler;
|
|
break;
|
|
|
|
case N_OLEVEL:
|
|
// compiler -O level: name,,NO_SECT,0,0
|
|
type = eSymbolTypeCompiler;
|
|
break;
|
|
|
|
case N_PSYM:
|
|
// parameter: name,,NO_SECT,type,offset
|
|
type = eSymbolTypeVariable;
|
|
break;
|
|
|
|
case N_ENTRY:
|
|
// alternate entry: name,,n_sect,linenumber,address
|
|
symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value);
|
|
type = eSymbolTypeLineEntry;
|
|
break;
|
|
|
|
//----------------------------------------------------------------------
|
|
// Left and Right Braces
|
|
//----------------------------------------------------------------------
|
|
case N_LBRAC:
|
|
// left bracket: 0,,NO_SECT,nesting level,address
|
|
// We use the current number of symbols in the symbol table in lieu of
|
|
// using nlist_idx in case we ever start trimming entries out
|
|
symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value);
|
|
N_BRAC_indexes.push_back(sym_idx);
|
|
type = eSymbolTypeScopeBegin;
|
|
break;
|
|
|
|
case N_RBRAC:
|
|
// right bracket: 0,,NO_SECT,nesting level,address
|
|
// Set the size of the N_LBRAC to the terminating index of this N_RBRAC
|
|
// so that we can always skip the entire symbol if we need to navigate
|
|
// more quickly at the source level when parsing STABS
|
|
symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value);
|
|
if ( !N_BRAC_indexes.empty() )
|
|
{
|
|
symbol_ptr = symtab->SymbolAtIndex(N_BRAC_indexes.back());
|
|
symbol_ptr->SetByteSize(sym_idx + 1);
|
|
symbol_ptr->SetSizeIsSibling(true);
|
|
N_BRAC_indexes.pop_back();
|
|
}
|
|
type = eSymbolTypeScopeEnd;
|
|
break;
|
|
|
|
case N_EXCL:
|
|
// deleted include file: name,,NO_SECT,0,sum
|
|
type = eSymbolTypeHeaderFile;
|
|
break;
|
|
|
|
//----------------------------------------------------------------------
|
|
// COMM scopes
|
|
//----------------------------------------------------------------------
|
|
case N_BCOMM:
|
|
// begin common: name,,NO_SECT,0,0
|
|
// We use the current number of symbols in the symbol table in lieu of
|
|
// using nlist_idx in case we ever start trimming entries out
|
|
type = eSymbolTypeScopeBegin;
|
|
N_COMM_indexes.push_back(sym_idx);
|
|
break;
|
|
|
|
case N_ECOML:
|
|
// end common (local name): 0,,n_sect,0,address
|
|
symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value);
|
|
// Fall through
|
|
|
|
case N_ECOMM:
|
|
// end common: name,,n_sect,0,0
|
|
// Set the size of the N_BCOMM to the terminating index of this N_ECOMM/N_ECOML
|
|
// so that we can always skip the entire symbol if we need to navigate
|
|
// more quickly at the source level when parsing STABS
|
|
if ( !N_COMM_indexes.empty() )
|
|
{
|
|
symbol_ptr = symtab->SymbolAtIndex(N_COMM_indexes.back());
|
|
symbol_ptr->SetByteSize(sym_idx + 1);
|
|
symbol_ptr->SetSizeIsSibling(true);
|
|
N_COMM_indexes.pop_back();
|
|
}
|
|
type = eSymbolTypeScopeEnd;
|
|
break;
|
|
|
|
case N_LENG:
|
|
// second stab entry with length information
|
|
type = eSymbolTypeAdditional;
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//uint8_t n_pext = N_PEXT & nlist.n_type;
|
|
uint8_t n_type = N_TYPE & nlist.n_type;
|
|
sym[sym_idx].SetExternal((N_EXT & nlist.n_type) != 0);
|
|
|
|
switch (n_type)
|
|
{
|
|
case N_INDR: // Fall through
|
|
case N_PBUD: // Fall through
|
|
case N_UNDF:
|
|
type = eSymbolTypeUndefined;
|
|
break;
|
|
|
|
case N_ABS:
|
|
type = eSymbolTypeAbsolute;
|
|
break;
|
|
|
|
case N_SECT:
|
|
{
|
|
symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value);
|
|
|
|
if (symbol_section == NULL)
|
|
{
|
|
// TODO: warn about this?
|
|
add_nlist = false;
|
|
break;
|
|
}
|
|
|
|
if (TEXT_eh_frame_sectID == nlist.n_sect)
|
|
{
|
|
type = eSymbolTypeException;
|
|
}
|
|
else
|
|
{
|
|
uint32_t section_type = symbol_section->Get() & SECTION_TYPE;
|
|
|
|
switch (section_type)
|
|
{
|
|
case S_CSTRING_LITERALS: type = eSymbolTypeData; break; // section with only literal C strings
|
|
case S_4BYTE_LITERALS: type = eSymbolTypeData; break; // section with only 4 byte literals
|
|
case S_8BYTE_LITERALS: type = eSymbolTypeData; break; // section with only 8 byte literals
|
|
case S_LITERAL_POINTERS: type = eSymbolTypeTrampoline; break; // section with only pointers to literals
|
|
case S_NON_LAZY_SYMBOL_POINTERS: type = eSymbolTypeTrampoline; break; // section with only non-lazy symbol pointers
|
|
case S_LAZY_SYMBOL_POINTERS: type = eSymbolTypeTrampoline; break; // section with only lazy symbol pointers
|
|
case S_SYMBOL_STUBS: type = eSymbolTypeTrampoline; break; // section with only symbol stubs, byte size of stub in the reserved2 field
|
|
case S_MOD_INIT_FUNC_POINTERS: type = eSymbolTypeCode; break; // section with only function pointers for initialization
|
|
case S_MOD_TERM_FUNC_POINTERS: type = eSymbolTypeCode; break; // section with only function pointers for termination
|
|
case S_INTERPOSING: type = eSymbolTypeTrampoline; break; // section with only pairs of function pointers for interposing
|
|
case S_16BYTE_LITERALS: type = eSymbolTypeData; break; // section with only 16 byte literals
|
|
case S_DTRACE_DOF: type = eSymbolTypeInstrumentation; break;
|
|
case S_LAZY_DYLIB_SYMBOL_POINTERS: type = eSymbolTypeTrampoline; break;
|
|
default:
|
|
switch (symbol_section->GetType())
|
|
{
|
|
case lldb::eSectionTypeCode:
|
|
type = eSymbolTypeCode;
|
|
break;
|
|
case eSectionTypeData:
|
|
case eSectionTypeDataCString: // Inlined C string data
|
|
case eSectionTypeDataCStringPointers: // Pointers to C string data
|
|
case eSectionTypeDataSymbolAddress: // Address of a symbol in the symbol table
|
|
case eSectionTypeData4:
|
|
case eSectionTypeData8:
|
|
case eSectionTypeData16:
|
|
type = eSymbolTypeData;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (type == eSymbolTypeInvalid)
|
|
{
|
|
const char *symbol_sect_name = symbol_section->GetName().AsCString();
|
|
if (symbol_section->IsDescendant (text_section_sp.get()))
|
|
{
|
|
if (symbol_section->IsClear(S_ATTR_PURE_INSTRUCTIONS |
|
|
S_ATTR_SELF_MODIFYING_CODE |
|
|
S_ATTR_SOME_INSTRUCTIONS))
|
|
type = eSymbolTypeData;
|
|
else
|
|
type = eSymbolTypeCode;
|
|
}
|
|
else if (symbol_section->IsDescendant(data_section_sp.get()))
|
|
{
|
|
if (symbol_sect_name && ::strstr (symbol_sect_name, "__objc") == symbol_sect_name)
|
|
{
|
|
type = eSymbolTypeRuntime;
|
|
|
|
if (symbol_name &&
|
|
symbol_name[0] == '_' &&
|
|
symbol_name[1] == 'O' &&
|
|
symbol_name[2] == 'B')
|
|
{
|
|
llvm::StringRef symbol_name_ref(symbol_name);
|
|
static const llvm::StringRef g_objc_v2_prefix_class ("_OBJC_CLASS_$_");
|
|
static const llvm::StringRef g_objc_v2_prefix_metaclass ("_OBJC_METACLASS_$_");
|
|
static const llvm::StringRef g_objc_v2_prefix_ivar ("_OBJC_IVAR_$_");
|
|
if (symbol_name_ref.startswith(g_objc_v2_prefix_class))
|
|
{
|
|
symbol_name_non_abi_mangled = symbol_name + 1;
|
|
symbol_name = symbol_name + g_objc_v2_prefix_class.size();
|
|
type = eSymbolTypeObjCClass;
|
|
demangled_is_synthesized = true;
|
|
}
|
|
else if (symbol_name_ref.startswith(g_objc_v2_prefix_metaclass))
|
|
{
|
|
symbol_name_non_abi_mangled = symbol_name + 1;
|
|
symbol_name = symbol_name + g_objc_v2_prefix_metaclass.size();
|
|
type = eSymbolTypeObjCMetaClass;
|
|
demangled_is_synthesized = true;
|
|
}
|
|
else if (symbol_name_ref.startswith(g_objc_v2_prefix_ivar))
|
|
{
|
|
symbol_name_non_abi_mangled = symbol_name + 1;
|
|
symbol_name = symbol_name + g_objc_v2_prefix_ivar.size();
|
|
type = eSymbolTypeObjCIVar;
|
|
demangled_is_synthesized = true;
|
|
}
|
|
}
|
|
}
|
|
else if (symbol_sect_name && ::strstr (symbol_sect_name, "__gcc_except_tab") == symbol_sect_name)
|
|
{
|
|
type = eSymbolTypeException;
|
|
}
|
|
else
|
|
{
|
|
type = eSymbolTypeData;
|
|
}
|
|
}
|
|
else if (symbol_sect_name && ::strstr (symbol_sect_name, "__IMPORT") == symbol_sect_name)
|
|
{
|
|
type = eSymbolTypeTrampoline;
|
|
}
|
|
else if (symbol_section->IsDescendant(objc_section_sp.get()))
|
|
{
|
|
type = eSymbolTypeRuntime;
|
|
if (symbol_name && symbol_name[0] == '.')
|
|
{
|
|
llvm::StringRef symbol_name_ref(symbol_name);
|
|
static const llvm::StringRef g_objc_v1_prefix_class (".objc_class_name_");
|
|
if (symbol_name_ref.startswith(g_objc_v1_prefix_class))
|
|
{
|
|
symbol_name_non_abi_mangled = symbol_name;
|
|
symbol_name = symbol_name + g_objc_v1_prefix_class.size();
|
|
type = eSymbolTypeObjCClass;
|
|
demangled_is_synthesized = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (add_nlist)
|
|
{
|
|
uint64_t symbol_value = nlist.n_value;
|
|
if (symbol_name_non_abi_mangled)
|
|
{
|
|
sym[sym_idx].GetMangled().SetMangledName (ConstString(symbol_name_non_abi_mangled));
|
|
sym[sym_idx].GetMangled().SetDemangledName (ConstString(symbol_name));
|
|
}
|
|
else
|
|
{
|
|
bool symbol_name_is_mangled = false;
|
|
|
|
if (symbol_name && symbol_name[0] == '_')
|
|
{
|
|
symbol_name_is_mangled = symbol_name[1] == '_';
|
|
symbol_name++; // Skip the leading underscore
|
|
}
|
|
|
|
if (symbol_name)
|
|
{
|
|
ConstString const_symbol_name(symbol_name);
|
|
sym[sym_idx].GetMangled().SetValue(const_symbol_name, symbol_name_is_mangled);
|
|
if (is_gsym && is_debug)
|
|
N_GSYM_name_to_sym_idx[sym[sym_idx].GetMangled().GetName(Mangled::ePreferMangled).GetCString()] = sym_idx;
|
|
}
|
|
}
|
|
if (symbol_section)
|
|
{
|
|
const addr_t section_file_addr = symbol_section->GetFileAddress();
|
|
if (symbol_byte_size == 0 && function_starts_count > 0)
|
|
{
|
|
addr_t symbol_lookup_file_addr = nlist.n_value;
|
|
// Do an exact address match for non-ARM addresses, else get the closest since
|
|
// the symbol might be a thumb symbol which has an address with bit zero set
|
|
FunctionStarts::Entry *func_start_entry = function_starts.FindEntry (symbol_lookup_file_addr, !is_arm);
|
|
if (is_arm && func_start_entry)
|
|
{
|
|
// Verify that the function start address is the symbol address (ARM)
|
|
// or the symbol address + 1 (thumb)
|
|
if (func_start_entry->addr != symbol_lookup_file_addr &&
|
|
func_start_entry->addr != (symbol_lookup_file_addr + 1))
|
|
{
|
|
// Not the right entry, NULL it out...
|
|
func_start_entry = NULL;
|
|
}
|
|
}
|
|
if (func_start_entry)
|
|
{
|
|
func_start_entry->data = true;
|
|
|
|
addr_t symbol_file_addr = func_start_entry->addr;
|
|
uint32_t symbol_flags = 0;
|
|
if (is_arm)
|
|
{
|
|
if (symbol_file_addr & 1)
|
|
symbol_flags = MACHO_NLIST_ARM_SYMBOL_IS_THUMB;
|
|
symbol_file_addr &= 0xfffffffffffffffeull;
|
|
}
|
|
|
|
const FunctionStarts::Entry *next_func_start_entry = function_starts.FindNextEntry (func_start_entry);
|
|
const addr_t section_end_file_addr = section_file_addr + symbol_section->GetByteSize();
|
|
if (next_func_start_entry)
|
|
{
|
|
addr_t next_symbol_file_addr = next_func_start_entry->addr;
|
|
// Be sure the clear the Thumb address bit when we calculate the size
|
|
// from the current and next address
|
|
if (is_arm)
|
|
next_symbol_file_addr &= 0xfffffffffffffffeull;
|
|
symbol_byte_size = std::min<lldb::addr_t>(next_symbol_file_addr - symbol_file_addr, section_end_file_addr - symbol_file_addr);
|
|
}
|
|
else
|
|
{
|
|
symbol_byte_size = section_end_file_addr - symbol_file_addr;
|
|
}
|
|
}
|
|
}
|
|
symbol_value -= section_file_addr;
|
|
}
|
|
|
|
if (is_debug == false)
|
|
{
|
|
if (type == eSymbolTypeCode)
|
|
{
|
|
// See if we can find a N_FUN entry for any code symbols.
|
|
// If we do find a match, and the name matches, then we
|
|
// can merge the two into just the function symbol to avoid
|
|
// duplicate entries in the symbol table
|
|
std::pair<ValueToSymbolIndexMap::const_iterator, ValueToSymbolIndexMap::const_iterator> range;
|
|
range = N_FUN_addr_to_sym_idx.equal_range(nlist.n_value);
|
|
if (range.first != range.second)
|
|
{
|
|
bool found_it = false;
|
|
for (ValueToSymbolIndexMap::const_iterator pos = range.first; pos != range.second; ++pos)
|
|
{
|
|
if (sym[sym_idx].GetMangled().GetName(Mangled::ePreferMangled) == sym[pos->second].GetMangled().GetName(Mangled::ePreferMangled))
|
|
{
|
|
m_nlist_idx_to_sym_idx[nlist_idx] = pos->second;
|
|
// We just need the flags from the linker symbol, so put these flags
|
|
// into the N_FUN flags to avoid duplicate symbols in the symbol table
|
|
sym[pos->second].SetExternal(sym[sym_idx].IsExternal());
|
|
sym[pos->second].SetFlags (nlist.n_type << 16 | nlist.n_desc);
|
|
if (resolver_addresses.find(nlist.n_value) != resolver_addresses.end())
|
|
sym[pos->second].SetType (eSymbolTypeResolver);
|
|
sym[sym_idx].Clear();
|
|
found_it = true;
|
|
break;
|
|
}
|
|
}
|
|
if (found_it)
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if (resolver_addresses.find(nlist.n_value) != resolver_addresses.end())
|
|
type = eSymbolTypeResolver;
|
|
}
|
|
}
|
|
else if (type == eSymbolTypeData)
|
|
{
|
|
// See if we can find a N_STSYM entry for any data symbols.
|
|
// If we do find a match, and the name matches, then we
|
|
// can merge the two into just the Static symbol to avoid
|
|
// duplicate entries in the symbol table
|
|
std::pair<ValueToSymbolIndexMap::const_iterator, ValueToSymbolIndexMap::const_iterator> range;
|
|
range = N_STSYM_addr_to_sym_idx.equal_range(nlist.n_value);
|
|
if (range.first != range.second)
|
|
{
|
|
bool found_it = false;
|
|
for (ValueToSymbolIndexMap::const_iterator pos = range.first; pos != range.second; ++pos)
|
|
{
|
|
if (sym[sym_idx].GetMangled().GetName(Mangled::ePreferMangled) == sym[pos->second].GetMangled().GetName(Mangled::ePreferMangled))
|
|
{
|
|
m_nlist_idx_to_sym_idx[nlist_idx] = pos->second;
|
|
// We just need the flags from the linker symbol, so put these flags
|
|
// into the N_STSYM flags to avoid duplicate symbols in the symbol table
|
|
sym[pos->second].SetExternal(sym[sym_idx].IsExternal());
|
|
sym[pos->second].SetFlags (nlist.n_type << 16 | nlist.n_desc);
|
|
sym[sym_idx].Clear();
|
|
found_it = true;
|
|
break;
|
|
}
|
|
}
|
|
if (found_it)
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
// Combine N_GSYM stab entries with the non stab symbol
|
|
ConstNameToSymbolIndexMap::const_iterator pos = N_GSYM_name_to_sym_idx.find(sym[sym_idx].GetMangled().GetName(Mangled::ePreferMangled).GetCString());
|
|
if (pos != N_GSYM_name_to_sym_idx.end())
|
|
{
|
|
const uint32_t GSYM_sym_idx = pos->second;
|
|
m_nlist_idx_to_sym_idx[nlist_idx] = GSYM_sym_idx;
|
|
// Copy the address, because often the N_GSYM address has an invalid address of zero
|
|
// when the global is a common symbol
|
|
sym[GSYM_sym_idx].GetAddress().SetSection (symbol_section);
|
|
sym[GSYM_sym_idx].GetAddress().SetOffset (symbol_value);
|
|
// We just need the flags from the linker symbol, so put these flags
|
|
// into the N_STSYM flags to avoid duplicate symbols in the symbol table
|
|
sym[GSYM_sym_idx].SetFlags (nlist.n_type << 16 | nlist.n_desc);
|
|
sym[sym_idx].Clear();
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sym[sym_idx].SetID (nlist_idx);
|
|
sym[sym_idx].SetType (type);
|
|
sym[sym_idx].GetAddress().SetSection (symbol_section);
|
|
sym[sym_idx].GetAddress().SetOffset (symbol_value);
|
|
sym[sym_idx].SetFlags (nlist.n_type << 16 | nlist.n_desc);
|
|
|
|
if (symbol_byte_size > 0)
|
|
sym[sym_idx].SetByteSize(symbol_byte_size);
|
|
|
|
if (demangled_is_synthesized)
|
|
sym[sym_idx].SetDemangledNameIsSynthesized(true);
|
|
++sym_idx;
|
|
}
|
|
else
|
|
{
|
|
sym[sym_idx].Clear();
|
|
}
|
|
|
|
}
|
|
/////////////////////////////
|
|
}
|
|
break; // No more entries to consider
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Must reset this in case it was mutated above!
|
|
nlist_data_offset = 0;
|
|
#endif
|
|
|
|
if (nlist_data.GetByteSize() > 0)
|
|
{
|
|
|
|
// If the sym array was not created while parsing the DSC unmapped
|
|
// symbols, create it now.
|
|
if (sym == NULL)
|
|
{
|
|
sym = symtab->Resize (symtab_load_command.nsyms + m_dysymtab.nindirectsyms);
|
|
num_syms = symtab->GetNumSymbols();
|
|
}
|
|
|
|
if (unmapped_local_symbols_found)
|
|
{
|
|
assert(m_dysymtab.ilocalsym == 0);
|
|
nlist_data_offset += (m_dysymtab.nlocalsym * nlist_byte_size);
|
|
nlist_idx = m_dysymtab.nlocalsym;
|
|
}
|
|
else
|
|
{
|
|
nlist_idx = 0;
|
|
}
|
|
|
|
for (; nlist_idx < symtab_load_command.nsyms; ++nlist_idx)
|
|
{
|
|
struct nlist_64 nlist;
|
|
if (!nlist_data.ValidOffsetForDataOfSize(nlist_data_offset, nlist_byte_size))
|
|
break;
|
|
|
|
nlist.n_strx = nlist_data.GetU32_unchecked(&nlist_data_offset);
|
|
nlist.n_type = nlist_data.GetU8_unchecked (&nlist_data_offset);
|
|
nlist.n_sect = nlist_data.GetU8_unchecked (&nlist_data_offset);
|
|
nlist.n_desc = nlist_data.GetU16_unchecked (&nlist_data_offset);
|
|
nlist.n_value = nlist_data.GetAddress_unchecked (&nlist_data_offset);
|
|
|
|
SymbolType type = eSymbolTypeInvalid;
|
|
const char *symbol_name = NULL;
|
|
|
|
if (have_strtab_data)
|
|
{
|
|
symbol_name = strtab_data.PeekCStr(nlist.n_strx);
|
|
|
|
if (symbol_name == NULL)
|
|
{
|
|
// No symbol should be NULL, even the symbols with no
|
|
// string values should have an offset zero which points
|
|
// to an empty C-string
|
|
Host::SystemLog (Host::eSystemLogError,
|
|
"error: symbol[%u] has invalid string table offset 0x%x in %s, ignoring symbol\n",
|
|
nlist_idx,
|
|
nlist.n_strx,
|
|
module_sp->GetFileSpec().GetPath().c_str());
|
|
continue;
|
|
}
|
|
if (symbol_name[0] == '\0')
|
|
symbol_name = NULL;
|
|
}
|
|
else
|
|
{
|
|
const addr_t str_addr = strtab_addr + nlist.n_strx;
|
|
Error str_error;
|
|
if (process->ReadCStringFromMemory(str_addr, memory_symbol_name, str_error))
|
|
symbol_name = memory_symbol_name.c_str();
|
|
}
|
|
const char *symbol_name_non_abi_mangled = NULL;
|
|
|
|
SectionSP symbol_section;
|
|
lldb::addr_t symbol_byte_size = 0;
|
|
bool add_nlist = true;
|
|
bool is_gsym = false;
|
|
bool is_debug = ((nlist.n_type & N_STAB) != 0);
|
|
bool demangled_is_synthesized = false;
|
|
|
|
assert (sym_idx < num_syms);
|
|
|
|
sym[sym_idx].SetDebug (is_debug);
|
|
|
|
if (is_debug)
|
|
{
|
|
switch (nlist.n_type)
|
|
{
|
|
case N_GSYM:
|
|
// global symbol: name,,NO_SECT,type,0
|
|
// Sometimes the N_GSYM value contains the address.
|
|
|
|
// FIXME: In the .o files, we have a GSYM and a debug symbol for all the ObjC data. They
|
|
// have the same address, but we want to ensure that we always find only the real symbol,
|
|
// 'cause we don't currently correctly attribute the GSYM one to the ObjCClass/Ivar/MetaClass
|
|
// symbol type. This is a temporary hack to make sure the ObjectiveC symbols get treated
|
|
// correctly. To do this right, we should coalesce all the GSYM & global symbols that have the
|
|
// same address.
|
|
|
|
if (symbol_name && symbol_name[0] == '_' && symbol_name[1] == 'O'
|
|
&& (strncmp (symbol_name, "_OBJC_IVAR_$_", strlen ("_OBJC_IVAR_$_")) == 0
|
|
|| strncmp (symbol_name, "_OBJC_CLASS_$_", strlen ("_OBJC_CLASS_$_")) == 0
|
|
|| strncmp (symbol_name, "_OBJC_METACLASS_$_", strlen ("_OBJC_METACLASS_$_")) == 0))
|
|
add_nlist = false;
|
|
else
|
|
{
|
|
is_gsym = true;
|
|
sym[sym_idx].SetExternal(true);
|
|
if (nlist.n_value != 0)
|
|
symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value);
|
|
type = eSymbolTypeData;
|
|
}
|
|
break;
|
|
|
|
case N_FNAME:
|
|
// procedure name (f77 kludge): name,,NO_SECT,0,0
|
|
type = eSymbolTypeCompiler;
|
|
break;
|
|
|
|
case N_FUN:
|
|
// procedure: name,,n_sect,linenumber,address
|
|
if (symbol_name)
|
|
{
|
|
type = eSymbolTypeCode;
|
|
symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value);
|
|
|
|
N_FUN_addr_to_sym_idx.insert(std::make_pair(nlist.n_value, sym_idx));
|
|
// We use the current number of symbols in the symbol table in lieu of
|
|
// using nlist_idx in case we ever start trimming entries out
|
|
N_FUN_indexes.push_back(sym_idx);
|
|
}
|
|
else
|
|
{
|
|
type = eSymbolTypeCompiler;
|
|
|
|
if ( !N_FUN_indexes.empty() )
|
|
{
|
|
// Copy the size of the function into the original STAB entry so we don't have
|
|
// to hunt for it later
|
|
symtab->SymbolAtIndex(N_FUN_indexes.back())->SetByteSize(nlist.n_value);
|
|
N_FUN_indexes.pop_back();
|
|
// We don't really need the end function STAB as it contains the size which
|
|
// we already placed with the original symbol, so don't add it if we want a
|
|
// minimal symbol table
|
|
add_nlist = false;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case N_STSYM:
|
|
// static symbol: name,,n_sect,type,address
|
|
N_STSYM_addr_to_sym_idx.insert(std::make_pair(nlist.n_value, sym_idx));
|
|
symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value);
|
|
type = eSymbolTypeData;
|
|
break;
|
|
|
|
case N_LCSYM:
|
|
// .lcomm symbol: name,,n_sect,type,address
|
|
symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value);
|
|
type = eSymbolTypeCommonBlock;
|
|
break;
|
|
|
|
case N_BNSYM:
|
|
// We use the current number of symbols in the symbol table in lieu of
|
|
// using nlist_idx in case we ever start trimming entries out
|
|
// Skip these if we want minimal symbol tables
|
|
add_nlist = false;
|
|
break;
|
|
|
|
case N_ENSYM:
|
|
// Set the size of the N_BNSYM to the terminating index of this N_ENSYM
|
|
// so that we can always skip the entire symbol if we need to navigate
|
|
// more quickly at the source level when parsing STABS
|
|
// Skip these if we want minimal symbol tables
|
|
add_nlist = false;
|
|
break;
|
|
|
|
|
|
case N_OPT:
|
|
// emitted with gcc2_compiled and in gcc source
|
|
type = eSymbolTypeCompiler;
|
|
break;
|
|
|
|
case N_RSYM:
|
|
// register sym: name,,NO_SECT,type,register
|
|
type = eSymbolTypeVariable;
|
|
break;
|
|
|
|
case N_SLINE:
|
|
// src line: 0,,n_sect,linenumber,address
|
|
symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value);
|
|
type = eSymbolTypeLineEntry;
|
|
break;
|
|
|
|
case N_SSYM:
|
|
// structure elt: name,,NO_SECT,type,struct_offset
|
|
type = eSymbolTypeVariableType;
|
|
break;
|
|
|
|
case N_SO:
|
|
// source file name
|
|
type = eSymbolTypeSourceFile;
|
|
if (symbol_name == NULL)
|
|
{
|
|
add_nlist = false;
|
|
if (N_SO_index != UINT32_MAX)
|
|
{
|
|
// Set the size of the N_SO to the terminating index of this N_SO
|
|
// so that we can always skip the entire N_SO if we need to navigate
|
|
// more quickly at the source level when parsing STABS
|
|
symbol_ptr = symtab->SymbolAtIndex(N_SO_index);
|
|
symbol_ptr->SetByteSize(sym_idx);
|
|
symbol_ptr->SetSizeIsSibling(true);
|
|
}
|
|
N_NSYM_indexes.clear();
|
|
N_INCL_indexes.clear();
|
|
N_BRAC_indexes.clear();
|
|
N_COMM_indexes.clear();
|
|
N_FUN_indexes.clear();
|
|
N_SO_index = UINT32_MAX;
|
|
}
|
|
else
|
|
{
|
|
// We use the current number of symbols in the symbol table in lieu of
|
|
// using nlist_idx in case we ever start trimming entries out
|
|
const bool N_SO_has_full_path = symbol_name[0] == '/';
|
|
if (N_SO_has_full_path)
|
|
{
|
|
if ((N_SO_index == sym_idx - 1) && ((sym_idx - 1) < num_syms))
|
|
{
|
|
// We have two consecutive N_SO entries where the first contains a directory
|
|
// and the second contains a full path.
|
|
sym[sym_idx - 1].GetMangled().SetValue(ConstString(symbol_name), false);
|
|
m_nlist_idx_to_sym_idx[nlist_idx] = sym_idx - 1;
|
|
add_nlist = false;
|
|
}
|
|
else
|
|
{
|
|
// This is the first entry in a N_SO that contains a directory or
|
|
// a full path to the source file
|
|
N_SO_index = sym_idx;
|
|
}
|
|
}
|
|
else if ((N_SO_index == sym_idx - 1) && ((sym_idx - 1) < num_syms))
|
|
{
|
|
// This is usually the second N_SO entry that contains just the filename,
|
|
// so here we combine it with the first one if we are minimizing the symbol table
|
|
const char *so_path = sym[sym_idx - 1].GetMangled().GetDemangledName().AsCString();
|
|
if (so_path && so_path[0])
|
|
{
|
|
std::string full_so_path (so_path);
|
|
const size_t double_slash_pos = full_so_path.find("//");
|
|
if (double_slash_pos != std::string::npos)
|
|
{
|
|
// The linker has been generating bad N_SO entries with doubled up paths
|
|
// in the format "%s%s" where the first string in the DW_AT_comp_dir,
|
|
// and the second is the directory for the source file so you end up with
|
|
// a path that looks like "/tmp/src//tmp/src/"
|
|
FileSpec so_dir(so_path, false);
|
|
if (!so_dir.Exists())
|
|
{
|
|
so_dir.SetFile(&full_so_path[double_slash_pos + 1], false);
|
|
if (so_dir.Exists())
|
|
{
|
|
// Trim off the incorrect path
|
|
full_so_path.erase(0, double_slash_pos + 1);
|
|
}
|
|
}
|
|
}
|
|
if (*full_so_path.rbegin() != '/')
|
|
full_so_path += '/';
|
|
full_so_path += symbol_name;
|
|
sym[sym_idx - 1].GetMangled().SetValue(ConstString(full_so_path.c_str()), false);
|
|
add_nlist = false;
|
|
m_nlist_idx_to_sym_idx[nlist_idx] = sym_idx - 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This could be a relative path to a N_SO
|
|
N_SO_index = sym_idx;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case N_OSO:
|
|
// object file name: name,,0,0,st_mtime
|
|
type = eSymbolTypeObjectFile;
|
|
break;
|
|
|
|
case N_LSYM:
|
|
// local sym: name,,NO_SECT,type,offset
|
|
type = eSymbolTypeLocal;
|
|
break;
|
|
|
|
//----------------------------------------------------------------------
|
|
// INCL scopes
|
|
//----------------------------------------------------------------------
|
|
case N_BINCL:
|
|
// include file beginning: name,,NO_SECT,0,sum
|
|
// We use the current number of symbols in the symbol table in lieu of
|
|
// using nlist_idx in case we ever start trimming entries out
|
|
N_INCL_indexes.push_back(sym_idx);
|
|
type = eSymbolTypeScopeBegin;
|
|
break;
|
|
|
|
case N_EINCL:
|
|
// include file end: name,,NO_SECT,0,0
|
|
// Set the size of the N_BINCL to the terminating index of this N_EINCL
|
|
// so that we can always skip the entire symbol if we need to navigate
|
|
// more quickly at the source level when parsing STABS
|
|
if ( !N_INCL_indexes.empty() )
|
|
{
|
|
symbol_ptr = symtab->SymbolAtIndex(N_INCL_indexes.back());
|
|
symbol_ptr->SetByteSize(sym_idx + 1);
|
|
symbol_ptr->SetSizeIsSibling(true);
|
|
N_INCL_indexes.pop_back();
|
|
}
|
|
type = eSymbolTypeScopeEnd;
|
|
break;
|
|
|
|
case N_SOL:
|
|
// #included file name: name,,n_sect,0,address
|
|
type = eSymbolTypeHeaderFile;
|
|
|
|
// We currently don't use the header files on darwin
|
|
add_nlist = false;
|
|
break;
|
|
|
|
case N_PARAMS:
|
|
// compiler parameters: name,,NO_SECT,0,0
|
|
type = eSymbolTypeCompiler;
|
|
break;
|
|
|
|
case N_VERSION:
|
|
// compiler version: name,,NO_SECT,0,0
|
|
type = eSymbolTypeCompiler;
|
|
break;
|
|
|
|
case N_OLEVEL:
|
|
// compiler -O level: name,,NO_SECT,0,0
|
|
type = eSymbolTypeCompiler;
|
|
break;
|
|
|
|
case N_PSYM:
|
|
// parameter: name,,NO_SECT,type,offset
|
|
type = eSymbolTypeVariable;
|
|
break;
|
|
|
|
case N_ENTRY:
|
|
// alternate entry: name,,n_sect,linenumber,address
|
|
symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value);
|
|
type = eSymbolTypeLineEntry;
|
|
break;
|
|
|
|
//----------------------------------------------------------------------
|
|
// Left and Right Braces
|
|
//----------------------------------------------------------------------
|
|
case N_LBRAC:
|
|
// left bracket: 0,,NO_SECT,nesting level,address
|
|
// We use the current number of symbols in the symbol table in lieu of
|
|
// using nlist_idx in case we ever start trimming entries out
|
|
symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value);
|
|
N_BRAC_indexes.push_back(sym_idx);
|
|
type = eSymbolTypeScopeBegin;
|
|
break;
|
|
|
|
case N_RBRAC:
|
|
// right bracket: 0,,NO_SECT,nesting level,address
|
|
// Set the size of the N_LBRAC to the terminating index of this N_RBRAC
|
|
// so that we can always skip the entire symbol if we need to navigate
|
|
// more quickly at the source level when parsing STABS
|
|
symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value);
|
|
if ( !N_BRAC_indexes.empty() )
|
|
{
|
|
symbol_ptr = symtab->SymbolAtIndex(N_BRAC_indexes.back());
|
|
symbol_ptr->SetByteSize(sym_idx + 1);
|
|
symbol_ptr->SetSizeIsSibling(true);
|
|
N_BRAC_indexes.pop_back();
|
|
}
|
|
type = eSymbolTypeScopeEnd;
|
|
break;
|
|
|
|
case N_EXCL:
|
|
// deleted include file: name,,NO_SECT,0,sum
|
|
type = eSymbolTypeHeaderFile;
|
|
break;
|
|
|
|
//----------------------------------------------------------------------
|
|
// COMM scopes
|
|
//----------------------------------------------------------------------
|
|
case N_BCOMM:
|
|
// begin common: name,,NO_SECT,0,0
|
|
// We use the current number of symbols in the symbol table in lieu of
|
|
// using nlist_idx in case we ever start trimming entries out
|
|
type = eSymbolTypeScopeBegin;
|
|
N_COMM_indexes.push_back(sym_idx);
|
|
break;
|
|
|
|
case N_ECOML:
|
|
// end common (local name): 0,,n_sect,0,address
|
|
symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value);
|
|
// Fall through
|
|
|
|
case N_ECOMM:
|
|
// end common: name,,n_sect,0,0
|
|
// Set the size of the N_BCOMM to the terminating index of this N_ECOMM/N_ECOML
|
|
// so that we can always skip the entire symbol if we need to navigate
|
|
// more quickly at the source level when parsing STABS
|
|
if ( !N_COMM_indexes.empty() )
|
|
{
|
|
symbol_ptr = symtab->SymbolAtIndex(N_COMM_indexes.back());
|
|
symbol_ptr->SetByteSize(sym_idx + 1);
|
|
symbol_ptr->SetSizeIsSibling(true);
|
|
N_COMM_indexes.pop_back();
|
|
}
|
|
type = eSymbolTypeScopeEnd;
|
|
break;
|
|
|
|
case N_LENG:
|
|
// second stab entry with length information
|
|
type = eSymbolTypeAdditional;
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//uint8_t n_pext = N_PEXT & nlist.n_type;
|
|
uint8_t n_type = N_TYPE & nlist.n_type;
|
|
sym[sym_idx].SetExternal((N_EXT & nlist.n_type) != 0);
|
|
|
|
switch (n_type)
|
|
{
|
|
case N_INDR:// Fall through
|
|
case N_PBUD:// Fall through
|
|
case N_UNDF:
|
|
type = eSymbolTypeUndefined;
|
|
break;
|
|
|
|
case N_ABS:
|
|
type = eSymbolTypeAbsolute;
|
|
break;
|
|
|
|
case N_SECT:
|
|
{
|
|
symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value);
|
|
|
|
if (!symbol_section)
|
|
{
|
|
// TODO: warn about this?
|
|
add_nlist = false;
|
|
break;
|
|
}
|
|
|
|
if (TEXT_eh_frame_sectID == nlist.n_sect)
|
|
{
|
|
type = eSymbolTypeException;
|
|
}
|
|
else
|
|
{
|
|
uint32_t section_type = symbol_section->Get() & SECTION_TYPE;
|
|
|
|
switch (section_type)
|
|
{
|
|
case S_CSTRING_LITERALS: type = eSymbolTypeData; break; // section with only literal C strings
|
|
case S_4BYTE_LITERALS: type = eSymbolTypeData; break; // section with only 4 byte literals
|
|
case S_8BYTE_LITERALS: type = eSymbolTypeData; break; // section with only 8 byte literals
|
|
case S_LITERAL_POINTERS: type = eSymbolTypeTrampoline; break; // section with only pointers to literals
|
|
case S_NON_LAZY_SYMBOL_POINTERS: type = eSymbolTypeTrampoline; break; // section with only non-lazy symbol pointers
|
|
case S_LAZY_SYMBOL_POINTERS: type = eSymbolTypeTrampoline; break; // section with only lazy symbol pointers
|
|
case S_SYMBOL_STUBS: type = eSymbolTypeTrampoline; break; // section with only symbol stubs, byte size of stub in the reserved2 field
|
|
case S_MOD_INIT_FUNC_POINTERS: type = eSymbolTypeCode; break; // section with only function pointers for initialization
|
|
case S_MOD_TERM_FUNC_POINTERS: type = eSymbolTypeCode; break; // section with only function pointers for termination
|
|
case S_INTERPOSING: type = eSymbolTypeTrampoline; break; // section with only pairs of function pointers for interposing
|
|
case S_16BYTE_LITERALS: type = eSymbolTypeData; break; // section with only 16 byte literals
|
|
case S_DTRACE_DOF: type = eSymbolTypeInstrumentation; break;
|
|
case S_LAZY_DYLIB_SYMBOL_POINTERS: type = eSymbolTypeTrampoline; break;
|
|
default:
|
|
switch (symbol_section->GetType())
|
|
{
|
|
case lldb::eSectionTypeCode:
|
|
type = eSymbolTypeCode;
|
|
break;
|
|
case eSectionTypeData:
|
|
case eSectionTypeDataCString: // Inlined C string data
|
|
case eSectionTypeDataCStringPointers: // Pointers to C string data
|
|
case eSectionTypeDataSymbolAddress: // Address of a symbol in the symbol table
|
|
case eSectionTypeData4:
|
|
case eSectionTypeData8:
|
|
case eSectionTypeData16:
|
|
type = eSymbolTypeData;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (type == eSymbolTypeInvalid)
|
|
{
|
|
const char *symbol_sect_name = symbol_section->GetName().AsCString();
|
|
if (symbol_section->IsDescendant (text_section_sp.get()))
|
|
{
|
|
if (symbol_section->IsClear(S_ATTR_PURE_INSTRUCTIONS |
|
|
S_ATTR_SELF_MODIFYING_CODE |
|
|
S_ATTR_SOME_INSTRUCTIONS))
|
|
type = eSymbolTypeData;
|
|
else
|
|
type = eSymbolTypeCode;
|
|
}
|
|
else
|
|
if (symbol_section->IsDescendant(data_section_sp.get()))
|
|
{
|
|
if (symbol_sect_name && ::strstr (symbol_sect_name, "__objc") == symbol_sect_name)
|
|
{
|
|
type = eSymbolTypeRuntime;
|
|
|
|
if (symbol_name &&
|
|
symbol_name[0] == '_' &&
|
|
symbol_name[1] == 'O' &&
|
|
symbol_name[2] == 'B')
|
|
{
|
|
llvm::StringRef symbol_name_ref(symbol_name);
|
|
static const llvm::StringRef g_objc_v2_prefix_class ("_OBJC_CLASS_$_");
|
|
static const llvm::StringRef g_objc_v2_prefix_metaclass ("_OBJC_METACLASS_$_");
|
|
static const llvm::StringRef g_objc_v2_prefix_ivar ("_OBJC_IVAR_$_");
|
|
if (symbol_name_ref.startswith(g_objc_v2_prefix_class))
|
|
{
|
|
symbol_name_non_abi_mangled = symbol_name + 1;
|
|
symbol_name = symbol_name + g_objc_v2_prefix_class.size();
|
|
type = eSymbolTypeObjCClass;
|
|
demangled_is_synthesized = true;
|
|
}
|
|
else if (symbol_name_ref.startswith(g_objc_v2_prefix_metaclass))
|
|
{
|
|
symbol_name_non_abi_mangled = symbol_name + 1;
|
|
symbol_name = symbol_name + g_objc_v2_prefix_metaclass.size();
|
|
type = eSymbolTypeObjCMetaClass;
|
|
demangled_is_synthesized = true;
|
|
}
|
|
else if (symbol_name_ref.startswith(g_objc_v2_prefix_ivar))
|
|
{
|
|
symbol_name_non_abi_mangled = symbol_name + 1;
|
|
symbol_name = symbol_name + g_objc_v2_prefix_ivar.size();
|
|
type = eSymbolTypeObjCIVar;
|
|
demangled_is_synthesized = true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
if (symbol_sect_name && ::strstr (symbol_sect_name, "__gcc_except_tab") == symbol_sect_name)
|
|
{
|
|
type = eSymbolTypeException;
|
|
}
|
|
else
|
|
{
|
|
type = eSymbolTypeData;
|
|
}
|
|
}
|
|
else
|
|
if (symbol_sect_name && ::strstr (symbol_sect_name, "__IMPORT") == symbol_sect_name)
|
|
{
|
|
type = eSymbolTypeTrampoline;
|
|
}
|
|
else
|
|
if (symbol_section->IsDescendant(objc_section_sp.get()))
|
|
{
|
|
type = eSymbolTypeRuntime;
|
|
if (symbol_name && symbol_name[0] == '.')
|
|
{
|
|
llvm::StringRef symbol_name_ref(symbol_name);
|
|
static const llvm::StringRef g_objc_v1_prefix_class (".objc_class_name_");
|
|
if (symbol_name_ref.startswith(g_objc_v1_prefix_class))
|
|
{
|
|
symbol_name_non_abi_mangled = symbol_name;
|
|
symbol_name = symbol_name + g_objc_v1_prefix_class.size();
|
|
type = eSymbolTypeObjCClass;
|
|
demangled_is_synthesized = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (add_nlist)
|
|
{
|
|
uint64_t symbol_value = nlist.n_value;
|
|
|
|
if (symbol_name_non_abi_mangled)
|
|
{
|
|
sym[sym_idx].GetMangled().SetMangledName (ConstString(symbol_name_non_abi_mangled));
|
|
sym[sym_idx].GetMangled().SetDemangledName (ConstString(symbol_name));
|
|
}
|
|
else
|
|
{
|
|
bool symbol_name_is_mangled = false;
|
|
|
|
if (symbol_name && symbol_name[0] == '_')
|
|
{
|
|
symbol_name_is_mangled = symbol_name[1] == '_';
|
|
symbol_name++; // Skip the leading underscore
|
|
}
|
|
|
|
if (symbol_name)
|
|
{
|
|
ConstString const_symbol_name(symbol_name);
|
|
sym[sym_idx].GetMangled().SetValue(const_symbol_name, symbol_name_is_mangled);
|
|
if (is_gsym && is_debug)
|
|
{
|
|
N_GSYM_name_to_sym_idx[sym[sym_idx].GetMangled().GetName(Mangled::ePreferMangled).GetCString()] = sym_idx;
|
|
}
|
|
}
|
|
}
|
|
if (symbol_section)
|
|
{
|
|
const addr_t section_file_addr = symbol_section->GetFileAddress();
|
|
if (symbol_byte_size == 0 && function_starts_count > 0)
|
|
{
|
|
addr_t symbol_lookup_file_addr = nlist.n_value;
|
|
// Do an exact address match for non-ARM addresses, else get the closest since
|
|
// the symbol might be a thumb symbol which has an address with bit zero set
|
|
FunctionStarts::Entry *func_start_entry = function_starts.FindEntry (symbol_lookup_file_addr, !is_arm);
|
|
if (is_arm && func_start_entry)
|
|
{
|
|
// Verify that the function start address is the symbol address (ARM)
|
|
// or the symbol address + 1 (thumb)
|
|
if (func_start_entry->addr != symbol_lookup_file_addr &&
|
|
func_start_entry->addr != (symbol_lookup_file_addr + 1))
|
|
{
|
|
// Not the right entry, NULL it out...
|
|
func_start_entry = NULL;
|
|
}
|
|
}
|
|
if (func_start_entry)
|
|
{
|
|
func_start_entry->data = true;
|
|
|
|
addr_t symbol_file_addr = func_start_entry->addr;
|
|
if (is_arm)
|
|
symbol_file_addr &= 0xfffffffffffffffeull;
|
|
|
|
const FunctionStarts::Entry *next_func_start_entry = function_starts.FindNextEntry (func_start_entry);
|
|
const addr_t section_end_file_addr = section_file_addr + symbol_section->GetByteSize();
|
|
if (next_func_start_entry)
|
|
{
|
|
addr_t next_symbol_file_addr = next_func_start_entry->addr;
|
|
// Be sure the clear the Thumb address bit when we calculate the size
|
|
// from the current and next address
|
|
if (is_arm)
|
|
next_symbol_file_addr &= 0xfffffffffffffffeull;
|
|
symbol_byte_size = std::min<lldb::addr_t>(next_symbol_file_addr - symbol_file_addr, section_end_file_addr - symbol_file_addr);
|
|
}
|
|
else
|
|
{
|
|
symbol_byte_size = section_end_file_addr - symbol_file_addr;
|
|
}
|
|
}
|
|
}
|
|
symbol_value -= section_file_addr;
|
|
}
|
|
|
|
if (is_debug == false)
|
|
{
|
|
if (type == eSymbolTypeCode)
|
|
{
|
|
// See if we can find a N_FUN entry for any code symbols.
|
|
// If we do find a match, and the name matches, then we
|
|
// can merge the two into just the function symbol to avoid
|
|
// duplicate entries in the symbol table
|
|
std::pair<ValueToSymbolIndexMap::const_iterator, ValueToSymbolIndexMap::const_iterator> range;
|
|
range = N_FUN_addr_to_sym_idx.equal_range(nlist.n_value);
|
|
if (range.first != range.second)
|
|
{
|
|
bool found_it = false;
|
|
for (ValueToSymbolIndexMap::const_iterator pos = range.first; pos != range.second; ++pos)
|
|
{
|
|
if (sym[sym_idx].GetMangled().GetName(Mangled::ePreferMangled) == sym[pos->second].GetMangled().GetName(Mangled::ePreferMangled))
|
|
{
|
|
m_nlist_idx_to_sym_idx[nlist_idx] = pos->second;
|
|
// We just need the flags from the linker symbol, so put these flags
|
|
// into the N_FUN flags to avoid duplicate symbols in the symbol table
|
|
sym[pos->second].SetExternal(sym[sym_idx].IsExternal());
|
|
sym[pos->second].SetFlags (nlist.n_type << 16 | nlist.n_desc);
|
|
if (resolver_addresses.find(nlist.n_value) != resolver_addresses.end())
|
|
sym[pos->second].SetType (eSymbolTypeResolver);
|
|
sym[sym_idx].Clear();
|
|
found_it = true;
|
|
break;
|
|
}
|
|
}
|
|
if (found_it)
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if (resolver_addresses.find(nlist.n_value) != resolver_addresses.end())
|
|
type = eSymbolTypeResolver;
|
|
}
|
|
}
|
|
else if (type == eSymbolTypeData)
|
|
{
|
|
// See if we can find a N_STSYM entry for any data symbols.
|
|
// If we do find a match, and the name matches, then we
|
|
// can merge the two into just the Static symbol to avoid
|
|
// duplicate entries in the symbol table
|
|
std::pair<ValueToSymbolIndexMap::const_iterator, ValueToSymbolIndexMap::const_iterator> range;
|
|
range = N_STSYM_addr_to_sym_idx.equal_range(nlist.n_value);
|
|
if (range.first != range.second)
|
|
{
|
|
bool found_it = false;
|
|
for (ValueToSymbolIndexMap::const_iterator pos = range.first; pos != range.second; ++pos)
|
|
{
|
|
if (sym[sym_idx].GetMangled().GetName(Mangled::ePreferMangled) == sym[pos->second].GetMangled().GetName(Mangled::ePreferMangled))
|
|
{
|
|
m_nlist_idx_to_sym_idx[nlist_idx] = pos->second;
|
|
// We just need the flags from the linker symbol, so put these flags
|
|
// into the N_STSYM flags to avoid duplicate symbols in the symbol table
|
|
sym[pos->second].SetExternal(sym[sym_idx].IsExternal());
|
|
sym[pos->second].SetFlags (nlist.n_type << 16 | nlist.n_desc);
|
|
sym[sym_idx].Clear();
|
|
found_it = true;
|
|
break;
|
|
}
|
|
}
|
|
if (found_it)
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
// Combine N_GSYM stab entries with the non stab symbol
|
|
ConstNameToSymbolIndexMap::const_iterator pos = N_GSYM_name_to_sym_idx.find(sym[sym_idx].GetMangled().GetName(Mangled::ePreferMangled).GetCString());
|
|
if (pos != N_GSYM_name_to_sym_idx.end())
|
|
{
|
|
const uint32_t GSYM_sym_idx = pos->second;
|
|
m_nlist_idx_to_sym_idx[nlist_idx] = GSYM_sym_idx;
|
|
// Copy the address, because often the N_GSYM address has an invalid address of zero
|
|
// when the global is a common symbol
|
|
sym[GSYM_sym_idx].GetAddress().SetSection (symbol_section);
|
|
sym[GSYM_sym_idx].GetAddress().SetOffset (symbol_value);
|
|
// We just need the flags from the linker symbol, so put these flags
|
|
// into the N_STSYM flags to avoid duplicate symbols in the symbol table
|
|
sym[GSYM_sym_idx].SetFlags (nlist.n_type << 16 | nlist.n_desc);
|
|
sym[sym_idx].Clear();
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sym[sym_idx].SetID (nlist_idx);
|
|
sym[sym_idx].SetType (type);
|
|
sym[sym_idx].GetAddress().SetSection (symbol_section);
|
|
sym[sym_idx].GetAddress().SetOffset (symbol_value);
|
|
sym[sym_idx].SetFlags (nlist.n_type << 16 | nlist.n_desc);
|
|
|
|
if (symbol_byte_size > 0)
|
|
sym[sym_idx].SetByteSize(symbol_byte_size);
|
|
|
|
if (demangled_is_synthesized)
|
|
sym[sym_idx].SetDemangledNameIsSynthesized(true);
|
|
|
|
++sym_idx;
|
|
}
|
|
else
|
|
{
|
|
sym[sym_idx].Clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32_t synthetic_sym_id = symtab_load_command.nsyms;
|
|
|
|
if (function_starts_count > 0)
|
|
{
|
|
char synthetic_function_symbol[PATH_MAX];
|
|
uint32_t num_synthetic_function_symbols = 0;
|
|
for (i=0; i<function_starts_count; ++i)
|
|
{
|
|
if (function_starts.GetEntryRef (i).data == false)
|
|
++num_synthetic_function_symbols;
|
|
}
|
|
|
|
if (num_synthetic_function_symbols > 0)
|
|
{
|
|
if (num_syms < sym_idx + num_synthetic_function_symbols)
|
|
{
|
|
num_syms = sym_idx + num_synthetic_function_symbols;
|
|
sym = symtab->Resize (num_syms);
|
|
}
|
|
uint32_t synthetic_function_symbol_idx = 0;
|
|
for (i=0; i<function_starts_count; ++i)
|
|
{
|
|
const FunctionStarts::Entry *func_start_entry = function_starts.GetEntryAtIndex (i);
|
|
if (func_start_entry->data == false)
|
|
{
|
|
addr_t symbol_file_addr = func_start_entry->addr;
|
|
uint32_t symbol_flags = 0;
|
|
if (is_arm)
|
|
{
|
|
if (symbol_file_addr & 1)
|
|
symbol_flags = MACHO_NLIST_ARM_SYMBOL_IS_THUMB;
|
|
symbol_file_addr &= 0xfffffffffffffffeull;
|
|
}
|
|
Address symbol_addr;
|
|
if (module_sp->ResolveFileAddress (symbol_file_addr, symbol_addr))
|
|
{
|
|
SectionSP symbol_section (symbol_addr.GetSection());
|
|
uint32_t symbol_byte_size = 0;
|
|
if (symbol_section)
|
|
{
|
|
const addr_t section_file_addr = symbol_section->GetFileAddress();
|
|
const FunctionStarts::Entry *next_func_start_entry = function_starts.FindNextEntry (func_start_entry);
|
|
const addr_t section_end_file_addr = section_file_addr + symbol_section->GetByteSize();
|
|
if (next_func_start_entry)
|
|
{
|
|
addr_t next_symbol_file_addr = next_func_start_entry->addr;
|
|
if (is_arm)
|
|
next_symbol_file_addr &= 0xfffffffffffffffeull;
|
|
symbol_byte_size = std::min<lldb::addr_t>(next_symbol_file_addr - symbol_file_addr, section_end_file_addr - symbol_file_addr);
|
|
}
|
|
else
|
|
{
|
|
symbol_byte_size = section_end_file_addr - symbol_file_addr;
|
|
}
|
|
snprintf (synthetic_function_symbol,
|
|
sizeof(synthetic_function_symbol),
|
|
"___lldb_unnamed_function%u$$%s",
|
|
++synthetic_function_symbol_idx,
|
|
module_sp->GetFileSpec().GetFilename().GetCString());
|
|
sym[sym_idx].SetID (synthetic_sym_id++);
|
|
sym[sym_idx].GetMangled().SetDemangledName(ConstString(synthetic_function_symbol));
|
|
sym[sym_idx].SetType (eSymbolTypeCode);
|
|
sym[sym_idx].SetIsSynthetic (true);
|
|
sym[sym_idx].GetAddress() = symbol_addr;
|
|
if (symbol_flags)
|
|
sym[sym_idx].SetFlags (symbol_flags);
|
|
if (symbol_byte_size)
|
|
sym[sym_idx].SetByteSize (symbol_byte_size);
|
|
++sym_idx;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Trim our symbols down to just what we ended up with after
|
|
// removing any symbols.
|
|
if (sym_idx < num_syms)
|
|
{
|
|
num_syms = sym_idx;
|
|
sym = symtab->Resize (num_syms);
|
|
}
|
|
|
|
// Now synthesize indirect symbols
|
|
if (m_dysymtab.nindirectsyms != 0)
|
|
{
|
|
if (indirect_symbol_index_data.GetByteSize())
|
|
{
|
|
NListIndexToSymbolIndexMap::const_iterator end_index_pos = m_nlist_idx_to_sym_idx.end();
|
|
|
|
for (uint32_t sect_idx = 1; sect_idx < m_mach_sections.size(); ++sect_idx)
|
|
{
|
|
if ((m_mach_sections[sect_idx].flags & SECTION_TYPE) == S_SYMBOL_STUBS)
|
|
{
|
|
uint32_t symbol_stub_byte_size = m_mach_sections[sect_idx].reserved2;
|
|
if (symbol_stub_byte_size == 0)
|
|
continue;
|
|
|
|
const uint32_t num_symbol_stubs = m_mach_sections[sect_idx].size / symbol_stub_byte_size;
|
|
|
|
if (num_symbol_stubs == 0)
|
|
continue;
|
|
|
|
const uint32_t symbol_stub_index_offset = m_mach_sections[sect_idx].reserved1;
|
|
for (uint32_t stub_idx = 0; stub_idx < num_symbol_stubs; ++stub_idx)
|
|
{
|
|
const uint32_t symbol_stub_index = symbol_stub_index_offset + stub_idx;
|
|
const lldb::addr_t symbol_stub_addr = m_mach_sections[sect_idx].addr + (stub_idx * symbol_stub_byte_size);
|
|
lldb::offset_t symbol_stub_offset = symbol_stub_index * 4;
|
|
if (indirect_symbol_index_data.ValidOffsetForDataOfSize(symbol_stub_offset, 4))
|
|
{
|
|
const uint32_t stub_sym_id = indirect_symbol_index_data.GetU32 (&symbol_stub_offset);
|
|
if (stub_sym_id & (INDIRECT_SYMBOL_ABS | INDIRECT_SYMBOL_LOCAL))
|
|
continue;
|
|
|
|
NListIndexToSymbolIndexMap::const_iterator index_pos = m_nlist_idx_to_sym_idx.find (stub_sym_id);
|
|
Symbol *stub_symbol = NULL;
|
|
if (index_pos != end_index_pos)
|
|
{
|
|
// We have a remapping from the original nlist index to
|
|
// a current symbol index, so just look this up by index
|
|
stub_symbol = symtab->SymbolAtIndex (index_pos->second);
|
|
}
|
|
else
|
|
{
|
|
// We need to lookup a symbol using the original nlist
|
|
// symbol index since this index is coming from the
|
|
// S_SYMBOL_STUBS
|
|
stub_symbol = symtab->FindSymbolByID (stub_sym_id);
|
|
}
|
|
|
|
if (stub_symbol)
|
|
{
|
|
Address so_addr(symbol_stub_addr, section_list);
|
|
|
|
if (stub_symbol->GetType() == eSymbolTypeUndefined)
|
|
{
|
|
// Change the external symbol into a trampoline that makes sense
|
|
// These symbols were N_UNDF N_EXT, and are useless to us, so we
|
|
// can re-use them so we don't have to make up a synthetic symbol
|
|
// for no good reason.
|
|
if (resolver_addresses.find(symbol_stub_addr) == resolver_addresses.end())
|
|
stub_symbol->SetType (eSymbolTypeTrampoline);
|
|
else
|
|
stub_symbol->SetType (eSymbolTypeResolver);
|
|
stub_symbol->SetExternal (false);
|
|
stub_symbol->GetAddress() = so_addr;
|
|
stub_symbol->SetByteSize (symbol_stub_byte_size);
|
|
}
|
|
else
|
|
{
|
|
// Make a synthetic symbol to describe the trampoline stub
|
|
Mangled stub_symbol_mangled_name(stub_symbol->GetMangled());
|
|
if (sym_idx >= num_syms)
|
|
{
|
|
sym = symtab->Resize (++num_syms);
|
|
stub_symbol = NULL; // this pointer no longer valid
|
|
}
|
|
sym[sym_idx].SetID (synthetic_sym_id++);
|
|
sym[sym_idx].GetMangled() = stub_symbol_mangled_name;
|
|
if (resolver_addresses.find(symbol_stub_addr) == resolver_addresses.end())
|
|
sym[sym_idx].SetType (eSymbolTypeTrampoline);
|
|
else
|
|
sym[sym_idx].SetType (eSymbolTypeResolver);
|
|
sym[sym_idx].SetIsSynthetic (true);
|
|
sym[sym_idx].GetAddress() = so_addr;
|
|
sym[sym_idx].SetByteSize (symbol_stub_byte_size);
|
|
++sym_idx;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (log)
|
|
log->Warning ("symbol stub referencing symbol table symbol %u that isn't in our minimal symbol table, fix this!!!", stub_sym_id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (!trie_entries.empty())
|
|
{
|
|
for (const auto &e : trie_entries)
|
|
{
|
|
if (e.entry.import_name)
|
|
{
|
|
// Make a synthetic symbol to describe re-exported symbol.
|
|
if (sym_idx >= num_syms)
|
|
sym = symtab->Resize (++num_syms);
|
|
sym[sym_idx].SetID (synthetic_sym_id++);
|
|
sym[sym_idx].GetMangled() = Mangled(e.entry.name);
|
|
sym[sym_idx].SetType (eSymbolTypeReExported);
|
|
sym[sym_idx].SetIsSynthetic (true);
|
|
sym[sym_idx].SetReExportedSymbolName(e.entry.import_name);
|
|
if (e.entry.other > 0 && e.entry.other <= dylib_files.GetSize())
|
|
{
|
|
sym[sym_idx].SetReExportedSymbolSharedLibrary(dylib_files.GetFileSpecAtIndex(e.entry.other-1));
|
|
}
|
|
++sym_idx;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// StreamFile s(stdout, false);
|
|
// s.Printf ("Symbol table before CalculateSymbolSizes():\n");
|
|
// symtab->Dump(&s, NULL, eSortOrderNone);
|
|
// Set symbol byte sizes correctly since mach-o nlist entries don't have sizes
|
|
symtab->CalculateSymbolSizes();
|
|
|
|
// s.Printf ("Symbol table after CalculateSymbolSizes():\n");
|
|
// symtab->Dump(&s, NULL, eSortOrderNone);
|
|
|
|
return symtab->GetNumSymbols();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
void
|
|
ObjectFileMachO::Dump (Stream *s)
|
|
{
|
|
ModuleSP module_sp(GetModule());
|
|
if (module_sp)
|
|
{
|
|
lldb_private::Mutex::Locker locker(module_sp->GetMutex());
|
|
s->Printf("%p: ", static_cast<void*>(this));
|
|
s->Indent();
|
|
if (m_header.magic == MH_MAGIC_64 || m_header.magic == MH_CIGAM_64)
|
|
s->PutCString("ObjectFileMachO64");
|
|
else
|
|
s->PutCString("ObjectFileMachO32");
|
|
|
|
ArchSpec header_arch;
|
|
GetArchitecture(header_arch);
|
|
|
|
*s << ", file = '" << m_file << "', arch = " << header_arch.GetArchitectureName() << "\n";
|
|
|
|
SectionList *sections = GetSectionList();
|
|
if (sections)
|
|
sections->Dump(s, NULL, true, UINT32_MAX);
|
|
|
|
if (m_symtab_ap.get())
|
|
m_symtab_ap->Dump(s, NULL, eSortOrderNone);
|
|
}
|
|
}
|
|
|
|
bool
|
|
ObjectFileMachO::GetUUID (const llvm::MachO::mach_header &header,
|
|
const lldb_private::DataExtractor &data,
|
|
lldb::offset_t lc_offset,
|
|
lldb_private::UUID& uuid)
|
|
{
|
|
uint32_t i;
|
|
struct uuid_command load_cmd;
|
|
|
|
lldb::offset_t offset = lc_offset;
|
|
for (i=0; i<header.ncmds; ++i)
|
|
{
|
|
const lldb::offset_t cmd_offset = offset;
|
|
if (data.GetU32(&offset, &load_cmd, 2) == NULL)
|
|
break;
|
|
|
|
if (load_cmd.cmd == LC_UUID)
|
|
{
|
|
const uint8_t *uuid_bytes = data.PeekData(offset, 16);
|
|
|
|
if (uuid_bytes)
|
|
{
|
|
// OpenCL on Mac OS X uses the same UUID for each of its object files.
|
|
// We pretend these object files have no UUID to prevent crashing.
|
|
|
|
const uint8_t opencl_uuid[] = { 0x8c, 0x8e, 0xb3, 0x9b,
|
|
0x3b, 0xa8,
|
|
0x4b, 0x16,
|
|
0xb6, 0xa4,
|
|
0x27, 0x63, 0xbb, 0x14, 0xf0, 0x0d };
|
|
|
|
if (!memcmp(uuid_bytes, opencl_uuid, 16))
|
|
return false;
|
|
|
|
uuid.SetBytes (uuid_bytes);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
offset = cmd_offset + load_cmd.cmdsize;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool
|
|
ObjectFileMachO::GetArchitecture (const llvm::MachO::mach_header &header,
|
|
const lldb_private::DataExtractor &data,
|
|
lldb::offset_t lc_offset,
|
|
ArchSpec &arch)
|
|
{
|
|
arch.SetArchitecture (eArchTypeMachO, header.cputype, header.cpusubtype);
|
|
|
|
if (arch.IsValid())
|
|
{
|
|
llvm::Triple &triple = arch.GetTriple();
|
|
if (header.filetype == MH_PRELOAD)
|
|
{
|
|
// Set OS to "unknown" - this is a standalone binary with no dyld et al
|
|
triple.setOS(llvm::Triple::UnknownOS);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
struct load_command load_cmd;
|
|
|
|
lldb::offset_t offset = lc_offset;
|
|
for (uint32_t i=0; i<header.ncmds; ++i)
|
|
{
|
|
const lldb::offset_t cmd_offset = offset;
|
|
if (data.GetU32(&offset, &load_cmd, 2) == NULL)
|
|
break;
|
|
|
|
switch (load_cmd.cmd)
|
|
{
|
|
case LC_VERSION_MIN_IPHONEOS:
|
|
triple.setOS (llvm::Triple::IOS);
|
|
return true;
|
|
|
|
case LC_VERSION_MIN_MACOSX:
|
|
triple.setOS (llvm::Triple::MacOSX);
|
|
return true;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
offset = cmd_offset + load_cmd.cmdsize;
|
|
}
|
|
|
|
// Only set the OS to iOS for ARM, we don't want to set it for x86 and x86_64.
|
|
// We do this because we now have MacOSX or iOS as the OS value for x86 and
|
|
// x86_64 for normal desktop (MacOSX) and simulator (iOS) binaries. And if
|
|
// we compare a "x86_64-apple-ios" to a "x86_64-apple-" triple, it will say
|
|
// it is compatible (because the OS is unspecified in the second one and will
|
|
// match anything in the first
|
|
if (header.cputype == CPU_TYPE_ARM || header.cputype == CPU_TYPE_ARM64)
|
|
triple.setOS (llvm::Triple::IOS);
|
|
}
|
|
}
|
|
return arch.IsValid();
|
|
}
|
|
|
|
bool
|
|
ObjectFileMachO::GetUUID (lldb_private::UUID* uuid)
|
|
{
|
|
ModuleSP module_sp(GetModule());
|
|
if (module_sp)
|
|
{
|
|
lldb_private::Mutex::Locker locker(module_sp->GetMutex());
|
|
lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic);
|
|
return GetUUID (m_header, m_data, offset, *uuid);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
uint32_t
|
|
ObjectFileMachO::GetDependentModules (FileSpecList& files)
|
|
{
|
|
uint32_t count = 0;
|
|
ModuleSP module_sp(GetModule());
|
|
if (module_sp)
|
|
{
|
|
lldb_private::Mutex::Locker locker(module_sp->GetMutex());
|
|
struct load_command load_cmd;
|
|
lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic);
|
|
const bool resolve_path = false; // Don't resolve the dependent file paths since they may not reside on this system
|
|
uint32_t i;
|
|
for (i=0; i<m_header.ncmds; ++i)
|
|
{
|
|
const uint32_t cmd_offset = offset;
|
|
if (m_data.GetU32(&offset, &load_cmd, 2) == NULL)
|
|
break;
|
|
|
|
switch (load_cmd.cmd)
|
|
{
|
|
case LC_LOAD_DYLIB:
|
|
case LC_LOAD_WEAK_DYLIB:
|
|
case LC_REEXPORT_DYLIB:
|
|
case LC_LOAD_DYLINKER:
|
|
case LC_LOADFVMLIB:
|
|
case LC_LOAD_UPWARD_DYLIB:
|
|
{
|
|
uint32_t name_offset = cmd_offset + m_data.GetU32(&offset);
|
|
const char *path = m_data.PeekCStr(name_offset);
|
|
// Skip any path that starts with '@' since these are usually:
|
|
// @executable_path/.../file
|
|
// @rpath/.../file
|
|
if (path && path[0] != '@')
|
|
{
|
|
FileSpec file_spec(path, resolve_path);
|
|
if (files.AppendIfUnique(file_spec))
|
|
count++;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
offset = cmd_offset + load_cmd.cmdsize;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
lldb_private::Address
|
|
ObjectFileMachO::GetEntryPointAddress ()
|
|
{
|
|
// If the object file is not an executable it can't hold the entry point. m_entry_point_address
|
|
// is initialized to an invalid address, so we can just return that.
|
|
// If m_entry_point_address is valid it means we've found it already, so return the cached value.
|
|
|
|
if (!IsExecutable() || m_entry_point_address.IsValid())
|
|
return m_entry_point_address;
|
|
|
|
// Otherwise, look for the UnixThread or Thread command. The data for the Thread command is given in
|
|
// /usr/include/mach-o.h, but it is basically:
|
|
//
|
|
// uint32_t flavor - this is the flavor argument you would pass to thread_get_state
|
|
// uint32_t count - this is the count of longs in the thread state data
|
|
// struct XXX_thread_state state - this is the structure from <machine/thread_status.h> corresponding to the flavor.
|
|
// <repeat this trio>
|
|
//
|
|
// So we just keep reading the various register flavors till we find the GPR one, then read the PC out of there.
|
|
// FIXME: We will need to have a "RegisterContext data provider" class at some point that can get all the registers
|
|
// out of data in this form & attach them to a given thread. That should underlie the MacOS X User process plugin,
|
|
// and we'll also need it for the MacOS X Core File process plugin. When we have that we can also use it here.
|
|
//
|
|
// For now we hard-code the offsets and flavors we need:
|
|
//
|
|
//
|
|
|
|
ModuleSP module_sp(GetModule());
|
|
if (module_sp)
|
|
{
|
|
lldb_private::Mutex::Locker locker(module_sp->GetMutex());
|
|
struct load_command load_cmd;
|
|
lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic);
|
|
uint32_t i;
|
|
lldb::addr_t start_address = LLDB_INVALID_ADDRESS;
|
|
bool done = false;
|
|
|
|
for (i=0; i<m_header.ncmds; ++i)
|
|
{
|
|
const lldb::offset_t cmd_offset = offset;
|
|
if (m_data.GetU32(&offset, &load_cmd, 2) == NULL)
|
|
break;
|
|
|
|
switch (load_cmd.cmd)
|
|
{
|
|
case LC_UNIXTHREAD:
|
|
case LC_THREAD:
|
|
{
|
|
while (offset < cmd_offset + load_cmd.cmdsize)
|
|
{
|
|
uint32_t flavor = m_data.GetU32(&offset);
|
|
uint32_t count = m_data.GetU32(&offset);
|
|
if (count == 0)
|
|
{
|
|
// We've gotten off somehow, log and exit;
|
|
return m_entry_point_address;
|
|
}
|
|
|
|
switch (m_header.cputype)
|
|
{
|
|
case llvm::MachO::CPU_TYPE_ARM:
|
|
if (flavor == 1) // ARM_THREAD_STATE from mach/arm/thread_status.h
|
|
{
|
|
offset += 60; // This is the offset of pc in the GPR thread state data structure.
|
|
start_address = m_data.GetU32(&offset);
|
|
done = true;
|
|
}
|
|
break;
|
|
case llvm::MachO::CPU_TYPE_ARM64:
|
|
if (flavor == 6) // ARM_THREAD_STATE64 from mach/arm/thread_status.h
|
|
{
|
|
offset += 256; // This is the offset of pc in the GPR thread state data structure.
|
|
start_address = m_data.GetU64(&offset);
|
|
done = true;
|
|
}
|
|
break;
|
|
case llvm::MachO::CPU_TYPE_I386:
|
|
if (flavor == 1) // x86_THREAD_STATE32 from mach/i386/thread_status.h
|
|
{
|
|
offset += 40; // This is the offset of eip in the GPR thread state data structure.
|
|
start_address = m_data.GetU32(&offset);
|
|
done = true;
|
|
}
|
|
break;
|
|
case llvm::MachO::CPU_TYPE_X86_64:
|
|
if (flavor == 4) // x86_THREAD_STATE64 from mach/i386/thread_status.h
|
|
{
|
|
offset += 16 * 8; // This is the offset of rip in the GPR thread state data structure.
|
|
start_address = m_data.GetU64(&offset);
|
|
done = true;
|
|
}
|
|
break;
|
|
default:
|
|
return m_entry_point_address;
|
|
}
|
|
// Haven't found the GPR flavor yet, skip over the data for this flavor:
|
|
if (done)
|
|
break;
|
|
offset += count * 4;
|
|
}
|
|
}
|
|
break;
|
|
case LC_MAIN:
|
|
{
|
|
ConstString text_segment_name ("__TEXT");
|
|
uint64_t entryoffset = m_data.GetU64(&offset);
|
|
SectionSP text_segment_sp = GetSectionList()->FindSectionByName(text_segment_name);
|
|
if (text_segment_sp)
|
|
{
|
|
done = true;
|
|
start_address = text_segment_sp->GetFileAddress() + entryoffset;
|
|
}
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
if (done)
|
|
break;
|
|
|
|
// Go to the next load command:
|
|
offset = cmd_offset + load_cmd.cmdsize;
|
|
}
|
|
|
|
if (start_address != LLDB_INVALID_ADDRESS)
|
|
{
|
|
// We got the start address from the load commands, so now resolve that address in the sections
|
|
// of this ObjectFile:
|
|
if (!m_entry_point_address.ResolveAddressUsingFileSections (start_address, GetSectionList()))
|
|
{
|
|
m_entry_point_address.Clear();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We couldn't read the UnixThread load command - maybe it wasn't there. As a fallback look for the
|
|
// "start" symbol in the main executable.
|
|
|
|
ModuleSP module_sp (GetModule());
|
|
|
|
if (module_sp)
|
|
{
|
|
SymbolContextList contexts;
|
|
SymbolContext context;
|
|
if (module_sp->FindSymbolsWithNameAndType(ConstString ("start"), eSymbolTypeCode, contexts))
|
|
{
|
|
if (contexts.GetContextAtIndex(0, context))
|
|
m_entry_point_address = context.symbol->GetAddress();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return m_entry_point_address;
|
|
|
|
}
|
|
|
|
lldb_private::Address
|
|
ObjectFileMachO::GetHeaderAddress ()
|
|
{
|
|
lldb_private::Address header_addr;
|
|
SectionList *section_list = GetSectionList();
|
|
if (section_list)
|
|
{
|
|
SectionSP text_segment_sp (section_list->FindSectionByName (GetSegmentNameTEXT()));
|
|
if (text_segment_sp)
|
|
{
|
|
header_addr.SetSection (text_segment_sp);
|
|
header_addr.SetOffset (0);
|
|
}
|
|
}
|
|
return header_addr;
|
|
}
|
|
|
|
uint32_t
|
|
ObjectFileMachO::GetNumThreadContexts ()
|
|
{
|
|
ModuleSP module_sp(GetModule());
|
|
if (module_sp)
|
|
{
|
|
lldb_private::Mutex::Locker locker(module_sp->GetMutex());
|
|
if (!m_thread_context_offsets_valid)
|
|
{
|
|
m_thread_context_offsets_valid = true;
|
|
lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic);
|
|
FileRangeArray::Entry file_range;
|
|
thread_command thread_cmd;
|
|
for (uint32_t i=0; i<m_header.ncmds; ++i)
|
|
{
|
|
const uint32_t cmd_offset = offset;
|
|
if (m_data.GetU32(&offset, &thread_cmd, 2) == NULL)
|
|
break;
|
|
|
|
if (thread_cmd.cmd == LC_THREAD)
|
|
{
|
|
file_range.SetRangeBase (offset);
|
|
file_range.SetByteSize (thread_cmd.cmdsize - 8);
|
|
m_thread_context_offsets.Append (file_range);
|
|
}
|
|
offset = cmd_offset + thread_cmd.cmdsize;
|
|
}
|
|
}
|
|
}
|
|
return m_thread_context_offsets.GetSize();
|
|
}
|
|
|
|
lldb::RegisterContextSP
|
|
ObjectFileMachO::GetThreadContextAtIndex (uint32_t idx, lldb_private::Thread &thread)
|
|
{
|
|
lldb::RegisterContextSP reg_ctx_sp;
|
|
|
|
ModuleSP module_sp(GetModule());
|
|
if (module_sp)
|
|
{
|
|
lldb_private::Mutex::Locker locker(module_sp->GetMutex());
|
|
if (!m_thread_context_offsets_valid)
|
|
GetNumThreadContexts ();
|
|
|
|
const FileRangeArray::Entry *thread_context_file_range = m_thread_context_offsets.GetEntryAtIndex (idx);
|
|
if (thread_context_file_range)
|
|
{
|
|
|
|
DataExtractor data (m_data,
|
|
thread_context_file_range->GetRangeBase(),
|
|
thread_context_file_range->GetByteSize());
|
|
|
|
switch (m_header.cputype)
|
|
{
|
|
case llvm::MachO::CPU_TYPE_ARM64:
|
|
reg_ctx_sp.reset (new RegisterContextDarwin_arm64_Mach (thread, data));
|
|
break;
|
|
|
|
case llvm::MachO::CPU_TYPE_ARM:
|
|
reg_ctx_sp.reset (new RegisterContextDarwin_arm_Mach (thread, data));
|
|
break;
|
|
|
|
case llvm::MachO::CPU_TYPE_I386:
|
|
reg_ctx_sp.reset (new RegisterContextDarwin_i386_Mach (thread, data));
|
|
break;
|
|
|
|
case llvm::MachO::CPU_TYPE_X86_64:
|
|
reg_ctx_sp.reset (new RegisterContextDarwin_x86_64_Mach (thread, data));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return reg_ctx_sp;
|
|
}
|
|
|
|
|
|
ObjectFile::Type
|
|
ObjectFileMachO::CalculateType()
|
|
{
|
|
switch (m_header.filetype)
|
|
{
|
|
case MH_OBJECT: // 0x1u
|
|
if (GetAddressByteSize () == 4)
|
|
{
|
|
// 32 bit kexts are just object files, but they do have a valid
|
|
// UUID load command.
|
|
UUID uuid;
|
|
if (GetUUID(&uuid))
|
|
{
|
|
// this checking for the UUID load command is not enough
|
|
// we could eventually look for the symbol named
|
|
// "OSKextGetCurrentIdentifier" as this is required of kexts
|
|
if (m_strata == eStrataInvalid)
|
|
m_strata = eStrataKernel;
|
|
return eTypeSharedLibrary;
|
|
}
|
|
}
|
|
return eTypeObjectFile;
|
|
|
|
case MH_EXECUTE: return eTypeExecutable; // 0x2u
|
|
case MH_FVMLIB: return eTypeSharedLibrary; // 0x3u
|
|
case MH_CORE: return eTypeCoreFile; // 0x4u
|
|
case MH_PRELOAD: return eTypeSharedLibrary; // 0x5u
|
|
case MH_DYLIB: return eTypeSharedLibrary; // 0x6u
|
|
case MH_DYLINKER: return eTypeDynamicLinker; // 0x7u
|
|
case MH_BUNDLE: return eTypeSharedLibrary; // 0x8u
|
|
case MH_DYLIB_STUB: return eTypeStubLibrary; // 0x9u
|
|
case MH_DSYM: return eTypeDebugInfo; // 0xAu
|
|
case MH_KEXT_BUNDLE: return eTypeSharedLibrary; // 0xBu
|
|
default:
|
|
break;
|
|
}
|
|
return eTypeUnknown;
|
|
}
|
|
|
|
ObjectFile::Strata
|
|
ObjectFileMachO::CalculateStrata()
|
|
{
|
|
switch (m_header.filetype)
|
|
{
|
|
case MH_OBJECT: // 0x1u
|
|
{
|
|
// 32 bit kexts are just object files, but they do have a valid
|
|
// UUID load command.
|
|
UUID uuid;
|
|
if (GetUUID(&uuid))
|
|
{
|
|
// this checking for the UUID load command is not enough
|
|
// we could eventually look for the symbol named
|
|
// "OSKextGetCurrentIdentifier" as this is required of kexts
|
|
if (m_type == eTypeInvalid)
|
|
m_type = eTypeSharedLibrary;
|
|
|
|
return eStrataKernel;
|
|
}
|
|
}
|
|
return eStrataUnknown;
|
|
|
|
case MH_EXECUTE: // 0x2u
|
|
// Check for the MH_DYLDLINK bit in the flags
|
|
if (m_header.flags & MH_DYLDLINK)
|
|
{
|
|
return eStrataUser;
|
|
}
|
|
else
|
|
{
|
|
SectionList *section_list = GetSectionList();
|
|
if (section_list)
|
|
{
|
|
static ConstString g_kld_section_name ("__KLD");
|
|
if (section_list->FindSectionByName(g_kld_section_name))
|
|
return eStrataKernel;
|
|
}
|
|
}
|
|
return eStrataRawImage;
|
|
|
|
case MH_FVMLIB: return eStrataUser; // 0x3u
|
|
case MH_CORE: return eStrataUnknown; // 0x4u
|
|
case MH_PRELOAD: return eStrataRawImage; // 0x5u
|
|
case MH_DYLIB: return eStrataUser; // 0x6u
|
|
case MH_DYLINKER: return eStrataUser; // 0x7u
|
|
case MH_BUNDLE: return eStrataUser; // 0x8u
|
|
case MH_DYLIB_STUB: return eStrataUser; // 0x9u
|
|
case MH_DSYM: return eStrataUnknown; // 0xAu
|
|
case MH_KEXT_BUNDLE: return eStrataKernel; // 0xBu
|
|
default:
|
|
break;
|
|
}
|
|
return eStrataUnknown;
|
|
}
|
|
|
|
|
|
uint32_t
|
|
ObjectFileMachO::GetVersion (uint32_t *versions, uint32_t num_versions)
|
|
{
|
|
ModuleSP module_sp(GetModule());
|
|
if (module_sp)
|
|
{
|
|
lldb_private::Mutex::Locker locker(module_sp->GetMutex());
|
|
struct dylib_command load_cmd;
|
|
lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic);
|
|
uint32_t version_cmd = 0;
|
|
uint64_t version = 0;
|
|
uint32_t i;
|
|
for (i=0; i<m_header.ncmds; ++i)
|
|
{
|
|
const lldb::offset_t cmd_offset = offset;
|
|
if (m_data.GetU32(&offset, &load_cmd, 2) == NULL)
|
|
break;
|
|
|
|
if (load_cmd.cmd == LC_ID_DYLIB)
|
|
{
|
|
if (version_cmd == 0)
|
|
{
|
|
version_cmd = load_cmd.cmd;
|
|
if (m_data.GetU32(&offset, &load_cmd.dylib, 4) == NULL)
|
|
break;
|
|
version = load_cmd.dylib.current_version;
|
|
}
|
|
break; // Break for now unless there is another more complete version
|
|
// number load command in the future.
|
|
}
|
|
offset = cmd_offset + load_cmd.cmdsize;
|
|
}
|
|
|
|
if (version_cmd == LC_ID_DYLIB)
|
|
{
|
|
if (versions != NULL && num_versions > 0)
|
|
{
|
|
if (num_versions > 0)
|
|
versions[0] = (version & 0xFFFF0000ull) >> 16;
|
|
if (num_versions > 1)
|
|
versions[1] = (version & 0x0000FF00ull) >> 8;
|
|
if (num_versions > 2)
|
|
versions[2] = (version & 0x000000FFull);
|
|
// Fill in an remaining version numbers with invalid values
|
|
for (i=3; i<num_versions; ++i)
|
|
versions[i] = UINT32_MAX;
|
|
}
|
|
// The LC_ID_DYLIB load command has a version with 3 version numbers
|
|
// in it, so always return 3
|
|
return 3;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
ObjectFileMachO::GetArchitecture (ArchSpec &arch)
|
|
{
|
|
ModuleSP module_sp(GetModule());
|
|
if (module_sp)
|
|
{
|
|
lldb_private::Mutex::Locker locker(module_sp->GetMutex());
|
|
return GetArchitecture (m_header, m_data, MachHeaderSizeFromMagic(m_header.magic), arch);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
UUID
|
|
ObjectFileMachO::GetProcessSharedCacheUUID (Process *process)
|
|
{
|
|
UUID uuid;
|
|
if (process)
|
|
{
|
|
addr_t all_image_infos = process->GetImageInfoAddress();
|
|
|
|
// The address returned by GetImageInfoAddress may be the address of dyld (don't want)
|
|
// or it may be the address of the dyld_all_image_infos structure (want). The first four
|
|
// bytes will be either the version field (all_image_infos) or a Mach-O file magic constant.
|
|
// Version 13 and higher of dyld_all_image_infos is required to get the sharedCacheUUID field.
|
|
|
|
Error err;
|
|
uint32_t version_or_magic = process->ReadUnsignedIntegerFromMemory (all_image_infos, 4, -1, err);
|
|
if (version_or_magic != static_cast<uint32_t>(-1)
|
|
&& version_or_magic != MH_MAGIC
|
|
&& version_or_magic != MH_CIGAM
|
|
&& version_or_magic != MH_MAGIC_64
|
|
&& version_or_magic != MH_CIGAM_64
|
|
&& version_or_magic >= 13)
|
|
{
|
|
addr_t sharedCacheUUID_address = LLDB_INVALID_ADDRESS;
|
|
int wordsize = process->GetAddressByteSize();
|
|
if (wordsize == 8)
|
|
{
|
|
sharedCacheUUID_address = all_image_infos + 160; // sharedCacheUUID <mach-o/dyld_images.h>
|
|
}
|
|
if (wordsize == 4)
|
|
{
|
|
sharedCacheUUID_address = all_image_infos + 84; // sharedCacheUUID <mach-o/dyld_images.h>
|
|
}
|
|
if (sharedCacheUUID_address != LLDB_INVALID_ADDRESS)
|
|
{
|
|
uuid_t shared_cache_uuid;
|
|
if (process->ReadMemory (sharedCacheUUID_address, shared_cache_uuid, sizeof (uuid_t), err) == sizeof (uuid_t))
|
|
{
|
|
uuid.SetBytes (shared_cache_uuid);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return uuid;
|
|
}
|
|
|
|
UUID
|
|
ObjectFileMachO::GetLLDBSharedCacheUUID ()
|
|
{
|
|
UUID uuid;
|
|
#if defined (__APPLE__) && (defined (__arm__) || defined (__arm64__) || defined (__aarch64__))
|
|
uint8_t *(*dyld_get_all_image_infos)(void);
|
|
dyld_get_all_image_infos = (uint8_t*(*)()) dlsym (RTLD_DEFAULT, "_dyld_get_all_image_infos");
|
|
if (dyld_get_all_image_infos)
|
|
{
|
|
uint8_t *dyld_all_image_infos_address = dyld_get_all_image_infos();
|
|
if (dyld_all_image_infos_address)
|
|
{
|
|
uint32_t *version = (uint32_t*) dyld_all_image_infos_address; // version <mach-o/dyld_images.h>
|
|
if (*version >= 13)
|
|
{
|
|
uuid_t *sharedCacheUUID_address = 0;
|
|
int wordsize = sizeof (uint8_t *);
|
|
if (wordsize == 8)
|
|
{
|
|
sharedCacheUUID_address = (uuid_t*) ((uint8_t*) dyld_all_image_infos_address + 160); // sharedCacheUUID <mach-o/dyld_images.h>
|
|
}
|
|
else
|
|
{
|
|
sharedCacheUUID_address = (uuid_t*) ((uint8_t*) dyld_all_image_infos_address + 84); // sharedCacheUUID <mach-o/dyld_images.h>
|
|
}
|
|
uuid.SetBytes (sharedCacheUUID_address);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
return uuid;
|
|
}
|
|
|
|
uint32_t
|
|
ObjectFileMachO::GetMinimumOSVersion (uint32_t *versions, uint32_t num_versions)
|
|
{
|
|
if (m_min_os_versions.empty())
|
|
{
|
|
lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic);
|
|
bool success = false;
|
|
for (uint32_t i=0; success == false && i < m_header.ncmds; ++i)
|
|
{
|
|
const lldb::offset_t load_cmd_offset = offset;
|
|
|
|
version_min_command lc;
|
|
if (m_data.GetU32(&offset, &lc.cmd, 2) == NULL)
|
|
break;
|
|
if (lc.cmd == LC_VERSION_MIN_MACOSX || lc.cmd == LC_VERSION_MIN_IPHONEOS)
|
|
{
|
|
if (m_data.GetU32 (&offset, &lc.version, (sizeof(lc) / sizeof(uint32_t)) - 2))
|
|
{
|
|
const uint32_t xxxx = lc.version >> 16;
|
|
const uint32_t yy = (lc.version >> 8) & 0xffu;
|
|
const uint32_t zz = lc.version & 0xffu;
|
|
if (xxxx)
|
|
{
|
|
m_min_os_versions.push_back(xxxx);
|
|
m_min_os_versions.push_back(yy);
|
|
m_min_os_versions.push_back(zz);
|
|
}
|
|
success = true;
|
|
}
|
|
}
|
|
offset = load_cmd_offset + lc.cmdsize;
|
|
}
|
|
|
|
if (success == false)
|
|
{
|
|
// Push an invalid value so we don't keep trying to
|
|
m_min_os_versions.push_back(UINT32_MAX);
|
|
}
|
|
}
|
|
|
|
if (m_min_os_versions.size() > 1 || m_min_os_versions[0] != UINT32_MAX)
|
|
{
|
|
if (versions != NULL && num_versions > 0)
|
|
{
|
|
for (size_t i=0; i<num_versions; ++i)
|
|
{
|
|
if (i < m_min_os_versions.size())
|
|
versions[i] = m_min_os_versions[i];
|
|
else
|
|
versions[i] = 0;
|
|
}
|
|
}
|
|
return m_min_os_versions.size();
|
|
}
|
|
// Call the superclasses version that will empty out the data
|
|
return ObjectFile::GetMinimumOSVersion (versions, num_versions);
|
|
}
|
|
|
|
uint32_t
|
|
ObjectFileMachO::GetSDKVersion(uint32_t *versions, uint32_t num_versions)
|
|
{
|
|
if (m_sdk_versions.empty())
|
|
{
|
|
lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic);
|
|
bool success = false;
|
|
for (uint32_t i=0; success == false && i < m_header.ncmds; ++i)
|
|
{
|
|
const lldb::offset_t load_cmd_offset = offset;
|
|
|
|
version_min_command lc;
|
|
if (m_data.GetU32(&offset, &lc.cmd, 2) == NULL)
|
|
break;
|
|
if (lc.cmd == LC_VERSION_MIN_MACOSX || lc.cmd == LC_VERSION_MIN_IPHONEOS)
|
|
{
|
|
if (m_data.GetU32 (&offset, &lc.version, (sizeof(lc) / sizeof(uint32_t)) - 2))
|
|
{
|
|
const uint32_t xxxx = lc.sdk >> 16;
|
|
const uint32_t yy = (lc.sdk >> 8) & 0xffu;
|
|
const uint32_t zz = lc.sdk & 0xffu;
|
|
if (xxxx)
|
|
{
|
|
m_sdk_versions.push_back(xxxx);
|
|
m_sdk_versions.push_back(yy);
|
|
m_sdk_versions.push_back(zz);
|
|
}
|
|
success = true;
|
|
}
|
|
}
|
|
offset = load_cmd_offset + lc.cmdsize;
|
|
}
|
|
|
|
if (success == false)
|
|
{
|
|
// Push an invalid value so we don't keep trying to
|
|
m_sdk_versions.push_back(UINT32_MAX);
|
|
}
|
|
}
|
|
|
|
if (m_sdk_versions.size() > 1 || m_sdk_versions[0] != UINT32_MAX)
|
|
{
|
|
if (versions != NULL && num_versions > 0)
|
|
{
|
|
for (size_t i=0; i<num_versions; ++i)
|
|
{
|
|
if (i < m_sdk_versions.size())
|
|
versions[i] = m_sdk_versions[i];
|
|
else
|
|
versions[i] = 0;
|
|
}
|
|
}
|
|
return m_sdk_versions.size();
|
|
}
|
|
// Call the superclasses version that will empty out the data
|
|
return ObjectFile::GetSDKVersion (versions, num_versions);
|
|
}
|
|
|
|
|
|
bool
|
|
ObjectFileMachO::GetIsDynamicLinkEditor()
|
|
{
|
|
return m_header.filetype == llvm::MachO::MH_DYLINKER;
|
|
}
|
|
|
|
//------------------------------------------------------------------
|
|
// PluginInterface protocol
|
|
//------------------------------------------------------------------
|
|
lldb_private::ConstString
|
|
ObjectFileMachO::GetPluginName()
|
|
{
|
|
return GetPluginNameStatic();
|
|
}
|
|
|
|
uint32_t
|
|
ObjectFileMachO::GetPluginVersion()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
|
|
bool
|
|
ObjectFileMachO::SetLoadAddress (Target &target,
|
|
lldb::addr_t value,
|
|
bool value_is_offset)
|
|
{
|
|
ModuleSP module_sp = GetModule();
|
|
if (module_sp)
|
|
{
|
|
size_t num_loaded_sections = 0;
|
|
SectionList *section_list = GetSectionList ();
|
|
if (section_list)
|
|
{
|
|
lldb::addr_t mach_base_file_addr = LLDB_INVALID_ADDRESS;
|
|
const size_t num_sections = section_list->GetSize();
|
|
|
|
const bool is_memory_image = (bool)m_process_wp.lock();
|
|
const Strata strata = GetStrata();
|
|
static ConstString g_linkedit_segname ("__LINKEDIT");
|
|
if (value_is_offset)
|
|
{
|
|
// "value" is an offset to apply to each top level segment
|
|
for (size_t sect_idx = 0; sect_idx < num_sections; ++sect_idx)
|
|
{
|
|
// Iterate through the object file sections to find all
|
|
// of the sections that size on disk (to avoid __PAGEZERO)
|
|
// and load them
|
|
SectionSP section_sp (section_list->GetSectionAtIndex (sect_idx));
|
|
if (section_sp &&
|
|
section_sp->GetFileSize() > 0 &&
|
|
section_sp->IsThreadSpecific() == false &&
|
|
module_sp.get() == section_sp->GetModule().get())
|
|
{
|
|
// Ignore __LINKEDIT and __DWARF segments
|
|
if (section_sp->GetName() == g_linkedit_segname)
|
|
{
|
|
// Only map __LINKEDIT if we have an in memory image and this isn't
|
|
// a kernel binary like a kext or mach_kernel.
|
|
if (is_memory_image == false || strata == eStrataKernel)
|
|
continue;
|
|
}
|
|
if (target.GetSectionLoadList().SetSectionLoadAddress (section_sp, section_sp->GetFileAddress() + value))
|
|
++num_loaded_sections;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// "value" is the new base address of the mach_header, adjust each
|
|
// section accordingly
|
|
|
|
// First find the address of the mach header which is the first non-zero
|
|
// file sized section whose file offset is zero as this will be subtracted
|
|
// from each other valid section's vmaddr and then get "base_addr" added to
|
|
// it when loading the module in the target
|
|
for (size_t sect_idx = 0;
|
|
sect_idx < num_sections && mach_base_file_addr == LLDB_INVALID_ADDRESS;
|
|
++sect_idx)
|
|
{
|
|
// Iterate through the object file sections to find all
|
|
// of the sections that size on disk (to avoid __PAGEZERO)
|
|
// and load them
|
|
Section *section = section_list->GetSectionAtIndex (sect_idx).get();
|
|
if (section &&
|
|
section->GetFileSize() > 0 &&
|
|
section->GetFileOffset() == 0 &&
|
|
section->IsThreadSpecific() == false &&
|
|
module_sp.get() == section->GetModule().get())
|
|
{
|
|
// Ignore __LINKEDIT and __DWARF segments
|
|
if (section->GetName() == g_linkedit_segname)
|
|
{
|
|
// Only map __LINKEDIT if we have an in memory image and this isn't
|
|
// a kernel binary like a kext or mach_kernel.
|
|
if (is_memory_image == false || strata == eStrataKernel)
|
|
continue;
|
|
}
|
|
mach_base_file_addr = section->GetFileAddress();
|
|
}
|
|
}
|
|
|
|
if (mach_base_file_addr != LLDB_INVALID_ADDRESS)
|
|
{
|
|
for (size_t sect_idx = 0; sect_idx < num_sections; ++sect_idx)
|
|
{
|
|
// Iterate through the object file sections to find all
|
|
// of the sections that size on disk (to avoid __PAGEZERO)
|
|
// and load them
|
|
SectionSP section_sp (section_list->GetSectionAtIndex (sect_idx));
|
|
if (section_sp &&
|
|
section_sp->GetFileSize() > 0 &&
|
|
section_sp->IsThreadSpecific() == false &&
|
|
module_sp.get() == section_sp->GetModule().get())
|
|
{
|
|
// Ignore __LINKEDIT and __DWARF segments
|
|
if (section_sp->GetName() == g_linkedit_segname)
|
|
{
|
|
// Only map __LINKEDIT if we have an in memory image and this isn't
|
|
// a kernel binary like a kext or mach_kernel.
|
|
if (is_memory_image == false || strata == eStrataKernel)
|
|
continue;
|
|
}
|
|
if (target.GetSectionLoadList().SetSectionLoadAddress (section_sp, section_sp->GetFileAddress() - mach_base_file_addr + value))
|
|
++num_loaded_sections;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return num_loaded_sections > 0;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
ObjectFileMachO::SaveCore (const lldb::ProcessSP &process_sp,
|
|
const FileSpec &outfile,
|
|
Error &error)
|
|
{
|
|
if (process_sp)
|
|
{
|
|
Target &target = process_sp->GetTarget();
|
|
const ArchSpec target_arch = target.GetArchitecture();
|
|
const llvm::Triple &target_triple = target_arch.GetTriple();
|
|
if (target_triple.getVendor() == llvm::Triple::Apple &&
|
|
(target_triple.getOS() == llvm::Triple::MacOSX ||
|
|
target_triple.getOS() == llvm::Triple::IOS))
|
|
{
|
|
bool make_core = false;
|
|
switch (target_arch.GetMachine())
|
|
{
|
|
// arm64 core file writing is having some problem with writing down the
|
|
// dyld shared images info struct and/or the main executable binary. May
|
|
// turn out to be a debugserver problem, not sure yet.
|
|
// case llvm::Triple::aarch64:
|
|
|
|
case llvm::Triple::arm:
|
|
case llvm::Triple::x86:
|
|
case llvm::Triple::x86_64:
|
|
make_core = true;
|
|
break;
|
|
default:
|
|
error.SetErrorStringWithFormat ("unsupported core architecture: %s", target_triple.str().c_str());
|
|
break;
|
|
}
|
|
|
|
if (make_core)
|
|
{
|
|
std::vector<segment_command_64> segment_load_commands;
|
|
// uint32_t range_info_idx = 0;
|
|
MemoryRegionInfo range_info;
|
|
Error range_error = process_sp->GetMemoryRegionInfo(0, range_info);
|
|
const uint32_t addr_byte_size = target_arch.GetAddressByteSize();
|
|
const ByteOrder byte_order = target_arch.GetByteOrder();
|
|
if (range_error.Success())
|
|
{
|
|
while (range_info.GetRange().GetRangeBase() != LLDB_INVALID_ADDRESS)
|
|
{
|
|
const addr_t addr = range_info.GetRange().GetRangeBase();
|
|
const addr_t size = range_info.GetRange().GetByteSize();
|
|
|
|
if (size == 0)
|
|
break;
|
|
|
|
// Calculate correct protections
|
|
uint32_t prot = 0;
|
|
if (range_info.GetReadable() == MemoryRegionInfo::eYes)
|
|
prot |= VM_PROT_READ;
|
|
if (range_info.GetWritable() == MemoryRegionInfo::eYes)
|
|
prot |= VM_PROT_WRITE;
|
|
if (range_info.GetExecutable() == MemoryRegionInfo::eYes)
|
|
prot |= VM_PROT_EXECUTE;
|
|
|
|
// printf ("[%3u] [0x%16.16" PRIx64 " - 0x%16.16" PRIx64 ") %c%c%c\n",
|
|
// range_info_idx,
|
|
// addr,
|
|
// size,
|
|
// (prot & VM_PROT_READ ) ? 'r' : '-',
|
|
// (prot & VM_PROT_WRITE ) ? 'w' : '-',
|
|
// (prot & VM_PROT_EXECUTE) ? 'x' : '-');
|
|
|
|
if (prot != 0)
|
|
{
|
|
uint32_t cmd_type = LC_SEGMENT_64;
|
|
uint32_t segment_size = sizeof (segment_command_64);
|
|
if (addr_byte_size == 4)
|
|
{
|
|
cmd_type = LC_SEGMENT;
|
|
segment_size = sizeof (segment_command);
|
|
}
|
|
segment_command_64 segment = {
|
|
cmd_type, // uint32_t cmd;
|
|
segment_size, // uint32_t cmdsize;
|
|
{0}, // char segname[16];
|
|
addr, // uint64_t vmaddr; // uint32_t for 32-bit Mach-O
|
|
size, // uint64_t vmsize; // uint32_t for 32-bit Mach-O
|
|
0, // uint64_t fileoff; // uint32_t for 32-bit Mach-O
|
|
size, // uint64_t filesize; // uint32_t for 32-bit Mach-O
|
|
prot, // uint32_t maxprot;
|
|
prot, // uint32_t initprot;
|
|
0, // uint32_t nsects;
|
|
0 }; // uint32_t flags;
|
|
segment_load_commands.push_back(segment);
|
|
}
|
|
else
|
|
{
|
|
// No protections and a size of 1 used to be returned from old
|
|
// debugservers when we asked about a region that was past the
|
|
// last memory region and it indicates the end...
|
|
if (size == 1)
|
|
break;
|
|
}
|
|
|
|
range_error = process_sp->GetMemoryRegionInfo(range_info.GetRange().GetRangeEnd(), range_info);
|
|
if (range_error.Fail())
|
|
break;
|
|
}
|
|
|
|
StreamString buffer (Stream::eBinary,
|
|
addr_byte_size,
|
|
byte_order);
|
|
|
|
mach_header_64 mach_header;
|
|
if (addr_byte_size == 8)
|
|
{
|
|
mach_header.magic = MH_MAGIC_64;
|
|
}
|
|
else
|
|
{
|
|
mach_header.magic = MH_MAGIC;
|
|
}
|
|
mach_header.cputype = target_arch.GetMachOCPUType();
|
|
mach_header.cpusubtype = target_arch.GetMachOCPUSubType();
|
|
mach_header.filetype = MH_CORE;
|
|
mach_header.ncmds = segment_load_commands.size();
|
|
mach_header.flags = 0;
|
|
mach_header.reserved = 0;
|
|
ThreadList &thread_list = process_sp->GetThreadList();
|
|
const uint32_t num_threads = thread_list.GetSize();
|
|
|
|
// Make an array of LC_THREAD data items. Each one contains
|
|
// the contents of the LC_THREAD load command. The data doesn't
|
|
// contain the load command + load command size, we will
|
|
// add the load command and load command size as we emit the data.
|
|
std::vector<StreamString> LC_THREAD_datas(num_threads);
|
|
for (auto &LC_THREAD_data : LC_THREAD_datas)
|
|
{
|
|
LC_THREAD_data.GetFlags().Set(Stream::eBinary);
|
|
LC_THREAD_data.SetAddressByteSize(addr_byte_size);
|
|
LC_THREAD_data.SetByteOrder(byte_order);
|
|
}
|
|
for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx)
|
|
{
|
|
ThreadSP thread_sp (thread_list.GetThreadAtIndex(thread_idx));
|
|
if (thread_sp)
|
|
{
|
|
switch (mach_header.cputype)
|
|
{
|
|
case llvm::MachO::CPU_TYPE_ARM64:
|
|
RegisterContextDarwin_arm64_Mach::Create_LC_THREAD (thread_sp.get(), LC_THREAD_datas[thread_idx]);
|
|
break;
|
|
|
|
case llvm::MachO::CPU_TYPE_ARM:
|
|
RegisterContextDarwin_arm_Mach::Create_LC_THREAD (thread_sp.get(), LC_THREAD_datas[thread_idx]);
|
|
break;
|
|
|
|
case llvm::MachO::CPU_TYPE_I386:
|
|
RegisterContextDarwin_i386_Mach::Create_LC_THREAD (thread_sp.get(), LC_THREAD_datas[thread_idx]);
|
|
break;
|
|
|
|
case llvm::MachO::CPU_TYPE_X86_64:
|
|
RegisterContextDarwin_x86_64_Mach::Create_LC_THREAD (thread_sp.get(), LC_THREAD_datas[thread_idx]);
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// The size of the load command is the size of the segments...
|
|
if (addr_byte_size == 8)
|
|
{
|
|
mach_header.sizeofcmds = segment_load_commands.size() * sizeof (struct segment_command_64);
|
|
}
|
|
else
|
|
{
|
|
mach_header.sizeofcmds = segment_load_commands.size() * sizeof (struct segment_command);
|
|
}
|
|
|
|
// and the size of all LC_THREAD load command
|
|
for (const auto &LC_THREAD_data : LC_THREAD_datas)
|
|
{
|
|
++mach_header.ncmds;
|
|
mach_header.sizeofcmds += 8 + LC_THREAD_data.GetSize();
|
|
}
|
|
|
|
printf ("mach_header: 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x\n",
|
|
mach_header.magic,
|
|
mach_header.cputype,
|
|
mach_header.cpusubtype,
|
|
mach_header.filetype,
|
|
mach_header.ncmds,
|
|
mach_header.sizeofcmds,
|
|
mach_header.flags,
|
|
mach_header.reserved);
|
|
|
|
// Write the mach header
|
|
buffer.PutHex32(mach_header.magic);
|
|
buffer.PutHex32(mach_header.cputype);
|
|
buffer.PutHex32(mach_header.cpusubtype);
|
|
buffer.PutHex32(mach_header.filetype);
|
|
buffer.PutHex32(mach_header.ncmds);
|
|
buffer.PutHex32(mach_header.sizeofcmds);
|
|
buffer.PutHex32(mach_header.flags);
|
|
if (addr_byte_size == 8)
|
|
{
|
|
buffer.PutHex32(mach_header.reserved);
|
|
}
|
|
|
|
// Skip the mach header and all load commands and align to the next
|
|
// 0x1000 byte boundary
|
|
addr_t file_offset = buffer.GetSize() + mach_header.sizeofcmds;
|
|
if (file_offset & 0x00000fff)
|
|
{
|
|
file_offset += 0x00001000ull;
|
|
file_offset &= (~0x00001000ull + 1);
|
|
}
|
|
|
|
for (auto &segment : segment_load_commands)
|
|
{
|
|
segment.fileoff = file_offset;
|
|
file_offset += segment.filesize;
|
|
}
|
|
|
|
// Write out all of the LC_THREAD load commands
|
|
for (const auto &LC_THREAD_data : LC_THREAD_datas)
|
|
{
|
|
const size_t LC_THREAD_data_size = LC_THREAD_data.GetSize();
|
|
buffer.PutHex32(LC_THREAD);
|
|
buffer.PutHex32(8 + LC_THREAD_data_size); // cmd + cmdsize + data
|
|
buffer.Write(LC_THREAD_data.GetData(), LC_THREAD_data_size);
|
|
}
|
|
|
|
// Write out all of the segment load commands
|
|
for (const auto &segment : segment_load_commands)
|
|
{
|
|
printf ("0x%8.8x 0x%8.8x [0x%16.16" PRIx64 " - 0x%16.16" PRIx64 ") [0x%16.16" PRIx64 " 0x%16.16" PRIx64 ") 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x]\n",
|
|
segment.cmd,
|
|
segment.cmdsize,
|
|
segment.vmaddr,
|
|
segment.vmaddr + segment.vmsize,
|
|
segment.fileoff,
|
|
segment.filesize,
|
|
segment.maxprot,
|
|
segment.initprot,
|
|
segment.nsects,
|
|
segment.flags);
|
|
|
|
buffer.PutHex32(segment.cmd);
|
|
buffer.PutHex32(segment.cmdsize);
|
|
buffer.PutRawBytes(segment.segname, sizeof(segment.segname));
|
|
if (addr_byte_size == 8)
|
|
{
|
|
buffer.PutHex64(segment.vmaddr);
|
|
buffer.PutHex64(segment.vmsize);
|
|
buffer.PutHex64(segment.fileoff);
|
|
buffer.PutHex64(segment.filesize);
|
|
}
|
|
else
|
|
{
|
|
buffer.PutHex32(static_cast<uint32_t>(segment.vmaddr));
|
|
buffer.PutHex32(static_cast<uint32_t>(segment.vmsize));
|
|
buffer.PutHex32(static_cast<uint32_t>(segment.fileoff));
|
|
buffer.PutHex32(static_cast<uint32_t>(segment.filesize));
|
|
}
|
|
buffer.PutHex32(segment.maxprot);
|
|
buffer.PutHex32(segment.initprot);
|
|
buffer.PutHex32(segment.nsects);
|
|
buffer.PutHex32(segment.flags);
|
|
}
|
|
|
|
File core_file;
|
|
std::string core_file_path(outfile.GetPath());
|
|
error = core_file.Open(core_file_path.c_str(),
|
|
File::eOpenOptionWrite |
|
|
File::eOpenOptionTruncate |
|
|
File::eOpenOptionCanCreate);
|
|
if (error.Success())
|
|
{
|
|
// Read 1 page at a time
|
|
uint8_t bytes[0x1000];
|
|
// Write the mach header and load commands out to the core file
|
|
size_t bytes_written = buffer.GetString().size();
|
|
error = core_file.Write(buffer.GetString().data(), bytes_written);
|
|
if (error.Success())
|
|
{
|
|
// Now write the file data for all memory segments in the process
|
|
for (const auto &segment : segment_load_commands)
|
|
{
|
|
if (core_file.SeekFromStart(segment.fileoff) == -1)
|
|
{
|
|
error.SetErrorStringWithFormat("unable to seek to offset 0x%" PRIx64 " in '%s'", segment.fileoff, core_file_path.c_str());
|
|
break;
|
|
}
|
|
|
|
printf ("Saving %" PRId64 " bytes of data for memory region at 0x%" PRIx64 "\n", segment.vmsize, segment.vmaddr);
|
|
addr_t bytes_left = segment.vmsize;
|
|
addr_t addr = segment.vmaddr;
|
|
Error memory_read_error;
|
|
while (bytes_left > 0 && error.Success())
|
|
{
|
|
const size_t bytes_to_read = bytes_left > sizeof(bytes) ? sizeof(bytes) : bytes_left;
|
|
const size_t bytes_read = process_sp->ReadMemory(addr, bytes, bytes_to_read, memory_read_error);
|
|
if (bytes_read == bytes_to_read)
|
|
{
|
|
size_t bytes_written = bytes_read;
|
|
error = core_file.Write(bytes, bytes_written);
|
|
bytes_left -= bytes_read;
|
|
addr += bytes_read;
|
|
}
|
|
else
|
|
{
|
|
// Some pages within regions are not readable, those
|
|
// should be zero filled
|
|
memset (bytes, 0, bytes_to_read);
|
|
size_t bytes_written = bytes_to_read;
|
|
error = core_file.Write(bytes, bytes_written);
|
|
bytes_left -= bytes_to_read;
|
|
addr += bytes_to_read;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error.SetErrorString("process doesn't support getting memory region info");
|
|
}
|
|
}
|
|
return true; // This is the right plug to handle saving core files for this process
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|