2016-01-06 15:12:03 -05:00
//===-- ObjectFileMachO.cpp -------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// C Includes
// C++ Includes
// Other libraries and framework includes
# include "llvm/ADT/StringRef.h"
// Project includes
# 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/DWARFCallFrameInfo.h"
# include "lldb/Symbol/ObjectFile.h"
# include "lldb/Target/MemoryRegionInfo.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
# define THUMB_ADDRESS_BIT_MASK 0xfffffffffffffffeull
using namespace lldb ;
using namespace lldb_private ;
using namespace llvm : : MachO ;
// Some structure definitions needed for parsing the dyld shared cache files
// found on iOS devices.
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 ;
} ;
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 ) ;
}
void
InvalidateAllRegisters ( ) override
{
// 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 :
int
DoReadGPR ( lldb : : tid_t tid , int flavor , GPR & gpr ) override
{
return 0 ;
}
int
DoReadFPU ( lldb : : tid_t tid , int flavor , FPU & fpu ) override
{
return 0 ;
}
int
DoReadEXC ( lldb : : tid_t tid , int flavor , EXC & exc ) override
{
return 0 ;
}
int
DoWriteGPR ( lldb : : tid_t tid , int flavor , const GPR & gpr ) override
{
return 0 ;
}
int
DoWriteFPU ( lldb : : tid_t tid , int flavor , const FPU & fpu ) override
{
return 0 ;
}
int
DoWriteEXC ( lldb : : tid_t tid , int flavor , const EXC & exc ) override
{
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 ) ;
}
void
InvalidateAllRegisters ( ) override
{
// 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 :
int
DoReadGPR ( lldb : : tid_t tid , int flavor , GPR & gpr ) override
{
return 0 ;
}
int
DoReadFPU ( lldb : : tid_t tid , int flavor , FPU & fpu ) override
{
return 0 ;
}
int
DoReadEXC ( lldb : : tid_t tid , int flavor , EXC & exc ) override
{
return 0 ;
}
int
DoWriteGPR ( lldb : : tid_t tid , int flavor , const GPR & gpr ) override
{
return 0 ;
}
int
DoWriteFPU ( lldb : : tid_t tid , int flavor , const FPU & fpu ) override
{
return 0 ;
}
int
DoWriteEXC ( lldb : : tid_t tid , int flavor , const EXC & exc ) override
{
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 ) ;
}
void
InvalidateAllRegisters ( ) override
{
// 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 )
{
2016-07-23 16:50:09 -04:00
case GPRAltRegSet :
2016-01-06 15:12:03 -05:00
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 :
int
DoReadGPR ( lldb : : tid_t tid , int flavor , GPR & gpr ) override
{
return - 1 ;
}
int
DoReadFPU ( lldb : : tid_t tid , int flavor , FPU & fpu ) override
{
return - 1 ;
}
int
DoReadEXC ( lldb : : tid_t tid , int flavor , EXC & exc ) override
{
return - 1 ;
}
int
DoReadDBG ( lldb : : tid_t tid , int flavor , DBG & dbg ) override
{
return - 1 ;
}
int
DoWriteGPR ( lldb : : tid_t tid , int flavor , const GPR & gpr ) override
{
return 0 ;
}
int
DoWriteFPU ( lldb : : tid_t tid , int flavor , const FPU & fpu ) override
{
return 0 ;
}
int
DoWriteEXC ( lldb : : tid_t tid , int flavor , const EXC & exc ) override
{
return 0 ;
}
int
DoWriteDBG ( lldb : : tid_t tid , int flavor , const DBG & dbg ) override
{
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 ) ;
}
void
InvalidateAllRegisters ( ) override
{
// 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 :
int
DoReadGPR ( lldb : : tid_t tid , int flavor , GPR & gpr ) override
{
return - 1 ;
}
int
DoReadFPU ( lldb : : tid_t tid , int flavor , FPU & fpu ) override
{
return - 1 ;
}
int
DoReadEXC ( lldb : : tid_t tid , int flavor , EXC & exc ) override
{
return - 1 ;
}
int
DoReadDBG ( lldb : : tid_t tid , int flavor , DBG & dbg ) override
{
return - 1 ;
}
int
DoWriteGPR ( lldb : : tid_t tid , int flavor , const GPR & gpr ) override
{
return 0 ;
}
int
DoWriteFPU ( lldb : : tid_t tid , int flavor , const FPU & fpu ) override
{
return 0 ;
}
int
DoWriteEXC ( lldb : : tid_t tid , int flavor , const EXC & exc ) override
{
return 0 ;
}
int
DoWriteDBG ( lldb : : tid_t tid , int flavor , const DBG & dbg ) override
{
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 - > MemoryMapFileContentsIfLocal ( 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 - > MemoryMapFileContentsIfLocal ( 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 ) ;
spec . SetObjectSize ( length ) ;
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 : : GetSegmentNameDATA_DIRTY ( )
{
static ConstString g_segment_name ( " __DATA_DIRTY " ) ;
return g_segment_name ;
}
const ConstString &
ObjectFileMachO : : GetSegmentNameDATA_CONST ( )
{
static ConstString g_segment_name ( " __DATA_CONST " ) ;
return g_segment_name ;
}
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 ( ) ,
2016-07-23 16:50:09 -04:00
m_thread_context_offsets_valid ( false ) ,
m_reexported_dylibs ( ) ,
m_allow_assembly_emulation_unwind_plans ( true )
2016-01-06 15:12:03 -05:00
{
: : 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 ( ) ,
2016-07-23 16:50:09 -04:00
m_thread_context_offsets_valid ( false ) ,
m_reexported_dylibs ( ) ,
m_allow_assembly_emulation_unwind_plans ( true )
2016-01-06 15:12:03 -05:00
{
: : memset ( & m_header , 0 , sizeof ( m_header ) ) ;
: : memset ( & m_dysymtab , 0 , sizeof ( m_dysymtab ) ) ;
}
bool
ObjectFileMachO : : ParseHeader ( DataExtractor & data ,
lldb : : offset_t * data_offset_ptr ,
llvm : : MachO : : mach_header & header )
{
data . SetByteOrder ( 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 ( endian : : InlHostByteOrder ( ) ) ;
data . SetAddressByteSize ( 4 ) ;
can_parse = true ;
break ;
case MH_MAGIC_64 :
data . SetByteOrder ( endian : : InlHostByteOrder ( ) ) ;
data . SetAddressByteSize ( 8 ) ;
can_parse = true ;
is_64_bit = true ;
break ;
case MH_CIGAM :
data . SetByteOrder ( endian : : InlHostByteOrder ( ) = = eByteOrderBig ? eByteOrderLittle : eByteOrderBig ) ;
data . SetAddressByteSize ( 4 ) ;
can_parse = true ;
break ;
case MH_CIGAM_64 :
data . SetByteOrder ( 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 )
{
2016-07-23 16:50:09 -04:00
std : : lock_guard < std : : recursive_mutex > guard ( module_sp - > GetMutex ( ) ) ;
2016-01-06 15:12:03 -05:00
bool can_parse = false ;
lldb : : offset_t offset = 0 ;
m_data . SetByteOrder ( 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 ( endian : : InlHostByteOrder ( ) ) ;
m_data . SetAddressByteSize ( 4 ) ;
can_parse = true ;
break ;
case MH_MAGIC_64 :
m_data . SetByteOrder ( endian : : InlHostByteOrder ( ) ) ;
m_data . SetAddressByteSize ( 8 ) ;
can_parse = true ;
break ;
case MH_CIGAM :
m_data . SetByteOrder ( endian : : InlHostByteOrder ( ) = = eByteOrderBig ? eByteOrderLittle : eByteOrderBig ) ;
m_data . SetAddressByteSize ( 4 ) ;
can_parse = true ;
break ;
case MH_CIGAM_64 :
m_data . SetByteOrder ( 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 - > GetAddressRef ( ) . 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 :
case eSectionTypeGoSymtab :
return eAddressClassData ;
case eSectionTypeDebug :
case eSectionTypeDWARFDebugAbbrev :
case eSectionTypeDWARFDebugAddr :
case eSectionTypeDWARFDebugAranges :
case eSectionTypeDWARFDebugFrame :
case eSectionTypeDWARFDebugInfo :
case eSectionTypeDWARFDebugLine :
case eSectionTypeDWARFDebugLoc :
case eSectionTypeDWARFDebugMacInfo :
case eSectionTypeDWARFDebugMacro :
case eSectionTypeDWARFDebugPubNames :
case eSectionTypeDWARFDebugPubTypes :
case eSectionTypeDWARFDebugRanges :
case eSectionTypeDWARFDebugStr :
case eSectionTypeDWARFDebugStrOffsets :
case eSectionTypeDWARFAppleNames :
case eSectionTypeDWARFAppleTypes :
case eSectionTypeDWARFAppleNamespaces :
case eSectionTypeDWARFAppleObjC :
return eAddressClassDebug ;
case eSectionTypeEHFrame :
case eSectionTypeARMexidx :
case eSectionTypeARMextab :
case eSectionTypeCompactUnwind :
return eAddressClassRuntime ;
2016-07-23 16:50:09 -04:00
case eSectionTypeAbsoluteAddress :
2016-01-06 15:12:03 -05:00
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 )
{
2016-07-23 16:50:09 -04:00
std : : lock_guard < std : : recursive_mutex > guard ( module_sp - > GetMutex ( ) ) ;
2016-01-06 15:12:03 -05:00
if ( m_symtab_ap . get ( ) = = NULL )
{
m_symtab_ap . reset ( new Symtab ( this ) ) ;
2016-07-23 16:50:09 -04:00
std : : lock_guard < std : : recursive_mutex > symtab_guard ( m_symtab_ap - > GetMutex ( ) ) ;
2016-01-06 15:12:03 -05:00
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 ;
// LC_ENCRYPTION_INFO and LC_ENCRYPTION_INFO_64 have the same sizes for
// the 3 fields we care about, so treat them the same.
if ( encryption_cmd . cmd = = LC_ENCRYPTION_INFO | | encryption_cmd . cmd = = LC_ENCRYPTION_INFO_64 )
{
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 ) )
{
2016-07-23 16:50:09 -04:00
const uint32_t segment_permissions =
( ( load_cmd . initprot & VM_PROT_READ ) ? ePermissionsReadable : 0 ) |
( ( load_cmd . initprot & VM_PROT_WRITE ) ? ePermissionsWritable : 0 ) |
( ( load_cmd . initprot & VM_PROT_EXECUTE ) ? ePermissionsExecutable : 0 ) ;
2016-01-06 15:12:03 -05:00
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 ) ;
2016-07-23 16:50:09 -04:00
segment_sp - > SetPermissions ( segment_permissions ) ;
2016-01-06 15:12:03 -05:00
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 ( & sect64 , 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 , & sect64 . 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 ) ;
2016-07-23 16:50:09 -04:00
segment_sp - > SetPermissions ( segment_permissions ) ;
2016-01-06 15:12:03 -05:00
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 " ) ;
static ConstString g_sect_name_go_symtab ( " __gosymtab " ) ;
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_go_symtab )
sect_type = eSectionTypeGoSymtab ;
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 ) ;
2016-07-23 16:50:09 -04:00
section_sp - > SetPermissions ( segment_permissions ) ;
2016-01-06 15:12:03 -05:00
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 bool
ParseTrieEntries ( DataExtractor & data ,
lldb : : offset_t offset ,
const bool is_arm ,
std : : vector < llvm : : StringRef > & nameSlices ,
std : : set < lldb : : addr_t > & resolver_addresses ,
std : : vector < TrieEntryWithOffset > & output )
{
if ( ! data . ValidOffset ( offset ) )
return true ;
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 )
{
e . entry . other = data . GetULEB128 ( & offset ) ;
uint64_t resolver_addr = e . entry . other ;
if ( is_arm )
resolver_addr & = THUMB_ADDRESS_BIT_MASK ;
resolver_addresses . insert ( resolver_addr ) ;
}
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 ) {
const char * cstr = data . GetCStr ( & children_offset ) ;
if ( cstr )
nameSlices . push_back ( llvm : : StringRef ( cstr ) ) ;
else
return false ; // Corrupt data
lldb : : offset_t childNodeOffset = data . GetULEB128 ( & children_offset ) ;
if ( childNodeOffset )
{
if ( ! ParseTrieEntries ( data ,
childNodeOffset ,
is_arm ,
nameSlices ,
resolver_addresses ,
output ) )
{
return false ;
}
}
nameSlices . pop_back ( ) ;
}
return true ;
}
// Read the UUID out of a dyld_shared_cache file on-disk.
UUID
ObjectFileMachO : : GetSharedCacheUUID ( FileSpec dyld_shared_cache , const ByteOrder byte_order , const uint32_t addr_byte_size )
{
UUID dsc_uuid ;
DataBufferSP dsc_data_sp = dyld_shared_cache . MemoryMapFileContentsIfLocal ( 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 [ 7 ] ;
lldb : : offset_t offset = 0 ;
memcpy ( version_str , dsc_header_data . GetData ( & offset , 6 ) , 6 ) ;
version_str [ 6 ] = ' \0 ' ;
if ( strcmp ( version_str , " dyld_v " ) = = 0 )
{
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 ) ;
}
}
return dsc_uuid ;
}
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 ) ) ;
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_$_ " ) ;
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 )
{
addr_t linkedit_load_addr = linkedit_section_sp - > GetLoadBaseAddress ( & target ) ;
if ( linkedit_load_addr = = LLDB_INVALID_ADDRESS )
{
// We might be trying to access the symbol table before the __LINKEDIT's load
// address has been set in the target. We can't fail to read the symbol table,
// so calculate the right address manually
linkedit_load_addr = CalculateSectionLoadAddressForMemoryImage ( m_memory_addr , GetMachHeaderSection ( ) , linkedit_section_sp . get ( ) ) ;
}
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_DATA_DIRTY = GetSegmentNameDATA_DIRTY ( ) ;
const ConstString & g_segment_name_DATA_CONST = GetSegmentNameDATA_CONST ( ) ;
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 data_dirty_section_sp ( section_list - > FindSectionByName ( g_segment_name_DATA_DIRTY ) ) ;
SectionSP data_const_section_sp ( section_list - > FindSectionByName ( g_segment_name_DATA_CONST ) ) ;
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 , eRegisterKindEHFrame , 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 ( ) ;
2016-07-23 16:50:09 -04:00
// For user process binaries (executables, dylibs, frameworks, bundles), if we don't have
// LC_FUNCTION_STARTS/eh_frame section in this binary, we're going to assume the binary
// has been stripped. Don't allow assembly language instruction emulation because we don't
// know proper function start boundaries.
//
// For all other types of binaries (kernels, stand-alone bare board binaries, kexts), they
// may not have LC_FUNCTION_STARTS / eh_frame sections - we should not make any assumptions
// about them based on that.
if ( function_starts_count = = 0 & & CalculateStrata ( ) = = eStrataUser )
{
m_allow_assembly_emulation_unwind_plans = false ;
Log * unwind_or_symbol_log ( lldb_private : : GetLogIfAnyCategoriesSet ( LIBLLDB_LOG_SYMBOLS | LIBLLDB_LOG_UNWIND ) ) ;
if ( unwind_or_symbol_log )
module_sp - > LogMessage ( unwind_or_symbol_log , " no LC_FUNCTION_STARTS, will not allow assembly profiled unwinds " ) ;
}
2016-01-06 15:12:03 -05:00
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 ,
is_arm ,
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 ;
}
}
}
typedef std : : set < ConstString > IndirectSymbols ;
IndirectSymbols indirect_symbol_names ;
# 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 ] ;
char dsc_path_development [ 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 ( ) ) ;
snprintf ( dsc_path_development , sizeof ( dsc_path ) , " %s%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 ( ) ,
" .development " ) ;
FileSpec dsc_nondevelopment_filespec ( dsc_path , false ) ;
FileSpec dsc_development_filespec ( dsc_path_development , false ) ;
FileSpec dsc_filespec ;
UUID dsc_uuid ;
UUID process_shared_cache_uuid ;
if ( process )
{
process_shared_cache_uuid = GetProcessSharedCacheUUID ( process ) ;
}
// First see if we can find an exact match for the inferior process shared cache UUID in
// the development or non-development shared caches on disk.
if ( process_shared_cache_uuid . IsValid ( ) )
{
if ( dsc_development_filespec . Exists ( ) )
{
UUID dsc_development_uuid = GetSharedCacheUUID ( dsc_development_filespec , byte_order , addr_byte_size ) ;
if ( dsc_development_uuid . IsValid ( ) & & dsc_development_uuid = = process_shared_cache_uuid )
{
dsc_filespec = dsc_development_filespec ;
dsc_uuid = dsc_development_uuid ;
}
}
if ( ! dsc_uuid . IsValid ( ) & & dsc_nondevelopment_filespec . Exists ( ) )
{
UUID dsc_nondevelopment_uuid = GetSharedCacheUUID ( dsc_nondevelopment_filespec , byte_order , addr_byte_size ) ;
if ( dsc_nondevelopment_uuid . IsValid ( ) & & dsc_nondevelopment_uuid = = process_shared_cache_uuid )
{
dsc_filespec = dsc_nondevelopment_filespec ;
dsc_uuid = dsc_nondevelopment_uuid ;
}
}
}
// Failing a UUID match, prefer the development dyld_shared cache if both are present.
if ( ! dsc_filespec . Exists ( ) )
{
if ( dsc_development_filespec . Exists ( ) )
{
dsc_filespec = dsc_development_filespec ;
}
else
{
dsc_filespec = dsc_nondevelopment_filespec ;
}
}
/* 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 dyld shared cache header to find the unmapped symbols
DataBufferSP dsc_data_sp = dsc_filespec . MemoryMapFileContentsIfLocal ( 0 , sizeof ( struct lldb_copy_dyld_cache_header_v1 ) ) ;
if ( ! dsc_uuid . IsValid ( ) )
{
dsc_uuid = GetSharedCacheUUID ( dsc_filespec , byte_order , addr_byte_size ) ;
}
if ( dsc_data_sp )
{
DataExtractor dsc_header_data ( dsc_data_sp , byte_order , addr_byte_size ) ;
bool uuid_match = true ;
if ( dsc_uuid . IsValid ( ) & & process )
{
if ( process_shared_cache_uuid . IsValid ( ) & & dsc_uuid ! = process_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_v1 ) )
{
DataBufferSP dsc_mapping_info_data_sp = dsc_filespec . MemoryMapFileContentsIfLocal ( 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 . MemoryMapFileContentsIfLocal ( localSymbolsOffset , localSymbolsSize ) )
{
DataExtractor dsc_local_symbols_data ( dsc_local_symbols_data_sp , byte_order , addr_byte_size ) ;
offset = 0 ;
typedef std : : map < ConstString , uint16_t > UndefinedNameToDescMap ;
typedef std : : map < uint32_t , ConstString > SymbolIndexToName ;
UndefinedNameToDescMap undefined_name_to_desc ;
SymbolIndexToName reexport_shlib_needs_fixup ;
// 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 ;
bool set_value = true ;
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.
is_gsym = true ;
sym [ sym_idx ] . SetExternal ( true ) ;
if ( symbol_name & & symbol_name [ 0 ] = = ' _ ' & & symbol_name [ 1 ] = = ' O ' )
{
llvm : : StringRef symbol_name_ref ( symbol_name ) ;
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 ( 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 ) ;
if ( symbol_name & & symbol_name [ 0 ] )
{
type = ObjectFile : : GetSymbolTypeFromName ( symbol_name + 1 , 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
2016-07-23 16:50:09 -04:00
const char * so_path = sym [ sym_idx - 1 ] . GetMangled ( ) . GetDemangledName ( lldb : : eLanguageTypeUnknown ) . AsCString ( ) ;
2016-01-06 15:12:03 -05:00
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 :
{
const char * reexport_name_cstr = strtab_data . PeekCStr ( nlist . n_value ) ;
if ( reexport_name_cstr & & reexport_name_cstr [ 0 ] )
{
type = eSymbolTypeReExported ;
ConstString reexport_name ( reexport_name_cstr + ( ( reexport_name_cstr [ 0 ] = = ' _ ' ) ? 1 : 0 ) ) ;
sym [ sym_idx ] . SetReExportedSymbolName ( reexport_name ) ;
set_value = false ;
reexport_shlib_needs_fixup [ sym_idx ] = reexport_name ;
indirect_symbol_names . insert ( ConstString ( symbol_name + ( ( symbol_name [ 0 ] = = ' _ ' ) ? 1 : 0 ) ) ) ;
}
else
type = eSymbolTypeUndefined ;
}
break ;
case N_UNDF :
if ( symbol_name & & symbol_name [ 0 ] )
{
ConstString undefined_name ( symbol_name + ( ( symbol_name [ 0 ] = = ' _ ' ) ? 1 : 0 ) ) ;
undefined_name_to_desc [ undefined_name ] = nlist . n_desc ;
}
// Fall through
case N_PBUD :
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 ( ) ) | |
symbol_section - > IsDescendant ( data_dirty_section_sp . get ( ) ) | |
symbol_section - > IsDescendant ( data_const_section_sp . get ( ) ) )
{
if ( symbol_sect_name & & : : strstr ( symbol_sect_name , " __objc " ) = = symbol_sect_name )
{
type = eSymbolTypeRuntime ;
if ( symbol_name )
{
llvm : : StringRef symbol_name_ref ( symbol_name ) ;
if ( symbol_name_ref . startswith ( " _OBJC_ " ) )
{
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 )
{
2016-07-23 16:50:09 -04:00
const char * gsym_name = sym [ sym_idx ] . GetMangled ( ) . GetName ( lldb : : eLanguageTypeUnknown , Mangled : : ePreferMangled ) . GetCString ( ) ;
2016-01-06 15:12:03 -05:00
if ( gsym_name )
N_GSYM_name_to_sym_idx [ gsym_name ] = 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 & = THUMB_ADDRESS_BIT_MASK ;
}
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 & = THUMB_ADDRESS_BIT_MASK ;
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 )
{
2016-07-23 16:50:09 -04:00
if ( sym [ sym_idx ] . GetMangled ( ) . GetName ( lldb : : eLanguageTypeUnknown , Mangled : : ePreferMangled ) = = sym [ pos - > second ] . GetMangled ( ) . GetName ( lldb : : eLanguageTypeUnknown , Mangled : : ePreferMangled ) )
2016-01-06 15:12:03 -05:00
{
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 | |
type = = eSymbolTypeObjCClass | |
type = = eSymbolTypeObjCMetaClass | |
type = = eSymbolTypeObjCIVar )
{
// 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 )
{
2016-07-23 16:50:09 -04:00
if ( sym [ sym_idx ] . GetMangled ( ) . GetName ( lldb : : eLanguageTypeUnknown , Mangled : : ePreferMangled ) = = sym [ pos - > second ] . GetMangled ( ) . GetName ( lldb : : eLanguageTypeUnknown , Mangled : : ePreferMangled ) )
2016-01-06 15:12:03 -05:00
{
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
{
2016-07-23 16:50:09 -04:00
const char * gsym_name = sym [ sym_idx ] . GetMangled ( ) . GetName ( lldb : : eLanguageTypeUnknown , Mangled : : ePreferMangled ) . GetCString ( ) ;
2016-01-06 15:12:03 -05:00
if ( gsym_name )
{
// Combine N_GSYM stab entries with the non stab symbol
ConstNameToSymbolIndexMap : : const_iterator pos = N_GSYM_name_to_sym_idx . find ( gsym_name ) ;
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 ] . GetAddressRef ( ) . SetSection ( symbol_section ) ;
sym [ GSYM_sym_idx ] . GetAddressRef ( ) . SetOffset ( symbol_value ) ;
// We just need the flags from the linker symbol, so put these flags
// into the N_GSYM 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 ) ;
if ( set_value )
{
sym [ sym_idx ] . GetAddressRef ( ) . SetSection ( symbol_section ) ;
sym [ sym_idx ] . GetAddressRef ( ) . 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
}
}
for ( const auto & pos : reexport_shlib_needs_fixup )
{
const auto undef_pos = undefined_name_to_desc . find ( pos . second ) ;
if ( undef_pos ! = undefined_name_to_desc . end ( ) )
{
const uint8_t dylib_ordinal = llvm : : MachO : : GET_LIBRARY_ORDINAL ( undef_pos - > second ) ;
if ( dylib_ordinal > 0 & & dylib_ordinal < dylib_files . GetSize ( ) )
sym [ pos . first ] . SetReExportedSymbolSharedLibrary ( dylib_files . GetFileSpecAtIndex ( dylib_ordinal - 1 ) ) ;
}
}
}
}
}
}
}
// 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 ;
}
typedef std : : map < ConstString , uint16_t > UndefinedNameToDescMap ;
typedef std : : map < uint32_t , ConstString > SymbolIndexToName ;
UndefinedNameToDescMap undefined_name_to_desc ;
SymbolIndexToName reexport_shlib_needs_fixup ;
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 ;
bool set_value = true ;
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.
is_gsym = true ;
sym [ sym_idx ] . SetExternal ( true ) ;
if ( symbol_name & & symbol_name [ 0 ] = = ' _ ' & & symbol_name [ 1 ] = = ' O ' )
{
llvm : : StringRef symbol_name_ref ( symbol_name ) ;
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 ( 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 ) ;
if ( symbol_name & & symbol_name [ 0 ] )
{
type = ObjectFile : : GetSymbolTypeFromName ( symbol_name + 1 , 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 ( lldb : : eLanguageTypeUnknown ) . 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 ) ;
2016-07-23 16:50:09 -04:00
LLVM_FALLTHROUGH ;
2016-01-06 15:12:03 -05:00
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 :
{
const char * reexport_name_cstr = strtab_data . PeekCStr ( nlist . n_value ) ;
if ( reexport_name_cstr & & reexport_name_cstr [ 0 ] )
{
type = eSymbolTypeReExported ;
ConstString reexport_name ( reexport_name_cstr + ( ( reexport_name_cstr [ 0 ] = = ' _ ' ) ? 1 : 0 ) ) ;
sym [ sym_idx ] . SetReExportedSymbolName ( reexport_name ) ;
set_value = false ;
reexport_shlib_needs_fixup [ sym_idx ] = reexport_name ;
indirect_symbol_names . insert ( ConstString ( symbol_name + ( ( symbol_name [ 0 ] = = ' _ ' ) ? 1 : 0 ) ) ) ;
}
else
type = eSymbolTypeUndefined ;
}
break ;
case N_UNDF :
if ( symbol_name & & symbol_name [ 0 ] )
{
ConstString undefined_name ( symbol_name + ( ( symbol_name [ 0 ] = = ' _ ' ) ? 1 : 0 ) ) ;
undefined_name_to_desc [ undefined_name ] = nlist . n_desc ;
}
2016-07-23 16:50:09 -04:00
LLVM_FALLTHROUGH ;
2016-01-06 15:12:03 -05:00
case N_PBUD :
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 ( ) ) | |
symbol_section - > IsDescendant ( data_dirty_section_sp . get ( ) ) | |
symbol_section - > IsDescendant ( data_const_section_sp . get ( ) ) )
{
if ( symbol_sect_name & & : : strstr ( symbol_sect_name , " __objc " ) = = symbol_sect_name )
{
type = eSymbolTypeRuntime ;
if ( symbol_name )
{
llvm : : StringRef symbol_name_ref ( symbol_name ) ;
if ( symbol_name_ref . startswith ( " _OBJC_ " ) )
{
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 )
{
const char * gsym_name = sym [ sym_idx ] . GetMangled ( ) . GetName ( lldb : : eLanguageTypeUnknown , Mangled : : ePreferMangled ) . GetCString ( ) ;
if ( gsym_name )
N_GSYM_name_to_sym_idx [ gsym_name ] = 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 & = THUMB_ADDRESS_BIT_MASK ;
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 & = THUMB_ADDRESS_BIT_MASK ;
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 ( lldb : : eLanguageTypeUnknown , Mangled : : ePreferMangled ) = = sym [ pos - > second ] . GetMangled ( ) . GetName ( lldb : : eLanguageTypeUnknown , 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 | |
type = = eSymbolTypeObjCClass | |
type = = eSymbolTypeObjCMetaClass | |
type = = eSymbolTypeObjCIVar )
{
// 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 ( lldb : : eLanguageTypeUnknown , Mangled : : ePreferMangled ) = = sym [ pos - > second ] . GetMangled ( ) . GetName ( lldb : : eLanguageTypeUnknown , 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
const char * gsym_name = sym [ sym_idx ] . GetMangled ( ) . GetName ( lldb : : eLanguageTypeUnknown , Mangled : : ePreferMangled ) . GetCString ( ) ;
if ( gsym_name )
{
ConstNameToSymbolIndexMap : : const_iterator pos = N_GSYM_name_to_sym_idx . find ( gsym_name ) ;
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 ] . GetAddressRef ( ) . SetSection ( symbol_section ) ;
sym [ GSYM_sym_idx ] . GetAddressRef ( ) . SetOffset ( symbol_value ) ;
// We just need the flags from the linker symbol, so put these flags
// into the N_GSYM 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 ) ;
if ( set_value )
{
sym [ sym_idx ] . GetAddressRef ( ) . SetSection ( symbol_section ) ;
sym [ sym_idx ] . GetAddressRef ( ) . 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 ( ) ;
}
}
for ( const auto & pos : reexport_shlib_needs_fixup )
{
const auto undef_pos = undefined_name_to_desc . find ( pos . second ) ;
if ( undef_pos ! = undefined_name_to_desc . end ( ) )
{
const uint8_t dylib_ordinal = llvm : : MachO : : GET_LIBRARY_ORDINAL ( undef_pos - > second ) ;
if ( dylib_ordinal > 0 & & dylib_ordinal < dylib_files . GetSize ( ) )
sym [ pos . first ] . SetReExportedSymbolSharedLibrary ( dylib_files . GetFileSpecAtIndex ( dylib_ordinal - 1 ) ) ;
}
}
}
uint32_t synthetic_sym_id = symtab_load_command . nsyms ;
if ( function_starts_count > 0 )
{
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 ) ;
}
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 & = THUMB_ADDRESS_BIT_MASK ;
}
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 & = THUMB_ADDRESS_BIT_MASK ;
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 ;
}
sym [ sym_idx ] . SetID ( synthetic_sym_id + + ) ;
2016-07-23 16:50:09 -04:00
sym [ sym_idx ] . GetMangled ( ) . SetDemangledName ( GetNextSyntheticSymbolName ( ) ) ;
2016-01-06 15:12:03 -05:00
sym [ sym_idx ] . SetType ( eSymbolTypeCode ) ;
sym [ sym_idx ] . SetIsSynthetic ( true ) ;
sym [ sym_idx ] . GetAddressRef ( ) = 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 - > GetAddressRef ( ) = 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 ] . GetAddressRef ( ) = 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 )
{
// Only add indirect symbols from the Trie entries if we
// didn't have a N_INDR nlist entry for this already
if ( indirect_symbol_names . find ( e . entry . name ) = = indirect_symbol_names . end ( ) )
{
// 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 )
{
2016-07-23 16:50:09 -04:00
std : : lock_guard < std : : recursive_mutex > guard ( module_sp - > GetMutex ( ) ) ;
2016-01-06 15:12:03 -05:00
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 ( ) ;
// Set OS to an unspecified unknown or a "*" so it can match any OS
triple . setOS ( llvm : : Triple : : UnknownOS ) ;
triple . setOSName ( llvm : : StringRef ( ) ) ;
if ( header . filetype = = MH_PRELOAD )
{
2016-07-23 16:50:09 -04:00
if ( header . cputype = = CPU_TYPE_ARM )
{
// If this is a 32-bit arm binary, and it's a standalone binary,
// force the Vendor to Apple so we don't accidentally pick up
// the generic armv7 ABI at runtime. Apple's armv7 ABI always uses
// r7 for the frame pointer register; most other armv7 ABIs use a
// combination of r7 and r11.
triple . setVendor ( llvm : : Triple : : Apple ) ;
}
else
{
// Set vendor to an unspecified unknown or a "*" so it can match any vendor
// This is required for correct behavior of EFI debugging on x86_64
triple . setVendor ( llvm : : Triple : : UnknownVendor ) ;
triple . setVendorName ( llvm : : StringRef ( ) ) ;
}
2016-01-06 15:12:03 -05:00
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 llvm : : MachO : : LC_VERSION_MIN_IPHONEOS :
triple . setOS ( llvm : : Triple : : IOS ) ;
return true ;
case llvm : : MachO : : LC_VERSION_MIN_MACOSX :
triple . setOS ( llvm : : Triple : : MacOSX ) ;
return true ;
case llvm : : MachO : : LC_VERSION_MIN_TVOS :
triple . setOS ( llvm : : Triple : : TvOS ) ;
return true ;
case llvm : : MachO : : LC_VERSION_MIN_WATCHOS :
triple . setOS ( llvm : : Triple : : WatchOS ) ;
return true ;
default :
break ;
}
offset = cmd_offset + load_cmd . cmdsize ;
}
if ( header . filetype ! = MH_KEXT_BUNDLE )
{
// We didn't find a LC_VERSION_MIN load command and this isn't a KEXT
// so lets not say our Vendor is Apple, leave it as an unspecified unknown
triple . setVendor ( llvm : : Triple : : UnknownVendor ) ;
triple . setVendorName ( llvm : : StringRef ( ) ) ;
}
}
}
return arch . IsValid ( ) ;
}
bool
ObjectFileMachO : : GetUUID ( lldb_private : : UUID * uuid )
{
ModuleSP module_sp ( GetModule ( ) ) ;
if ( module_sp )
{
2016-07-23 16:50:09 -04:00
std : : lock_guard < std : : recursive_mutex > guard ( module_sp - > GetMutex ( ) ) ;
2016-01-06 15:12:03 -05:00
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 )
{
2016-07-23 16:50:09 -04:00
std : : lock_guard < std : : recursive_mutex > guard ( module_sp - > GetMutex ( ) ) ;
2016-01-06 15:12:03 -05:00
struct load_command load_cmd ;
lldb : : offset_t offset = MachHeaderSizeFromMagic ( m_header . magic ) ;
std : : vector < std : : string > rpath_paths ;
std : : vector < std : : string > rpath_relative_paths ;
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_RPATH :
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 ) ;
if ( path )
{
if ( load_cmd . cmd = = LC_RPATH )
rpath_paths . push_back ( path ) ;
else
{
if ( path [ 0 ] = = ' @ ' )
{
if ( strncmp ( path , " @rpath " , strlen ( " @rpath " ) ) = = 0 )
rpath_relative_paths . push_back ( path + strlen ( " @rpath " ) ) ;
}
else
{
FileSpec file_spec ( path , resolve_path ) ;
if ( files . AppendIfUnique ( file_spec ) )
count + + ;
}
}
}
}
break ;
default :
break ;
}
offset = cmd_offset + load_cmd . cmdsize ;
}
if ( ! rpath_paths . empty ( ) )
{
// Fixup all LC_RPATH values to be absolute paths
FileSpec this_file_spec ( m_file ) ;
this_file_spec . ResolvePath ( ) ;
std : : string loader_path ( " @loader_path " ) ;
std : : string executable_path ( " @executable_path " ) ;
for ( auto & rpath : rpath_paths )
{
if ( rpath . find ( loader_path ) = = 0 )
{
rpath . erase ( 0 , loader_path . size ( ) ) ;
rpath . insert ( 0 , this_file_spec . GetDirectory ( ) . GetCString ( ) ) ;
}
else if ( rpath . find ( executable_path ) = = 0 )
{
rpath . erase ( 0 , executable_path . size ( ) ) ;
rpath . insert ( 0 , this_file_spec . GetDirectory ( ) . GetCString ( ) ) ;
}
}
for ( const auto & rpath_relative_path : rpath_relative_paths )
{
for ( const auto & rpath : rpath_paths )
{
std : : string path = rpath ;
path + = rpath_relative_path ;
// It is OK to resolve this path because we must find a file on
// disk for us to accept it anyway if it is rpath relative.
FileSpec file_spec ( path , true ) ;
// Remove any redundant parts of the path (like "../foo") since
// LC_RPATH values often contain "..".
file_spec . NormalizePath ( ) ;
if ( file_spec . Exists ( ) & & files . AppendIfUnique ( file_spec ) )
{
count + + ;
break ;
}
}
}
}
}
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 )
{
2016-07-23 16:50:09 -04:00
std : : lock_guard < std : : recursive_mutex > guard ( module_sp - > GetMutex ( ) ) ;
2016-01-06 15:12:03 -05:00
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 :
2016-07-23 16:50:09 -04:00
if ( flavor = = 1 | | flavor = = 9 ) // ARM_THREAD_STATE/ARM_THREAD_STATE32 from mach/arm/thread_status.h
2016-01-06 15:12:03 -05:00
{
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 ;
}
}
2016-07-23 16:50:09 -04:00
break ;
2016-01-06 15:12:03 -05:00
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 )
{
2016-07-23 16:50:09 -04:00
std : : lock_guard < std : : recursive_mutex > guard ( module_sp - > GetMutex ( ) ) ;
2016-01-06 15:12:03 -05:00
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 )
{
2016-07-23 16:50:09 -04:00
std : : lock_guard < std : : recursive_mutex > guard ( module_sp - > GetMutex ( ) ) ;
2016-01-06 15:12:03 -05:00
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 )
{
2016-07-23 16:50:09 -04:00
std : : lock_guard < std : : recursive_mutex > guard ( module_sp - > GetMutex ( ) ) ;
2016-01-06 15:12:03 -05:00
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 )
{
2016-07-23 16:50:09 -04:00
std : : lock_guard < std : : recursive_mutex > guard ( module_sp - > GetMutex ( ) ) ;
2016-01-06 15:12:03 -05:00
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 = = llvm : : MachO : : LC_VERSION_MIN_MACOSX
| | lc . cmd = = llvm : : MachO : : LC_VERSION_MIN_IPHONEOS
| | lc . cmd = = llvm : : MachO : : LC_VERSION_MIN_TVOS
| | lc . cmd = = llvm : : MachO : : LC_VERSION_MIN_WATCHOS )
{
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 = = llvm : : MachO : : LC_VERSION_MIN_MACOSX
| | lc . cmd = = llvm : : MachO : : LC_VERSION_MIN_IPHONEOS
| | lc . cmd = = llvm : : MachO : : LC_VERSION_MIN_TVOS
| | lc . cmd = = llvm : : MachO : : LC_VERSION_MIN_WATCHOS )
{
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 ;
}
2016-07-23 16:50:09 -04:00
bool
ObjectFileMachO : : AllowAssemblyEmulationUnwindPlans ( )
{
return m_allow_assembly_emulation_unwind_plans ;
}
2016-01-06 15:12:03 -05:00
//------------------------------------------------------------------
// PluginInterface protocol
//------------------------------------------------------------------
lldb_private : : ConstString
ObjectFileMachO : : GetPluginName ( )
{
return GetPluginNameStatic ( ) ;
}
uint32_t
ObjectFileMachO : : GetPluginVersion ( )
{
return 1 ;
}
Section *
ObjectFileMachO : : GetMachHeaderSection ( )
{
// Find the first address of the mach header which is the first non-zero
// file sized section whose file offset is zero. This is the base file address
// of the mach-o file which can be subtracted from the vmaddr of the other
// segments found in memory and added to the load address
ModuleSP module_sp = GetModule ( ) ;
if ( module_sp )
{
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 ( ) ;
for ( size_t sect_idx = 0 ;
sect_idx < num_sections & & mach_base_file_addr = = LLDB_INVALID_ADDRESS ;
+ + sect_idx )
{
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 ( ) )
{
return section ;
}
}
}
}
return nullptr ;
}
lldb : : addr_t
ObjectFileMachO : : CalculateSectionLoadAddressForMemoryImage ( lldb : : addr_t mach_header_load_address , const Section * mach_header_section , const Section * section )
{
ModuleSP module_sp = GetModule ( ) ;
if ( module_sp & & mach_header_section & & section & & mach_header_load_address ! = LLDB_INVALID_ADDRESS )
{
lldb : : addr_t mach_header_file_addr = mach_header_section - > GetFileAddress ( ) ;
if ( mach_header_file_addr ! = LLDB_INVALID_ADDRESS )
{
if ( section & &
section - > GetFileSize ( ) > 0 & &
section - > IsThreadSpecific ( ) = = false & &
module_sp . get ( ) = = section - > GetModule ( ) . get ( ) )
{
// Ignore __LINKEDIT and __DWARF segments
if ( section - > GetName ( ) = = GetSegmentNameLINKEDIT ( ) )
{
// Only map __LINKEDIT if we have an in memory image and this isn't
// a kernel binary like a kext or mach_kernel.
const bool is_memory_image = ( bool ) m_process_wp . lock ( ) ;
const Strata strata = GetStrata ( ) ;
if ( is_memory_image = = false | | strata = = eStrataKernel )
return LLDB_INVALID_ADDRESS ;
}
return section - > GetFileAddress ( ) - mach_header_file_addr + mach_header_load_address ;
}
}
}
return LLDB_INVALID_ADDRESS ;
}
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 )
{
const size_t num_sections = section_list - > GetSize ( ) ;
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 ( ) = = GetSegmentNameLINKEDIT ( ) )
{
// Only map __LINKEDIT if we have an in memory image and this isn't
// a kernel binary like a kext or mach_kernel.
const bool is_memory_image = ( bool ) m_process_wp . lock ( ) ;
const Strata strata = GetStrata ( ) ;
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
Section * mach_header_section = GetMachHeaderSection ( ) ;
if ( mach_header_section )
{
for ( size_t sect_idx = 0 ; sect_idx < num_sections ; + + sect_idx )
{
SectionSP section_sp ( section_list - > GetSectionAtIndex ( sect_idx ) ) ;
lldb : : addr_t section_load_addr = CalculateSectionLoadAddressForMemoryImage ( value , mach_header_section , section_sp . get ( ) ) ;
if ( section_load_addr ! = LLDB_INVALID_ADDRESS )
{
if ( target . GetSectionLoadList ( ) . SetSectionLoadAddress ( section_sp , section_load_addr ) )
+ + 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
| | target_triple . getOS ( ) = = llvm : : Triple : : WatchOS
| | target_triple . getOS ( ) = = llvm : : Triple : : TvOS ) )
{
bool make_core = false ;
switch ( target_arch . GetMachine ( ) )
{
case llvm : : Triple : : aarch64 :
case llvm : : Triple : : arm :
case llvm : : Triple : : thumb :
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 ;
}