forked from OSchip/llvm-project
abstract load commands to that WriterMachO can write 64-bit and 32-bit mach-o files
llvm-svn: 158133
This commit is contained in:
parent
aefcedae65
commit
40b8655082
|
@ -24,29 +24,23 @@
|
|||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
class load_command {
|
||||
public:
|
||||
uint32_t cmd;
|
||||
uint32_t cmdsize;
|
||||
|
||||
void copyTo(uint8_t* to, bool swap=false) {
|
||||
::memcpy(to, (uint8_t*)&cmd, cmdsize);
|
||||
}
|
||||
};
|
||||
|
||||
enum {
|
||||
MH_MAGIC = 0xfeedface,
|
||||
MAGIC_64 = 0xfeedfacf
|
||||
MH_MAGIC = 0xfeedface,
|
||||
MH_MAGIC_64 = 0xfeedfacf
|
||||
};
|
||||
|
||||
enum {
|
||||
CPU_TYPE_ARM = 0x0000000C,
|
||||
CPU_TYPE_I386 = 0x00000007,
|
||||
CPU_TYPE_X86_64 = 0x01000007
|
||||
};
|
||||
|
||||
enum {
|
||||
CPU_SUBTYPE_X86_ALL = 0x00000003,
|
||||
CPU_SUBTYPE_X86_64_ALL = 0x00000003
|
||||
CPU_SUBTYPE_X86_ALL = 0x00000003,
|
||||
CPU_SUBTYPE_X86_64_ALL = 0x00000003,
|
||||
CPU_SUBTYPE_ARM_V6 = 0x00000006,
|
||||
CPU_SUBTYPE_ARM_V7 = 0x00000009
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -60,6 +54,10 @@ enum {
|
|||
};
|
||||
|
||||
|
||||
//
|
||||
// Every mach-o file starts with this header. The header size is
|
||||
// 28 bytes for 32-bit architecures and 32-bytes for 64-bit architectures.
|
||||
//
|
||||
class mach_header {
|
||||
public:
|
||||
uint32_t magic;
|
||||
|
@ -75,19 +73,91 @@ public:
|
|||
return (magic == 0xfeedfacf) ? 32 : 28;
|
||||
}
|
||||
|
||||
void copyTo(uint8_t* to, bool swap=false) {
|
||||
void copyTo(uint8_t *to, bool swap=false) {
|
||||
::memcpy(to, (char*)&magic, this->size());
|
||||
}
|
||||
|
||||
void recordLoadCommand(const class load_command* lc) {
|
||||
++ncmds;
|
||||
sizeofcmds += lc->cmdsize;
|
||||
void recordLoadCommand(const class load_command *lc);
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Every mach-o file has a list of load commands after the mach_header.
|
||||
// Each load command starts with a type and length, so you can iterate
|
||||
// through the load commands even if you don't understand the content
|
||||
// of a particular type.
|
||||
//
|
||||
// The model for handling endianness and 32 vs 64 bitness is that the in-memory
|
||||
// object is always 64-bit and the native endianess. The endianess swapping
|
||||
// and pointer sizing is done when writing (copyTo method) or when reading
|
||||
// (constructor that takes a buffer).
|
||||
//
|
||||
// The load_command subclasses are designed so to mirror the traditional "C"
|
||||
// structs, so you can get and set the same field names (e.g. seg->vmaddr = 0).
|
||||
//
|
||||
class load_command {
|
||||
public:
|
||||
const uint32_t cmd; // type of load command
|
||||
const uint32_t cmdsize; // length of load command including this header
|
||||
|
||||
load_command(uint32_t cmdNumber, uint32_t sz, bool is64, bool align=false)
|
||||
: cmd(cmdNumber), cmdsize(pointerAlign(sz, is64, align)) {
|
||||
}
|
||||
|
||||
virtual ~load_command() {
|
||||
}
|
||||
|
||||
virtual void copyTo(uint8_t *to, bool swap=false) = 0;
|
||||
private:
|
||||
// Load commands must be pointer-size aligned. Most load commands are
|
||||
// a fixed size, so there is a runtime assert to check those. For variable
|
||||
// length load commands, setting the align option to true will add padding
|
||||
// at the end of the load command to round up its size for proper alignment.
|
||||
uint32_t pointerAlign(uint32_t size, bool is64, bool align) {
|
||||
if ( align ) {
|
||||
if ( is64 )
|
||||
return (size + 7) & (-8);
|
||||
else
|
||||
return (size + 3) & (-4);
|
||||
}
|
||||
else {
|
||||
if ( is64 )
|
||||
assert((size % 8) == 0);
|
||||
else
|
||||
assert((size % 4) == 0);
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
void mach_header::recordLoadCommand(const load_command *lc) {
|
||||
++ncmds;
|
||||
sizeofcmds += lc->cmdsize;
|
||||
}
|
||||
|
||||
// Supported load command types
|
||||
enum {
|
||||
LC_SEGMENT = 0x00000001,
|
||||
LC_SYMTAB = 0x00000002,
|
||||
LC_LOAD_DYLIB = 0x0000000C,
|
||||
LC_LOAD_DYLINKER = 0x0000000E,
|
||||
LC_SEGMENT_64 = 0x00000019,
|
||||
LC_MAIN = 0x80000028,
|
||||
LC_DYLD_INFO_ONLY = 0x80000022
|
||||
};
|
||||
|
||||
// Memory protection bit used in segment_command.initprot
|
||||
enum {
|
||||
VM_PROT_NONE = 0x0,
|
||||
VM_PROT_READ = 0x1,
|
||||
VM_PROT_WRITE = 0x2,
|
||||
VM_PROT_EXECUTE = 0x4,
|
||||
};
|
||||
|
||||
// Bits for the section.flags field
|
||||
enum {
|
||||
// Section "type" is the low byte
|
||||
SECTION_TYPE = 0x000000FF,
|
||||
S_REGULAR = 0x00000000,
|
||||
S_ZEROFILL = 0x00000001,
|
||||
|
@ -96,10 +166,28 @@ enum {
|
|||
S_LAZY_SYMBOL_POINTERS = 0x00000007,
|
||||
S_SYMBOL_STUBS = 0x00000008,
|
||||
|
||||
// Other bits in section.flags
|
||||
S_ATTR_PURE_INSTRUCTIONS = 0x80000000,
|
||||
S_ATTR_SOME_INSTRUCTIONS = 0x00000400
|
||||
};
|
||||
|
||||
|
||||
// section record for 32-bit architectures
|
||||
struct section {
|
||||
char sectname[16];
|
||||
char segname[16];
|
||||
uint32_t addr;
|
||||
uint32_t size;
|
||||
uint32_t offset;
|
||||
uint32_t align;
|
||||
uint32_t reloff;
|
||||
uint32_t nreloc;
|
||||
uint32_t flags;
|
||||
uint32_t reserved1;
|
||||
uint32_t reserved2;
|
||||
};
|
||||
|
||||
// section record for 64-bit architectures
|
||||
struct section_64 {
|
||||
char sectname[16];
|
||||
char segname[16];
|
||||
|
@ -115,20 +203,13 @@ struct section_64 {
|
|||
uint32_t reserved3;
|
||||
};
|
||||
|
||||
enum {
|
||||
LC_SEGMENT_64 = 0x19
|
||||
};
|
||||
|
||||
enum {
|
||||
VM_PROT_NONE = 0x0,
|
||||
VM_PROT_READ = 0x1,
|
||||
VM_PROT_WRITE = 0x2,
|
||||
VM_PROT_EXECUTE = 0x4,
|
||||
};
|
||||
|
||||
|
||||
|
||||
class segment_command_64 : public load_command {
|
||||
//
|
||||
// A segment load command has a fixed set of fields followed by an 'nsect'
|
||||
// array of section records. The in-memory object uses a pointer to
|
||||
// a dynamically allocated array of sections.
|
||||
//
|
||||
class segment_command : public load_command {
|
||||
public:
|
||||
char segname[16];
|
||||
uint64_t vmaddr;
|
||||
|
@ -139,81 +220,111 @@ public:
|
|||
uint32_t initprot;
|
||||
uint32_t nsects;
|
||||
uint32_t flags;
|
||||
section_64 sections[1];
|
||||
section_64 *sections;
|
||||
|
||||
// The segment_command_64 load commands has a nsect trailing
|
||||
// section_64 records appended to the end.
|
||||
static segment_command_64* make(unsigned sectCount) {
|
||||
// Compute size in portable way. Can't use offsetof() in non-POD class.
|
||||
// Can't use zero size sections[] array above.
|
||||
// So, since sizeof() already includes one section_64, subtract it off.
|
||||
unsigned size = sizeof(segment_command_64)
|
||||
+ ((int)sectCount -1) * sizeof(section_64);
|
||||
segment_command_64* result = reinterpret_cast<segment_command_64*>
|
||||
(::calloc(1, size));
|
||||
result->cmd = LC_SEGMENT_64;
|
||||
result->cmdsize = size;
|
||||
result->nsects = sectCount;
|
||||
return result;
|
||||
segment_command(unsigned sectCount, bool is64)
|
||||
: load_command((is64 ? LC_SEGMENT_64 : LC_SEGMENT),
|
||||
(is64 ? (72 + sectCount*80) : (56 + sectCount*68)),
|
||||
is64),
|
||||
vmaddr(0), vmsize(0), fileoff(0), filesize(0),
|
||||
maxprot(0), initprot(0), nsects(sectCount), flags(0) {
|
||||
sections = new section_64[sectCount];
|
||||
this->nsects = sectCount;
|
||||
}
|
||||
|
||||
~segment_command() {
|
||||
delete sections;
|
||||
}
|
||||
|
||||
void copyTo(uint8_t *to, bool swap) {
|
||||
if ( swap ) {
|
||||
assert(0 && "non-native endianness not supported yet");
|
||||
}
|
||||
else {
|
||||
if( is64() ) {
|
||||
// in-memory matches on-disk, so copy segment fields followed by sections
|
||||
::memcpy(to, (uint8_t*)&cmd, 72);
|
||||
if ( nsects != 0 )
|
||||
::memcpy(&to[72], sections, sizeof(section_64)*nsects);
|
||||
}
|
||||
else {
|
||||
// on-disk is 32-bit struct, so copy each field
|
||||
::memcpy(to, (uint8_t*)&cmd, 24);
|
||||
copy32(to, 24, vmaddr);
|
||||
copy32(to, 28, vmsize);
|
||||
copy32(to, 32, fileoff);
|
||||
copy32(to, 36, filesize);
|
||||
copy32(to, 40, maxprot);
|
||||
copy32(to, 44, initprot);
|
||||
copy32(to, 48, nsects);
|
||||
copy32(to, 52, flags);
|
||||
for(uint32_t i=0; i < nsects; ++i) {
|
||||
unsigned off = 56+i*68;
|
||||
::memcpy(&to[off], sections[i].sectname, 32);
|
||||
copy32(to, off+32, sections[i].addr);
|
||||
copy32(to, off+36, sections[i].size);
|
||||
copy32(to, off+40, sections[i].offset);
|
||||
copy32(to, off+44, sections[i].align);
|
||||
copy32(to, off+48, sections[i].reloff);
|
||||
copy32(to, off+52, sections[i].nreloc);
|
||||
copy32(to, off+56, sections[i].flags);
|
||||
copy32(to, off+60, sections[i].reserved1);
|
||||
copy32(to, off+64, sections[i].reserved2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void copy32(uint8_t *to, unsigned offset, uint64_t value) {
|
||||
uint32_t value32 = value; // FIXME: range check
|
||||
::memcpy(&to[offset], &value32, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
bool is64() {
|
||||
return (cmd == LC_SEGMENT_64);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
enum {
|
||||
LC_LOAD_DYLINKER = 0xe
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// The dylinker_command contains the path to the dynamic loader to use
|
||||
// with the program (e.g. "/usr/lib/dyld"). So, it is variable length.
|
||||
// But load commands must be pointer size aligned.
|
||||
//
|
||||
//
|
||||
class dylinker_command : public load_command {
|
||||
public:
|
||||
uint32_t name_offset;
|
||||
char name[1];
|
||||
|
||||
static dylinker_command* make(const char* path) {
|
||||
unsigned size = (sizeof(dylinker_command) + strlen(path) + 7) & (-8);
|
||||
dylinker_command* result = reinterpret_cast<dylinker_command*>
|
||||
(::calloc(1, size));
|
||||
result->cmd = LC_LOAD_DYLINKER;
|
||||
result->cmdsize = size;
|
||||
result->name_offset = 12;
|
||||
strcpy(result->name, path);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
enum {
|
||||
N_UNDF = 0x00,
|
||||
N_EXT = 0x01,
|
||||
N_PEXT = 0x10,
|
||||
N_SECT = 0x0e
|
||||
};
|
||||
|
||||
class nlist_64 {
|
||||
private:
|
||||
StringRef _name;
|
||||
public:
|
||||
uint32_t n_strx;
|
||||
uint8_t n_type;
|
||||
uint8_t n_sect;
|
||||
uint16_t n_desc;
|
||||
uint64_t n_value;
|
||||
|
||||
void copyTo(uint8_t* to, bool swap=false) {
|
||||
::memcpy(to, (uint8_t*)&n_strx, 16);
|
||||
dylinker_command(StringRef path, bool is64)
|
||||
: load_command(LC_LOAD_DYLINKER,12 + path.size(), is64, true),
|
||||
name_offset(12), _name(path) {
|
||||
}
|
||||
|
||||
virtual void copyTo(uint8_t *to, bool swap=false) {
|
||||
if ( swap ) {
|
||||
assert(0 && "non-native endianness not supported yet");
|
||||
}
|
||||
else {
|
||||
// in-memory matches on-disk, so copy first fields followed by path
|
||||
::memcpy(to, (uint8_t*)&cmd, 12);
|
||||
::memcpy(&to[12], _name.data(), _name.size());
|
||||
::bzero(&to[12+_name.size()], cmdsize-(12+_name.size()));
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
enum {
|
||||
LC_SYMTAB = 0x2
|
||||
};
|
||||
|
||||
//
|
||||
// The symtab_command just holds the offset to the array of nlist structs
|
||||
// and the offsets to the string pool for all symbol names.
|
||||
//
|
||||
class symtab_command : public load_command {
|
||||
public:
|
||||
uint32_t symoff;
|
||||
|
@ -221,40 +332,89 @@ public:
|
|||
uint32_t stroff;
|
||||
uint32_t strsize;
|
||||
|
||||
static symtab_command* make() {
|
||||
unsigned size = sizeof(symtab_command);
|
||||
symtab_command* result = reinterpret_cast<symtab_command*>
|
||||
(::calloc(1, size));
|
||||
result->cmd = LC_SYMTAB;
|
||||
result->cmdsize = size;
|
||||
return result;
|
||||
symtab_command(bool is64)
|
||||
: load_command(LC_SYMTAB, 24, is64),
|
||||
symoff(0), nsyms(0), stroff(0), strsize(0) {
|
||||
}
|
||||
|
||||
virtual void copyTo(uint8_t *to, bool swap=false) {
|
||||
if ( swap ) {
|
||||
assert(0 && "non-native endianness not supported yet");
|
||||
}
|
||||
else {
|
||||
// in-memory matches on-disk, so copy fields
|
||||
::memcpy(to, (uint8_t*)&cmd, 24);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
enum {
|
||||
LC_MAIN = 0x80000028
|
||||
};
|
||||
|
||||
//
|
||||
// The entry_point_command load command holds the offset to the function
|
||||
// _main in a dynamic executable.
|
||||
//
|
||||
class entry_point_command : public load_command {
|
||||
public:
|
||||
uint64_t entryoff; /* file (__TEXT) offset of main() */
|
||||
uint64_t stacksize;/* if not zero, initial stack size */
|
||||
uint64_t entryoff;
|
||||
uint64_t stacksize;
|
||||
|
||||
static entry_point_command* make() {
|
||||
unsigned size = sizeof(entry_point_command);
|
||||
entry_point_command* result = reinterpret_cast<entry_point_command*>
|
||||
(::calloc(1, size));
|
||||
result->cmd = LC_MAIN;
|
||||
result->cmdsize = size;
|
||||
return result;
|
||||
entry_point_command(bool is64)
|
||||
: load_command(LC_MAIN, 24, is64), entryoff(0), stacksize(0) {
|
||||
}
|
||||
|
||||
virtual void copyTo(uint8_t *to, bool swap=false) {
|
||||
if ( swap ) {
|
||||
assert(0 && "non-native endianness not supported yet");
|
||||
}
|
||||
else {
|
||||
// in-memory matches on-disk, so copy fields
|
||||
::memcpy(to, (uint8_t*)&cmd, 24);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
enum {
|
||||
LC_DYLD_INFO_ONLY = 0x80000022
|
||||
|
||||
|
||||
//
|
||||
// The dylib_command load command holds the name/path of a dynamic shared
|
||||
// library which this mach-o image depends on.
|
||||
//
|
||||
struct dylib_command : public load_command {
|
||||
uint32_t name_offset;
|
||||
uint32_t timestamp;
|
||||
uint32_t current_version;
|
||||
uint32_t compatibility_version;
|
||||
private:
|
||||
StringRef _loadPath;
|
||||
public:
|
||||
|
||||
dylib_command(StringRef path, bool is64)
|
||||
: load_command(LC_LOAD_DYLIB, 24 + path.size(), is64, true),
|
||||
name_offset(24), timestamp(0),
|
||||
current_version(0x10000), compatibility_version(0x10000),
|
||||
_loadPath(path) {
|
||||
}
|
||||
|
||||
virtual void copyTo(uint8_t *to, bool swap=false) {
|
||||
if ( swap ) {
|
||||
assert(0 && "non-native endianness not supported yet");
|
||||
}
|
||||
else {
|
||||
// in-memory matches on-disk, so copy first fields followed by path
|
||||
::memcpy(to, (uint8_t*)&cmd, 24);
|
||||
::memcpy(&to[24], _loadPath.data(), _loadPath.size());
|
||||
::bzero(&to[12+_loadPath.size()], cmdsize-(12+_loadPath.size()));
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// The dyld_info_command load command holds the offsets to various tables
|
||||
// of information needed by dyld to prepare the image for execution.
|
||||
//
|
||||
struct dyld_info_command : public load_command {
|
||||
uint32_t rebase_off;
|
||||
uint32_t rebase_size;
|
||||
|
@ -267,46 +427,25 @@ struct dyld_info_command : public load_command {
|
|||
uint32_t export_off;
|
||||
uint32_t export_size;
|
||||
|
||||
static dyld_info_command* make() {
|
||||
unsigned size = sizeof(dyld_info_command);
|
||||
dyld_info_command* result = reinterpret_cast<dyld_info_command*>
|
||||
(::calloc(1, size));
|
||||
result->cmd = LC_DYLD_INFO_ONLY;
|
||||
result->cmdsize = size;
|
||||
return result;
|
||||
dyld_info_command(bool is64)
|
||||
: load_command(LC_DYLD_INFO_ONLY, 48, is64),
|
||||
rebase_off(0), rebase_size(0),
|
||||
bind_off(0), bind_size(0), weak_bind_off(0), weak_bind_size(0),
|
||||
lazy_bind_off(0), lazy_bind_size(0), export_off(0), export_size(0) {
|
||||
}
|
||||
|
||||
virtual void copyTo(uint8_t *to, bool swap=false) {
|
||||
if ( swap ) {
|
||||
assert(0 && "non-native endianness not supported yet");
|
||||
}
|
||||
else {
|
||||
// in-memory matches on-disk, so copy fields
|
||||
::memcpy(to, (uint8_t*)&cmd, 48);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
enum {
|
||||
LC_LOAD_DYLIB = 0xC
|
||||
};
|
||||
|
||||
|
||||
struct dylib_command : public load_command {
|
||||
uint32_t name_offset;
|
||||
uint32_t timestamp;
|
||||
uint32_t current_version;
|
||||
uint32_t compatibility_version;
|
||||
char name[1];
|
||||
|
||||
static dylib_command* make(const char* path) {
|
||||
unsigned size = (sizeof(dylib_command) + strlen(path) + 7) & (-8);
|
||||
dylib_command* result = reinterpret_cast<dylib_command*>
|
||||
(::calloc(1, size));
|
||||
result->cmd = LC_LOAD_DYLIB;
|
||||
result->cmdsize = size;
|
||||
result->name_offset = 24;
|
||||
result->name_offset = 24;
|
||||
result->timestamp = 0;
|
||||
result->current_version = 0x10000;
|
||||
result->compatibility_version = 0x10000;
|
||||
strcpy(result->name, path);
|
||||
return result;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
enum {
|
||||
BIND_TYPE_POINTER = 1,
|
||||
BIND_TYPE_TEXT_ABSOLUTE32 = 2,
|
||||
|
@ -345,6 +484,48 @@ enum {
|
|||
|
||||
|
||||
|
||||
enum {
|
||||
N_UNDF = 0x00,
|
||||
N_EXT = 0x01,
|
||||
N_PEXT = 0x10,
|
||||
N_SECT = 0x0e
|
||||
};
|
||||
|
||||
class nlist {
|
||||
public:
|
||||
uint32_t n_strx;
|
||||
uint8_t n_type;
|
||||
uint8_t n_sect;
|
||||
uint16_t n_desc;
|
||||
uint64_t n_value;
|
||||
|
||||
static unsigned size(bool is64) {
|
||||
return (is64 ? 16 : 12);
|
||||
}
|
||||
|
||||
void copyTo(uint8_t *to, bool is64, bool swap=false) {
|
||||
if ( swap ) {
|
||||
assert(0 && "non-native endianness not supported yet");
|
||||
}
|
||||
else {
|
||||
if ( is64 ) {
|
||||
// in-memory matches on-disk, so just copy whole struct
|
||||
::memcpy(to, (uint8_t*)&n_strx, 16);
|
||||
}
|
||||
else {
|
||||
// on-disk uses 32-bit n_value, so special case n_value
|
||||
::memcpy(to, (uint8_t*)&n_strx, 8);
|
||||
uint32_t value32 = n_value; // FIXME: range check
|
||||
::memcpy(&to[8], &value32, sizeof(uint32_t));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace mach_o
|
||||
} // namespace lld
|
||||
|
||||
|
|
|
@ -73,6 +73,7 @@ namespace mach_o {
|
|||
//
|
||||
class Chunk {
|
||||
public:
|
||||
virtual ~Chunk() { }
|
||||
virtual StringRef segmentName() const = 0;
|
||||
virtual bool occupiesNoDiskSpace();
|
||||
virtual void write(uint8_t *fileBuffer) = 0;
|
||||
|
@ -158,8 +159,9 @@ public:
|
|||
uint64_t loadCommandsSize();
|
||||
|
||||
private:
|
||||
uint32_t filetype(WriterOptionsMachO::OutputKind kind);
|
||||
|
||||
uint32_t filetype(WriterOptionsMachO::OutputKind kind);
|
||||
uint32_t magic(uint32_t cpuType);
|
||||
|
||||
mach_header _mh;
|
||||
};
|
||||
|
||||
|
@ -187,20 +189,21 @@ private:
|
|||
|
||||
void addLoadCommand(load_command* lc);
|
||||
void setMachOSection(SectionChunk *chunk,
|
||||
segment_command_64 *seg, uint32_t index);
|
||||
segment_command *seg, uint32_t index);
|
||||
uint32_t permissionsFromSections(
|
||||
const SmallVector<SectionChunk*,16> &);
|
||||
bool use64BitMachO() const;
|
||||
|
||||
struct ChunkSegInfo {
|
||||
SectionChunk* chunk;
|
||||
segment_command_64* segment;
|
||||
section_64* section;
|
||||
SectionChunk *chunk;
|
||||
segment_command *segment;
|
||||
section_64 *section;
|
||||
};
|
||||
|
||||
MachHeaderChunk &_mh;
|
||||
const WriterOptionsMachO &_options;
|
||||
class MachOWriter &_writer;
|
||||
segment_command_64 *_linkEditSegment;
|
||||
segment_command *_linkEditSegment;
|
||||
symtab_command *_symbolTableLoadCommand;
|
||||
entry_point_command *_entryPointLoadCommand;
|
||||
dyld_info_command *_dyldInfoLoadCommand;
|
||||
|
@ -297,7 +300,7 @@ private:
|
|||
//
|
||||
class SymbolTableChunk : public LinkEditChunk {
|
||||
public:
|
||||
SymbolTableChunk(class SymbolStringsChunk&);
|
||||
SymbolTableChunk(class SymbolStringsChunk&, MachOWriter&);
|
||||
virtual void write(uint8_t *fileBuffer);
|
||||
virtual void computeSize(const lld::File &file,
|
||||
const std::vector<SectionChunk*>&);
|
||||
|
@ -307,10 +310,11 @@ public:
|
|||
private:
|
||||
uint8_t nType(const DefinedAtom*);
|
||||
|
||||
SymbolStringsChunk &_stringsChunk;
|
||||
std::vector<nlist_64> _globalDefinedsymbols;
|
||||
std::vector<nlist_64> _localDefinedsymbols;
|
||||
std::vector<nlist_64> _undefinedsymbols;
|
||||
MachOWriter &_writer;
|
||||
SymbolStringsChunk &_stringsChunk;
|
||||
std::vector<nlist> _globalDefinedsymbols;
|
||||
std::vector<nlist> _localDefinedsymbols;
|
||||
std::vector<nlist> _undefinedsymbols;
|
||||
};
|
||||
|
||||
|
||||
|
@ -350,7 +354,8 @@ public:
|
|||
uint64_t *segStartAddr, uint64_t *segEndAddr);
|
||||
|
||||
const std::vector<Chunk*> chunks() { return _chunks; }
|
||||
|
||||
bool use64BitMachO() const;
|
||||
|
||||
private:
|
||||
friend class LoadCommandsChunk;
|
||||
friend class LazyBindingInfoChunk;
|
||||
|
@ -612,7 +617,7 @@ void SectionChunk::applyFixup(Reference::Kind kind, uint64_t addend,
|
|||
MachHeaderChunk::MachHeaderChunk(const WriterOptionsMachO &options,
|
||||
const File &file) {
|
||||
// Set up mach_header based on options
|
||||
_mh.magic = MAGIC_64;
|
||||
_mh.magic = this->magic(options.cpuType());
|
||||
_mh.cputype = options.cpuType();
|
||||
_mh.cpusubtype = options.cpuSubtype();
|
||||
_mh.filetype = this->filetype(options.outputKind());
|
||||
|
@ -645,6 +650,18 @@ uint64_t MachHeaderChunk::loadCommandsSize() {
|
|||
return _mh.sizeofcmds;
|
||||
}
|
||||
|
||||
uint32_t MachHeaderChunk::magic(uint32_t cpuType) {
|
||||
switch ( cpuType ) {
|
||||
case CPU_TYPE_ARM:
|
||||
case CPU_TYPE_I386:
|
||||
return MH_MAGIC;
|
||||
case CPU_TYPE_X86_64:
|
||||
return MH_MAGIC_64;
|
||||
}
|
||||
assert(0 && "file cpu type not supported");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t MachHeaderChunk::filetype(WriterOptionsMachO::OutputKind kind) {
|
||||
switch ( kind ) {
|
||||
case WriterOptionsMachO::outputDynamicExecutable:
|
||||
|
@ -692,7 +709,7 @@ const char* LoadCommandsChunk::info() {
|
|||
}
|
||||
|
||||
void LoadCommandsChunk::setMachOSection(SectionChunk *chunk,
|
||||
segment_command_64 *seg, uint32_t index) {
|
||||
segment_command *seg, uint32_t index) {
|
||||
for (ChunkSegInfo &entry : _sectionInfo) {
|
||||
if ( entry.chunk == chunk ) {
|
||||
entry.section = &(seg->sections[index]);
|
||||
|
@ -713,10 +730,12 @@ uint32_t LoadCommandsChunk::permissionsFromSections(
|
|||
}
|
||||
|
||||
void LoadCommandsChunk::computeSize(const lld::File &file) {
|
||||
const bool is64 = _writer.use64BitMachO();
|
||||
// Main executables have a __PAGEZERO segment.
|
||||
uint64_t pageZeroSize = _options.pageZeroSize();
|
||||
if ( pageZeroSize != 0 ) {
|
||||
segment_command_64* pzSegCmd = segment_command_64::make(0);
|
||||
assert(is64 || (pageZeroSize < 0xFFFFFFFF));
|
||||
segment_command* pzSegCmd = new segment_command(0, is64);
|
||||
strcpy(pzSegCmd->segname, "__PAGEZERO");
|
||||
pzSegCmd->vmaddr = 0;
|
||||
pzSegCmd->vmsize = pageZeroSize;
|
||||
|
@ -735,7 +754,7 @@ void LoadCommandsChunk::computeSize(const lld::File &file) {
|
|||
StringRef entryName = entry.chunk->segmentName();
|
||||
if ( !lastSegName.equals(entryName) ) {
|
||||
// Start of new segment, so create load command for all previous sections.
|
||||
segment_command_64* segCmd = segment_command_64::make(sections.size());
|
||||
segment_command* segCmd = new segment_command(sections.size(), is64);
|
||||
strncpy(segCmd->segname, lastSegName.data(), 16);
|
||||
segCmd->initprot = this->permissionsFromSections(sections);
|
||||
segCmd->maxprot = VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE;
|
||||
|
@ -752,7 +771,7 @@ void LoadCommandsChunk::computeSize(const lld::File &file) {
|
|||
sections.push_back(entry.chunk);
|
||||
}
|
||||
// Add last segment load command.
|
||||
segment_command_64* segCmd = segment_command_64::make(sections.size());
|
||||
segment_command* segCmd = new segment_command(sections.size(), is64);
|
||||
strncpy(segCmd->segname, lastSegName.data(), 16);
|
||||
segCmd->initprot = this->permissionsFromSections(sections);;
|
||||
segCmd->maxprot = VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE;
|
||||
|
@ -764,14 +783,14 @@ void LoadCommandsChunk::computeSize(const lld::File &file) {
|
|||
}
|
||||
|
||||
// Add LINKEDIT segment load command
|
||||
_linkEditSegment = segment_command_64::make(0);
|
||||
_linkEditSegment = new segment_command(0, is64);
|
||||
strcpy(_linkEditSegment->segname, "__LINKEDIT");
|
||||
_linkEditSegment->initprot = VM_PROT_READ;
|
||||
_linkEditSegment->maxprot = VM_PROT_READ;
|
||||
this->addLoadCommand(_linkEditSegment);
|
||||
|
||||
// Add dyld load command.
|
||||
this->addLoadCommand(dylinker_command::make("/usr/lib/dyld"));
|
||||
this->addLoadCommand(new dylinker_command("/usr/lib/dyld", is64));
|
||||
|
||||
// Add dylib load commands.
|
||||
llvm::StringMap<uint32_t> dylibNamesToOrdinal;
|
||||
|
@ -784,20 +803,20 @@ void LoadCommandsChunk::computeSize(const lld::File &file) {
|
|||
}
|
||||
for (llvm::StringMap<uint32_t>::iterator it=dylibNamesToOrdinal.begin(),
|
||||
end=dylibNamesToOrdinal.end(); it != end; ++it) {
|
||||
this->addLoadCommand(dylib_command::make(it->first().data()));
|
||||
this->addLoadCommand(new dylib_command(it->first(), is64));
|
||||
}
|
||||
|
||||
// Add symbol table load command
|
||||
_symbolTableLoadCommand = symtab_command::make();
|
||||
_symbolTableLoadCommand = new symtab_command(is64);
|
||||
this->addLoadCommand(_symbolTableLoadCommand);
|
||||
|
||||
// Add dyld info load command
|
||||
_dyldInfoLoadCommand = dyld_info_command::make();
|
||||
_dyldInfoLoadCommand = new dyld_info_command(is64);
|
||||
this->addLoadCommand(_dyldInfoLoadCommand);
|
||||
|
||||
// Add entry point load command to main executables
|
||||
if (_options.outputKind() == WriterOptionsMachO::outputDynamicExecutable) {
|
||||
_entryPointLoadCommand = entry_point_command::make();
|
||||
_entryPointLoadCommand = new entry_point_command(is64);
|
||||
this->addLoadCommand(_entryPointLoadCommand);
|
||||
}
|
||||
|
||||
|
@ -808,7 +827,7 @@ void LoadCommandsChunk::computeSize(const lld::File &file) {
|
|||
|
||||
void LoadCommandsChunk::updateLoadCommandContent(const lld::File &file) {
|
||||
// Update segment/section information in segment load commands
|
||||
segment_command_64 *lastSegment = nullptr;
|
||||
segment_command *lastSegment = nullptr;
|
||||
for (ChunkSegInfo &entry : _sectionInfo) {
|
||||
// Set section info.
|
||||
::strncpy(entry.section->sectname, entry.chunk->sectionName().data(), 16);
|
||||
|
@ -1158,23 +1177,25 @@ void LazyBindingInfoChunk::computeSize(const lld::File &file,
|
|||
// SymbolTableChunk
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
SymbolTableChunk::SymbolTableChunk(SymbolStringsChunk& str)
|
||||
: _stringsChunk(str) {
|
||||
SymbolTableChunk::SymbolTableChunk(SymbolStringsChunk &str, MachOWriter &wrtr)
|
||||
: _writer(wrtr), _stringsChunk(str) {
|
||||
}
|
||||
|
||||
void SymbolTableChunk::write(uint8_t *chunkBuffer) {
|
||||
const bool is64 = _writer.use64BitMachO();
|
||||
const unsigned nlistSize = nlist::size(is64);
|
||||
uint8_t *p = chunkBuffer;
|
||||
for ( nlist_64 &sym : _globalDefinedsymbols ) {
|
||||
sym.copyTo(p);
|
||||
p += sizeof(nlist_64);
|
||||
for ( nlist &sym : _globalDefinedsymbols ) {
|
||||
sym.copyTo(p, is64);
|
||||
p += nlistSize;
|
||||
}
|
||||
for ( nlist_64 &sym : _localDefinedsymbols ) {
|
||||
sym.copyTo(p);
|
||||
p += sizeof(nlist_64);
|
||||
for ( nlist &sym : _localDefinedsymbols ) {
|
||||
sym.copyTo(p, is64);
|
||||
p += nlistSize;
|
||||
}
|
||||
for ( nlist_64 &sym : _undefinedsymbols ) {
|
||||
sym.copyTo(p);
|
||||
p += sizeof(nlist_64);
|
||||
for ( nlist &sym : _undefinedsymbols ) {
|
||||
sym.copyTo(p, is64);
|
||||
p += nlistSize;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1212,7 +1233,7 @@ void SymbolTableChunk::computeSize(const lld::File &file,
|
|||
if ( info.atom->name().empty() )
|
||||
continue;
|
||||
uint64_t atomAddress = chunk->address() + info.offsetInSection;
|
||||
nlist_64 sym;
|
||||
nlist sym;
|
||||
sym.n_strx = _stringsChunk.stringIndex(info.atom->name());
|
||||
sym.n_type = this->nType(info.atom);
|
||||
sym.n_sect = sectionIndex;
|
||||
|
@ -1227,7 +1248,7 @@ void SymbolTableChunk::computeSize(const lld::File &file,
|
|||
|
||||
// Add symbols for undefined/sharedLibrary symbols
|
||||
for (const SharedLibraryAtom* atom : file.sharedLibrary() ) {
|
||||
nlist_64 sym;
|
||||
nlist sym;
|
||||
sym.n_strx = _stringsChunk.stringIndex(atom->name());
|
||||
sym.n_type = N_UNDF;
|
||||
sym.n_sect = 0;
|
||||
|
@ -1235,7 +1256,7 @@ void SymbolTableChunk::computeSize(const lld::File &file,
|
|||
_undefinedsymbols.push_back(sym);
|
||||
}
|
||||
|
||||
_size = sizeof(nlist_64) * this->count();
|
||||
_size = nlist::size(_writer.use64BitMachO()) * this->count();
|
||||
}
|
||||
|
||||
|
||||
|
@ -1350,7 +1371,7 @@ void MachOWriter::createChunks(const lld::File &file) {
|
|||
_bindingInfo = new BindingInfoChunk(*this);
|
||||
_lazyBindingInfo = new LazyBindingInfoChunk(*this);
|
||||
_stringsChunk = new SymbolStringsChunk();
|
||||
_symbolTableChunk = new SymbolTableChunk(*_stringsChunk);
|
||||
_symbolTableChunk = new SymbolTableChunk(*_stringsChunk, *this);
|
||||
this->addLinkEditChunk(_bindingInfo);
|
||||
this->addLinkEditChunk(_lazyBindingInfo);
|
||||
this->addLinkEditChunk(_symbolTableChunk);
|
||||
|
@ -1459,6 +1480,18 @@ void MachOWriter::findSegment(StringRef segmentName, uint32_t *segIndex,
|
|||
}
|
||||
}
|
||||
|
||||
bool MachOWriter::use64BitMachO() const {
|
||||
switch ( _options.cpuType() ) {
|
||||
case CPU_TYPE_ARM:
|
||||
case CPU_TYPE_I386:
|
||||
return false;
|
||||
case CPU_TYPE_X86_64:
|
||||
return true;
|
||||
}
|
||||
assert(0 && "unknown cpu type");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Creates a mach-o final linked image from the given atom graph and writes
|
||||
|
@ -1519,7 +1552,7 @@ WriterOptionsMachO::WriterOptionsMachO()
|
|||
: _outputkind(outputDynamicExecutable),
|
||||
_archName("x86_64"),
|
||||
_architecture(arch_x86_64),
|
||||
_pageZeroSize(0x10000000),
|
||||
_pageZeroSize(0x100000000),
|
||||
_cpuType(mach_o::CPU_TYPE_X86_64),
|
||||
_cpuSubtype(mach_o::CPU_SUBTYPE_X86_64_ALL),
|
||||
_noTextRelocations(true) {
|
||||
|
|
Loading…
Reference in New Issue