abstract load commands to that WriterMachO can write 64-bit and 32-bit mach-o files

llvm-svn: 158133
This commit is contained in:
Nick Kledzik 2012-06-07 01:45:39 +00:00
parent aefcedae65
commit 40b8655082
2 changed files with 401 additions and 187 deletions

View File

@ -24,29 +24,23 @@
namespace lld { namespace lld {
namespace mach_o { 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 { enum {
MH_MAGIC = 0xfeedface, MH_MAGIC = 0xfeedface,
MAGIC_64 = 0xfeedfacf MH_MAGIC_64 = 0xfeedfacf
}; };
enum { enum {
CPU_TYPE_ARM = 0x0000000C,
CPU_TYPE_I386 = 0x00000007, CPU_TYPE_I386 = 0x00000007,
CPU_TYPE_X86_64 = 0x01000007 CPU_TYPE_X86_64 = 0x01000007
}; };
enum { enum {
CPU_SUBTYPE_X86_ALL = 0x00000003, CPU_SUBTYPE_X86_ALL = 0x00000003,
CPU_SUBTYPE_X86_64_ALL = 0x00000003 CPU_SUBTYPE_X86_64_ALL = 0x00000003,
CPU_SUBTYPE_ARM_V6 = 0x00000006,
CPU_SUBTYPE_ARM_V7 = 0x00000009
}; };
enum { 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 { class mach_header {
public: public:
uint32_t magic; uint32_t magic;
@ -75,19 +73,91 @@ public:
return (magic == 0xfeedfacf) ? 32 : 28; 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()); ::memcpy(to, (char*)&magic, this->size());
} }
void recordLoadCommand(const class load_command* lc) { void recordLoadCommand(const class load_command *lc);
++ncmds; };
sizeofcmds += lc->cmdsize;
//
// 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 { 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, SECTION_TYPE = 0x000000FF,
S_REGULAR = 0x00000000, S_REGULAR = 0x00000000,
S_ZEROFILL = 0x00000001, S_ZEROFILL = 0x00000001,
@ -96,10 +166,28 @@ enum {
S_LAZY_SYMBOL_POINTERS = 0x00000007, S_LAZY_SYMBOL_POINTERS = 0x00000007,
S_SYMBOL_STUBS = 0x00000008, S_SYMBOL_STUBS = 0x00000008,
// Other bits in section.flags
S_ATTR_PURE_INSTRUCTIONS = 0x80000000, S_ATTR_PURE_INSTRUCTIONS = 0x80000000,
S_ATTR_SOME_INSTRUCTIONS = 0x00000400 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 { struct section_64 {
char sectname[16]; char sectname[16];
char segname[16]; char segname[16];
@ -115,20 +203,13 @@ struct section_64 {
uint32_t reserved3; uint32_t reserved3;
}; };
enum {
LC_SEGMENT_64 = 0x19
};
enum { //
VM_PROT_NONE = 0x0, // A segment load command has a fixed set of fields followed by an 'nsect'
VM_PROT_READ = 0x1, // array of section records. The in-memory object uses a pointer to
VM_PROT_WRITE = 0x2, // a dynamically allocated array of sections.
VM_PROT_EXECUTE = 0x4, //
}; class segment_command : public load_command {
class segment_command_64 : public load_command {
public: public:
char segname[16]; char segname[16];
uint64_t vmaddr; uint64_t vmaddr;
@ -139,81 +220,111 @@ public:
uint32_t initprot; uint32_t initprot;
uint32_t nsects; uint32_t nsects;
uint32_t flags; uint32_t flags;
section_64 sections[1]; section_64 *sections;
// The segment_command_64 load commands has a nsect trailing segment_command(unsigned sectCount, bool is64)
// section_64 records appended to the end. : load_command((is64 ? LC_SEGMENT_64 : LC_SEGMENT),
static segment_command_64* make(unsigned sectCount) { (is64 ? (72 + sectCount*80) : (56 + sectCount*68)),
// Compute size in portable way. Can't use offsetof() in non-POD class. is64),
// Can't use zero size sections[] array above. vmaddr(0), vmsize(0), fileoff(0), filesize(0),
// So, since sizeof() already includes one section_64, subtract it off. maxprot(0), initprot(0), nsects(sectCount), flags(0) {
unsigned size = sizeof(segment_command_64) sections = new section_64[sectCount];
+ ((int)sectCount -1) * sizeof(section_64); this->nsects = sectCount;
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() {
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 { class dylinker_command : public load_command {
public: public:
uint32_t name_offset; uint32_t name_offset;
char name[1]; private:
StringRef _name;
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 {
public: public:
uint32_t n_strx; dylinker_command(StringRef path, bool is64)
uint8_t n_type; : load_command(LC_LOAD_DYLINKER,12 + path.size(), is64, true),
uint8_t n_sect; name_offset(12), _name(path) {
uint16_t n_desc;
uint64_t n_value;
void copyTo(uint8_t* to, bool swap=false) {
::memcpy(to, (uint8_t*)&n_strx, 16);
} }
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 { class symtab_command : public load_command {
public: public:
uint32_t symoff; uint32_t symoff;
@ -221,40 +332,89 @@ public:
uint32_t stroff; uint32_t stroff;
uint32_t strsize; uint32_t strsize;
static symtab_command* make() { symtab_command(bool is64)
unsigned size = sizeof(symtab_command); : load_command(LC_SYMTAB, 24, is64),
symtab_command* result = reinterpret_cast<symtab_command*> symoff(0), nsyms(0), stroff(0), strsize(0) {
(::calloc(1, size));
result->cmd = LC_SYMTAB;
result->cmdsize = size;
return result;
} }
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 { class entry_point_command : public load_command {
public: public:
uint64_t entryoff; /* file (__TEXT) offset of main() */ uint64_t entryoff;
uint64_t stacksize;/* if not zero, initial stack size */ uint64_t stacksize;
static entry_point_command* make() { entry_point_command(bool is64)
unsigned size = sizeof(entry_point_command); : load_command(LC_MAIN, 24, is64), entryoff(0), stacksize(0) {
entry_point_command* result = reinterpret_cast<entry_point_command*> }
(::calloc(1, size));
result->cmd = LC_MAIN; virtual void copyTo(uint8_t *to, bool swap=false) {
result->cmdsize = size; if ( swap ) {
return result; 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 { struct dyld_info_command : public load_command {
uint32_t rebase_off; uint32_t rebase_off;
uint32_t rebase_size; uint32_t rebase_size;
@ -267,46 +427,25 @@ struct dyld_info_command : public load_command {
uint32_t export_off; uint32_t export_off;
uint32_t export_size; uint32_t export_size;
static dyld_info_command* make() { dyld_info_command(bool is64)
unsigned size = sizeof(dyld_info_command); : load_command(LC_DYLD_INFO_ONLY, 48, is64),
dyld_info_command* result = reinterpret_cast<dyld_info_command*> rebase_off(0), rebase_size(0),
(::calloc(1, size)); bind_off(0), bind_size(0), weak_bind_off(0), weak_bind_size(0),
result->cmd = LC_DYLD_INFO_ONLY; lazy_bind_off(0), lazy_bind_size(0), export_off(0), export_size(0) {
result->cmdsize = size; }
return result;
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 { enum {
BIND_TYPE_POINTER = 1, BIND_TYPE_POINTER = 1,
BIND_TYPE_TEXT_ABSOLUTE32 = 2, 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 mach_o
} // namespace lld } // namespace lld

View File

@ -73,6 +73,7 @@ namespace mach_o {
// //
class Chunk { class Chunk {
public: public:
virtual ~Chunk() { }
virtual StringRef segmentName() const = 0; virtual StringRef segmentName() const = 0;
virtual bool occupiesNoDiskSpace(); virtual bool occupiesNoDiskSpace();
virtual void write(uint8_t *fileBuffer) = 0; virtual void write(uint8_t *fileBuffer) = 0;
@ -158,8 +159,9 @@ public:
uint64_t loadCommandsSize(); uint64_t loadCommandsSize();
private: private:
uint32_t filetype(WriterOptionsMachO::OutputKind kind); uint32_t filetype(WriterOptionsMachO::OutputKind kind);
uint32_t magic(uint32_t cpuType);
mach_header _mh; mach_header _mh;
}; };
@ -187,20 +189,21 @@ private:
void addLoadCommand(load_command* lc); void addLoadCommand(load_command* lc);
void setMachOSection(SectionChunk *chunk, void setMachOSection(SectionChunk *chunk,
segment_command_64 *seg, uint32_t index); segment_command *seg, uint32_t index);
uint32_t permissionsFromSections( uint32_t permissionsFromSections(
const SmallVector<SectionChunk*,16> &); const SmallVector<SectionChunk*,16> &);
bool use64BitMachO() const;
struct ChunkSegInfo { struct ChunkSegInfo {
SectionChunk* chunk; SectionChunk *chunk;
segment_command_64* segment; segment_command *segment;
section_64* section; section_64 *section;
}; };
MachHeaderChunk &_mh; MachHeaderChunk &_mh;
const WriterOptionsMachO &_options; const WriterOptionsMachO &_options;
class MachOWriter &_writer; class MachOWriter &_writer;
segment_command_64 *_linkEditSegment; segment_command *_linkEditSegment;
symtab_command *_symbolTableLoadCommand; symtab_command *_symbolTableLoadCommand;
entry_point_command *_entryPointLoadCommand; entry_point_command *_entryPointLoadCommand;
dyld_info_command *_dyldInfoLoadCommand; dyld_info_command *_dyldInfoLoadCommand;
@ -297,7 +300,7 @@ private:
// //
class SymbolTableChunk : public LinkEditChunk { class SymbolTableChunk : public LinkEditChunk {
public: public:
SymbolTableChunk(class SymbolStringsChunk&); SymbolTableChunk(class SymbolStringsChunk&, MachOWriter&);
virtual void write(uint8_t *fileBuffer); virtual void write(uint8_t *fileBuffer);
virtual void computeSize(const lld::File &file, virtual void computeSize(const lld::File &file,
const std::vector<SectionChunk*>&); const std::vector<SectionChunk*>&);
@ -307,10 +310,11 @@ public:
private: private:
uint8_t nType(const DefinedAtom*); uint8_t nType(const DefinedAtom*);
SymbolStringsChunk &_stringsChunk; MachOWriter &_writer;
std::vector<nlist_64> _globalDefinedsymbols; SymbolStringsChunk &_stringsChunk;
std::vector<nlist_64> _localDefinedsymbols; std::vector<nlist> _globalDefinedsymbols;
std::vector<nlist_64> _undefinedsymbols; std::vector<nlist> _localDefinedsymbols;
std::vector<nlist> _undefinedsymbols;
}; };
@ -350,7 +354,8 @@ public:
uint64_t *segStartAddr, uint64_t *segEndAddr); uint64_t *segStartAddr, uint64_t *segEndAddr);
const std::vector<Chunk*> chunks() { return _chunks; } const std::vector<Chunk*> chunks() { return _chunks; }
bool use64BitMachO() const;
private: private:
friend class LoadCommandsChunk; friend class LoadCommandsChunk;
friend class LazyBindingInfoChunk; friend class LazyBindingInfoChunk;
@ -612,7 +617,7 @@ void SectionChunk::applyFixup(Reference::Kind kind, uint64_t addend,
MachHeaderChunk::MachHeaderChunk(const WriterOptionsMachO &options, MachHeaderChunk::MachHeaderChunk(const WriterOptionsMachO &options,
const File &file) { const File &file) {
// Set up mach_header based on options // Set up mach_header based on options
_mh.magic = MAGIC_64; _mh.magic = this->magic(options.cpuType());
_mh.cputype = options.cpuType(); _mh.cputype = options.cpuType();
_mh.cpusubtype = options.cpuSubtype(); _mh.cpusubtype = options.cpuSubtype();
_mh.filetype = this->filetype(options.outputKind()); _mh.filetype = this->filetype(options.outputKind());
@ -645,6 +650,18 @@ uint64_t MachHeaderChunk::loadCommandsSize() {
return _mh.sizeofcmds; 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) { uint32_t MachHeaderChunk::filetype(WriterOptionsMachO::OutputKind kind) {
switch ( kind ) { switch ( kind ) {
case WriterOptionsMachO::outputDynamicExecutable: case WriterOptionsMachO::outputDynamicExecutable:
@ -692,7 +709,7 @@ const char* LoadCommandsChunk::info() {
} }
void LoadCommandsChunk::setMachOSection(SectionChunk *chunk, void LoadCommandsChunk::setMachOSection(SectionChunk *chunk,
segment_command_64 *seg, uint32_t index) { segment_command *seg, uint32_t index) {
for (ChunkSegInfo &entry : _sectionInfo) { for (ChunkSegInfo &entry : _sectionInfo) {
if ( entry.chunk == chunk ) { if ( entry.chunk == chunk ) {
entry.section = &(seg->sections[index]); entry.section = &(seg->sections[index]);
@ -713,10 +730,12 @@ uint32_t LoadCommandsChunk::permissionsFromSections(
} }
void LoadCommandsChunk::computeSize(const lld::File &file) { void LoadCommandsChunk::computeSize(const lld::File &file) {
const bool is64 = _writer.use64BitMachO();
// Main executables have a __PAGEZERO segment. // Main executables have a __PAGEZERO segment.
uint64_t pageZeroSize = _options.pageZeroSize(); uint64_t pageZeroSize = _options.pageZeroSize();
if ( pageZeroSize != 0 ) { 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"); strcpy(pzSegCmd->segname, "__PAGEZERO");
pzSegCmd->vmaddr = 0; pzSegCmd->vmaddr = 0;
pzSegCmd->vmsize = pageZeroSize; pzSegCmd->vmsize = pageZeroSize;
@ -735,7 +754,7 @@ void LoadCommandsChunk::computeSize(const lld::File &file) {
StringRef entryName = entry.chunk->segmentName(); StringRef entryName = entry.chunk->segmentName();
if ( !lastSegName.equals(entryName) ) { if ( !lastSegName.equals(entryName) ) {
// Start of new segment, so create load command for all previous sections. // 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); strncpy(segCmd->segname, lastSegName.data(), 16);
segCmd->initprot = this->permissionsFromSections(sections); segCmd->initprot = this->permissionsFromSections(sections);
segCmd->maxprot = VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE; 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); sections.push_back(entry.chunk);
} }
// Add last segment load command. // 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); strncpy(segCmd->segname, lastSegName.data(), 16);
segCmd->initprot = this->permissionsFromSections(sections);; segCmd->initprot = this->permissionsFromSections(sections);;
segCmd->maxprot = VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE; 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 // Add LINKEDIT segment load command
_linkEditSegment = segment_command_64::make(0); _linkEditSegment = new segment_command(0, is64);
strcpy(_linkEditSegment->segname, "__LINKEDIT"); strcpy(_linkEditSegment->segname, "__LINKEDIT");
_linkEditSegment->initprot = VM_PROT_READ; _linkEditSegment->initprot = VM_PROT_READ;
_linkEditSegment->maxprot = VM_PROT_READ; _linkEditSegment->maxprot = VM_PROT_READ;
this->addLoadCommand(_linkEditSegment); this->addLoadCommand(_linkEditSegment);
// Add dyld load command. // 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. // Add dylib load commands.
llvm::StringMap<uint32_t> dylibNamesToOrdinal; 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(), for (llvm::StringMap<uint32_t>::iterator it=dylibNamesToOrdinal.begin(),
end=dylibNamesToOrdinal.end(); it != end; ++it) { 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 // Add symbol table load command
_symbolTableLoadCommand = symtab_command::make(); _symbolTableLoadCommand = new symtab_command(is64);
this->addLoadCommand(_symbolTableLoadCommand); this->addLoadCommand(_symbolTableLoadCommand);
// Add dyld info load command // Add dyld info load command
_dyldInfoLoadCommand = dyld_info_command::make(); _dyldInfoLoadCommand = new dyld_info_command(is64);
this->addLoadCommand(_dyldInfoLoadCommand); this->addLoadCommand(_dyldInfoLoadCommand);
// Add entry point load command to main executables // Add entry point load command to main executables
if (_options.outputKind() == WriterOptionsMachO::outputDynamicExecutable) { if (_options.outputKind() == WriterOptionsMachO::outputDynamicExecutable) {
_entryPointLoadCommand = entry_point_command::make(); _entryPointLoadCommand = new entry_point_command(is64);
this->addLoadCommand(_entryPointLoadCommand); this->addLoadCommand(_entryPointLoadCommand);
} }
@ -808,7 +827,7 @@ void LoadCommandsChunk::computeSize(const lld::File &file) {
void LoadCommandsChunk::updateLoadCommandContent(const lld::File &file) { void LoadCommandsChunk::updateLoadCommandContent(const lld::File &file) {
// Update segment/section information in segment load commands // Update segment/section information in segment load commands
segment_command_64 *lastSegment = nullptr; segment_command *lastSegment = nullptr;
for (ChunkSegInfo &entry : _sectionInfo) { for (ChunkSegInfo &entry : _sectionInfo) {
// Set section info. // Set section info.
::strncpy(entry.section->sectname, entry.chunk->sectionName().data(), 16); ::strncpy(entry.section->sectname, entry.chunk->sectionName().data(), 16);
@ -1158,23 +1177,25 @@ void LazyBindingInfoChunk::computeSize(const lld::File &file,
// SymbolTableChunk // SymbolTableChunk
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
SymbolTableChunk::SymbolTableChunk(SymbolStringsChunk& str) SymbolTableChunk::SymbolTableChunk(SymbolStringsChunk &str, MachOWriter &wrtr)
: _stringsChunk(str) { : _writer(wrtr), _stringsChunk(str) {
} }
void SymbolTableChunk::write(uint8_t *chunkBuffer) { void SymbolTableChunk::write(uint8_t *chunkBuffer) {
const bool is64 = _writer.use64BitMachO();
const unsigned nlistSize = nlist::size(is64);
uint8_t *p = chunkBuffer; uint8_t *p = chunkBuffer;
for ( nlist_64 &sym : _globalDefinedsymbols ) { for ( nlist &sym : _globalDefinedsymbols ) {
sym.copyTo(p); sym.copyTo(p, is64);
p += sizeof(nlist_64); p += nlistSize;
} }
for ( nlist_64 &sym : _localDefinedsymbols ) { for ( nlist &sym : _localDefinedsymbols ) {
sym.copyTo(p); sym.copyTo(p, is64);
p += sizeof(nlist_64); p += nlistSize;
} }
for ( nlist_64 &sym : _undefinedsymbols ) { for ( nlist &sym : _undefinedsymbols ) {
sym.copyTo(p); sym.copyTo(p, is64);
p += sizeof(nlist_64); p += nlistSize;
} }
} }
@ -1212,7 +1233,7 @@ void SymbolTableChunk::computeSize(const lld::File &file,
if ( info.atom->name().empty() ) if ( info.atom->name().empty() )
continue; continue;
uint64_t atomAddress = chunk->address() + info.offsetInSection; uint64_t atomAddress = chunk->address() + info.offsetInSection;
nlist_64 sym; nlist sym;
sym.n_strx = _stringsChunk.stringIndex(info.atom->name()); sym.n_strx = _stringsChunk.stringIndex(info.atom->name());
sym.n_type = this->nType(info.atom); sym.n_type = this->nType(info.atom);
sym.n_sect = sectionIndex; sym.n_sect = sectionIndex;
@ -1227,7 +1248,7 @@ void SymbolTableChunk::computeSize(const lld::File &file,
// Add symbols for undefined/sharedLibrary symbols // Add symbols for undefined/sharedLibrary symbols
for (const SharedLibraryAtom* atom : file.sharedLibrary() ) { for (const SharedLibraryAtom* atom : file.sharedLibrary() ) {
nlist_64 sym; nlist sym;
sym.n_strx = _stringsChunk.stringIndex(atom->name()); sym.n_strx = _stringsChunk.stringIndex(atom->name());
sym.n_type = N_UNDF; sym.n_type = N_UNDF;
sym.n_sect = 0; sym.n_sect = 0;
@ -1235,7 +1256,7 @@ void SymbolTableChunk::computeSize(const lld::File &file,
_undefinedsymbols.push_back(sym); _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); _bindingInfo = new BindingInfoChunk(*this);
_lazyBindingInfo = new LazyBindingInfoChunk(*this); _lazyBindingInfo = new LazyBindingInfoChunk(*this);
_stringsChunk = new SymbolStringsChunk(); _stringsChunk = new SymbolStringsChunk();
_symbolTableChunk = new SymbolTableChunk(*_stringsChunk); _symbolTableChunk = new SymbolTableChunk(*_stringsChunk, *this);
this->addLinkEditChunk(_bindingInfo); this->addLinkEditChunk(_bindingInfo);
this->addLinkEditChunk(_lazyBindingInfo); this->addLinkEditChunk(_lazyBindingInfo);
this->addLinkEditChunk(_symbolTableChunk); 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 // Creates a mach-o final linked image from the given atom graph and writes
@ -1519,7 +1552,7 @@ WriterOptionsMachO::WriterOptionsMachO()
: _outputkind(outputDynamicExecutable), : _outputkind(outputDynamicExecutable),
_archName("x86_64"), _archName("x86_64"),
_architecture(arch_x86_64), _architecture(arch_x86_64),
_pageZeroSize(0x10000000), _pageZeroSize(0x100000000),
_cpuType(mach_o::CPU_TYPE_X86_64), _cpuType(mach_o::CPU_TYPE_X86_64),
_cpuSubtype(mach_o::CPU_SUBTYPE_X86_64_ALL), _cpuSubtype(mach_o::CPU_SUBTYPE_X86_64_ALL),
_noTextRelocations(true) { _noTextRelocations(true) {