diff options
Diffstat (limited to 'core/macfile.cc')
-rw-r--r-- | core/macfile.cc | 6715 |
1 files changed, 6715 insertions, 0 deletions
diff --git a/core/macfile.cc b/core/macfile.cc new file mode 100644 index 0000000..102b121 --- /dev/null +++ b/core/macfile.cc @@ -0,0 +1,6715 @@ +/** + * Support of Mach-O executable files. + */ + +#include "../runtime/common.h" +#include "../runtime/crypto.h" +#include "objects.h" +#include "osutils.h" +#include "streams.h" +#include "files.h" +#include "dwarf.h" +#include "macfile.h" +#include "processors.h" +#include "intel.h" +#include "lang.h" +#include "core.h" +#include "script.h" + +#include "mac_runtime32.dylib.inc" +#include "mac_runtime64.dylib.inc" + +/** + * MacLoadCommand + */ + +MacLoadCommand::MacLoadCommand(MacLoadCommandList *owner) + : BaseLoadCommand(owner), address_(0), size_(0), type_(0), object_(NULL), offset_(0) +{ + +} + +MacLoadCommand::MacLoadCommand(MacLoadCommandList *owner, uint64_t address, uint32_t size, uint32_t type) + : BaseLoadCommand(owner), address_(address), size_(size), type_(type), object_(NULL), offset_(0) +{ + +} + +MacLoadCommand::MacLoadCommand(MacLoadCommandList *owner, uint32_t type, IObject *object) + : BaseLoadCommand(owner), address_(0), size_(0), type_(type), object_(object), offset_(0) +{ + +} + +MacLoadCommand::MacLoadCommand(MacLoadCommandList *owner, const MacLoadCommand &src) + : BaseLoadCommand(owner, src), object_(NULL), offset_(0) +{ + address_ = src.address_; + size_ = src.size_; + type_ = src.type_; +} + +MacLoadCommand *MacLoadCommand::Clone(ILoadCommandList *owner) const +{ + MacLoadCommand *command = new MacLoadCommand(reinterpret_cast<MacLoadCommandList *>(owner), *this); + return command; +} + +void MacLoadCommand::ReadFromFile(MacArchitecture &file) +{ + load_command lc; + + /* Store current position into address_ + * and read struct load_command */ + address_ = file.Tell(); + file.Read(&lc, sizeof(lc)); + type_ = lc.cmd; + size_ = lc.cmdsize; + if (size_ < sizeof(lc)) + throw std::runtime_error("Invalid format"); +} + +std::string MacLoadCommand::name() const +{ + switch (type_) { + case LC_SEGMENT: + return std::string("LC_SEGMENT"); + case LC_SYMTAB: + return std::string("LC_SYMTAB"); + case LC_SYMSEG: + return std::string("LC_SYMSEG"); + case LC_THREAD: + return std::string("LC_THREAD"); + case LC_UNIXTHREAD: + return std::string("LC_UNIXTHREAD"); + case LC_LOADFVMLIB: + return std::string("LC_LOADFVMLIB"); + case LC_IDFVMLIB: + return std::string("LC_IDFVMLIB"); + case LC_IDENT: + return std::string("LC_IDENT"); + case LC_FVMFILE: + return std::string("LC_FVMFILE"); + case LC_PREPAGE: + return std::string("LC_PREPAGE"); + case LC_DYSYMTAB: + return std::string("LC_DYSYMTAB"); + case LC_LOAD_DYLIB: + return std::string("LC_LOAD_DYLIB"); + case LC_ID_DYLIB: + return std::string("LC_ID_DYLIB"); + case LC_LOAD_DYLINKER: + return std::string("LC_LOAD_DYLINKER"); + case LC_ID_DYLINKER: + return std::string("LC_ID_DYLINKER"); + case LC_PREBOUND_DYLIB: + return std::string("LC_PREBOUND_DYLIB"); + case LC_ROUTINES: + return std::string("LC_ROUTINES"); + case LC_SUB_FRAMEWORK: + return std::string("LC_SUB_FRAMEWORK"); + case LC_SUB_UMBRELLA: + return std::string("LC_SUB_UMBRELLA"); + case LC_SUB_CLIENT: + return std::string("LC_SUB_CLIENT"); + case LC_SUB_LIBRARY: + return std::string("LC_SUB_LIBRARY"); + case LC_TWOLEVEL_HINTS: + return std::string("LC_TWOLEVEL_HINTS"); + case LC_PREBIND_CKSUM: + return std::string("LC_PREBIND_CKSUM"); + case LC_LOAD_WEAK_DYLIB: + return std::string("LC_LOAD_WEAK_DYLIB"); + case LC_SEGMENT_64: + return std::string("LC_SEGMENT_64"); + case LC_ROUTINES_64: + return std::string("LC_ROUTINES_64"); + case LC_UUID: + return std::string("LC_UUID"); + case LC_RPATH: + return std::string("LC_RPATH"); + case LC_CODE_SIGNATURE: + return std::string("LC_CODE_SIGNATURE"); + case LC_SEGMENT_SPLIT_INFO: + return std::string("LC_SEGMENT_SPLIT_INFO"); + case LC_DYLD_INFO: + return std::string("LC_DYLD_INFO"); + case LC_DYLD_INFO_ONLY: + return std::string("LC_DYLD_INFO_ONLY"); + case LC_VERSION_MIN_MACOSX: + return std::string("LC_VERSION_MIN_MACOSX"); + case LC_FUNCTION_STARTS: + return std::string("LC_FUNCTION_STARTS"); + case LC_DYLD_ENVIRONMENT: + return std::string("LC_DYLD_ENVIRONMENT"); + case LC_MAIN: + return std::string("LC_MAIN"); + case LC_DATA_IN_CODE: + return std::string("LC_DATA_IN_CODE"); + case LC_SOURCE_VERSION: + return std::string("LC_SOURCE_VERSION"); + case LC_DYLIB_CODE_SIGN_DRS: + return std::string("LC_DYLIB_CODE_SIGN_DRS"); + case LC_ENCRYPTION_INFO_64: + return std::string("LC_ENCRYPTION_INFO_64"); + case LC_LINKER_OPTION: + return std::string("LC_LINKER_OPTION"); + case LC_LINKER_OPTIMIZATION_HINT: + return std::string("LC_LINKER_OPTIMIZATION_HINT"); + case LC_VERSION_MIN_TVOS: + return std::string("LC_VERSION_MIN_TVOS"); + case LC_VERSION_MIN_WATCHOS: + return std::string("LC_VERSION_MIN_WATCHOS"); + case LC_NOTE: + return std::string("LC_NOTE"); + case LC_BUILD_VERSION: + return std::string("LC_BUILD_VERSION"); + } + + return BaseLoadCommand::name(); +} + +void MacLoadCommand::WriteToFile(MacArchitecture &file) +{ + const IArchitecture *source = file.source(); + uint64_t position = file.Tell(); + bool need_copy = true; + + switch (type_) { + case LC_SEGMENT: + case LC_SEGMENT_64: + if (object_) { + reinterpret_cast<MacSegment *>(object_)->WriteToFile(file); + need_copy = false; + } + break; + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + if (object_) { + reinterpret_cast<MacImport *>(object_)->WriteToFile(file); + need_copy = false; + } + break; + case LC_SYMTAB: + { + symtab_command *symtab = file.symtab(); + file.Write(symtab, sizeof(symtab_command)); + need_copy = false; + } + break; + case LC_DYSYMTAB: + { + dysymtab_command *dysymtab = file.dysymtab(); + file.Write(dysymtab, sizeof(dysymtab_command)); + need_copy = false; + } + break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + { + dyld_info_command *dyld_info = file.dyld_info(); + file.Write(dyld_info, sizeof(dyld_info_command)); + need_copy = false; + } + break; + case LC_UNIXTHREAD: + { + thread_command command; + source->Seek(address_); + source->Read(&command, sizeof(command)); + x86_state_hdr_t state_hdr; + source->Read(&state_hdr, sizeof(state_hdr)); + + switch (file.type()) { + case CPU_TYPE_I386: + if (state_hdr.flavor == x86_THREAD_STATE32) { + x86_thread_state32_t thread_state; + source->Read(&thread_state, sizeof(thread_state)); + thread_state.__eip = static_cast<uint32_t>(file.entry_point()); + file.Write(&command, sizeof(command)); + file.Write(&state_hdr, sizeof(state_hdr)); + file.Write(&thread_state, sizeof(thread_state)); + need_copy = false; + } + break; + case CPU_TYPE_X86_64: + if (state_hdr.flavor == x86_THREAD_STATE64) { + x86_thread_state64_t thread_state; + source->Read(&thread_state, sizeof(thread_state)); + thread_state.__rip = file.entry_point(); + file.Write(&command, sizeof(command)); + file.Write(&state_hdr, sizeof(state_hdr)); + file.Write(&thread_state, sizeof(thread_state)); + need_copy = false; + } + break; + } + } + break; + case LC_MAIN: + { + entry_point_command command; + source->Seek(address_); + source->Read(&command, sizeof(command)); + + command.entryoff = file.entry_point() - file.segment_list()->GetBaseSegment()->address(); + + file.Write(&command, sizeof(command)); + need_copy = false; + } + break; + case LC_FUNCTION_STARTS: + case LC_DATA_IN_CODE: + case LC_DYLIB_CODE_SIGN_DRS: + { + linkedit_data_command command; + source->Seek(address_); + source->Read(&command, sizeof(command)); + command.dataoff = offset_; + file.Write(&command, sizeof(command)); + need_copy = false; + } + break; + case LC_ID_DYLIB: + if (file.export_list()->name() != source->export_list()->name()) { + std::string name = file.export_list()->name(); + name.resize(AlignValue(name.size() + 1, (file.cpu_address_size() == osDWord) ? sizeof(uint32_t) : sizeof(uint64_t)) - 1, 0); + dylib_command command; + source->Seek(address_); + source->Read(&command, sizeof(command)); + command.cmdsize = static_cast<uint32_t>(sizeof(command) + name.size() + 1); + command.dylib.name.offset = sizeof(command); + file.Write(&command, sizeof(command)); + file.Write(name.c_str(), name.size() + 1); + need_copy = false; + } + break; + } + + if (need_copy) { + source->Seek(address_); + file.CopyFrom(*source, size_); + } + + address_ = position; +} + +void MacLoadCommand::Rebase(uint64_t delta_base) +{ + address_ += delta_base; +} + +/** + * MacLoadCommandList + */ + +MacLoadCommandList::MacLoadCommandList(MacArchitecture *owner) + : BaseCommandList(owner) +{ + +} + +MacLoadCommandList::MacLoadCommandList(MacArchitecture *owner, const MacLoadCommandList &src) + : BaseCommandList(owner, src) +{ + +} + +MacLoadCommandList *MacLoadCommandList::Clone(MacArchitecture *owner) const +{ + MacLoadCommandList *load_command_list = new MacLoadCommandList(owner, *this); + return load_command_list; +} + +MacLoadCommand *MacLoadCommandList::item(size_t index) const +{ + return reinterpret_cast<MacLoadCommand*>(BaseCommandList::item(index)); +} + +void MacLoadCommandList::ReadFromFile(MacArchitecture &file, size_t count) +{ + uint64_t pos = file.Tell(); + Reserve(count); + for (size_t i = 0; i < count; i++) { + file.Seek(pos); + MacLoadCommand *command = new MacLoadCommand(this); + AddObject(command); + command->ReadFromFile(file); + pos += command->size(); + } +} + +void MacLoadCommandList::WriteToFile(MacArchitecture &file) +{ + for (size_t i = 0; i < count(); i++) { + item(i)->WriteToFile(file); + } +} + +void MacLoadCommandList::Pack() +{ + for (size_t i = count(); i > 0 ; i--) { + MacLoadCommand *load_command = item(i - 1); + switch (load_command->type()) { + case LC_SEGMENT: + case LC_SEGMENT_64: + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_CODE_SIGNATURE: + delete load_command; + break; + } + } +} + +void MacLoadCommandList::Add(uint32_t type, IObject *object) +{ + MacLoadCommand *command = new MacLoadCommand(this, type, object); + if (type == LC_SEGMENT || type == LC_SEGMENT_64) { + // insert segment commands to the top of list + for (size_t i = 0; i < count(); i++) { + MacLoadCommand *lc = item(i); + if (lc->type() == LC_SEGMENT || lc->type() == LC_SEGMENT_64) + continue; + InsertObject(i, command); + return; + } + } + AddObject(command); +} + +MacLoadCommand *MacLoadCommandList::GetCommandByObject(void *object) const +{ + for (size_t i = 0; i < count(); i++) { + MacLoadCommand *load_command = item(i); + if (load_command->object() == object) + return load_command; + } + return NULL; +} + +/** + * MacSegment + */ + +MacSegment::MacSegment(MacSegmentList *owner) + : BaseSection(owner), address_(0), size_(0), + physical_offset_(0), physical_size_(0), nsects_(0), maxprot_(VM_PROT_NONE), initprot_(VM_PROT_NONE) +{ + +} + +MacSegment::MacSegment(MacSegmentList *owner,uint64_t address, uint64_t size, uint32_t physical_offset, + uint32_t physical_size, uint32_t initprot, const std::string &name) + : BaseSection(owner), address_(address), size_(size), physical_offset_(physical_offset), physical_size_(physical_size), + nsects_(0), maxprot_(initprot), initprot_(initprot), name_(name) +{ + +} + +MacSegment::MacSegment(MacSegmentList *owner, const MacSegment &src) + : BaseSection(owner, src), nsects_(0) +{ + address_ = src.address_; + size_ = src.size_; + physical_offset_ = src.physical_offset_; + physical_size_ = src.physical_size_; + maxprot_ = src.maxprot_; + initprot_ = src.initprot_; + name_ = src.name_; +} + +MacSegment *MacSegment::Clone(ISectionList *owner) const +{ + MacSegment *segment = new MacSegment(reinterpret_cast<MacSegmentList *>(owner), *this); + return segment; +} + +uint32_t MacSegment::memory_type() const +{ + uint32_t res = mtNone; + if (initprot_ & VM_PROT_READ) + res |= mtReadable; + if (initprot_ & VM_PROT_WRITE) + res |= mtWritable; + if (initprot_ & VM_PROT_EXECUTE) + res |= mtExecutable; + return res; +} + +void MacSegment::ReadFromFile(MacArchitecture &file) +{ + uint32_t need_type = (file.cpu_address_size() == osDWord) ? LC_SEGMENT : LC_SEGMENT_64; + uint32_t type = file.ReadDWord(); + + if (type != need_type) + throw std::runtime_error("Invalid segment type"); + + if (file.cpu_address_size() == osDWord) { + segment_command command = segment_command(); + file.Read(&command.cmdsize, sizeof(command) - offsetof(segment_command, cmdsize)); + address_ = command.vmaddr; + size_ = command.vmsize; + physical_offset_ = command.fileoff; + physical_size_ = command.filesize; + nsects_ = command.nsects; + maxprot_ = command.maxprot; + initprot_ = command.initprot; + name_ = std::string(command.segname, strnlen(command.segname, sizeof(command.segname))); + } else { + segment_command_64 command = segment_command_64(); + file.Read(&command.cmdsize, sizeof(command) - offsetof(segment_command_64, cmdsize)); + if (command.filesize >> 32) + throw std::runtime_error("Segment size is too large"); + if (command.fileoff >> 32) + throw std::runtime_error("Segment offset is too large"); + address_ = command.vmaddr; + size_ = command.vmsize; + physical_offset_ = static_cast<uint32_t>(command.fileoff); + physical_size_ = static_cast<uint32_t>(command.filesize); + nsects_ = command.nsects; + maxprot_ = command.maxprot; + initprot_ = command.initprot; + name_ = std::string(command.segname, strnlen(command.segname, sizeof(command.segname))); + } + + // read sections for this segment + file.section_list()->ReadFromFile(file, nsects_, this); +} + +void MacSegment::WriteToFile(MacArchitecture &file) +{ + size_t i; + MacSectionList *section_list = file.section_list(); + + // calc child section sount + nsects_ = 0; + for (i = 0; i < section_list->count(); i++) { + if (section_list->item(i)->parent() == this) + nsects_++; + } + + // write header + uint64_t command_pos = file.Tell(); + if (file.cpu_address_size() == osDWord) { + segment_command cmd = segment_command(); + cmd.cmd = LC_SEGMENT; + cmd.vmaddr = static_cast<uint32_t>(address_); + cmd.vmsize = static_cast<uint32_t>(size_); + cmd.fileoff = static_cast<uint32_t>(physical_offset_); + cmd.filesize = static_cast<uint32_t>(physical_size_); + cmd.nsects = nsects_; + cmd.maxprot = maxprot_; + cmd.initprot = initprot_; + memcpy(cmd.segname, name_.c_str(), std::min(name_.size(), sizeof(cmd.segname))); + file.Write(&cmd, sizeof(cmd)); + } else { + segment_command_64 cmd = segment_command_64(); + cmd.cmd = LC_SEGMENT_64; + cmd.vmaddr = address_; + cmd.vmsize = size_; + cmd.fileoff = physical_offset_; + cmd.filesize = physical_size_; + cmd.nsects = nsects_; + cmd.maxprot = maxprot_; + cmd.initprot = initprot_; + memcpy(cmd.segname, name_.c_str(), std::min(name_.size(), sizeof(cmd.segname))); + file.Write(&cmd, sizeof(cmd)); + } + + // write child sections + for (i = 0; i < section_list->count(); i++) { + MacSection *section = section_list->item(i); + if (section->parent() == this) + section->WriteToFile(file); + } + + // write command size + uint64_t current_pos = file.Tell(); + file.Seek(command_pos + offsetof(segment_command, cmdsize)); + uint32_t command_size = static_cast<uint32_t>(current_pos - command_pos); + file.Write(&command_size, sizeof(command_size)); + file.Seek(current_pos); +} + +void MacSegment::update_type(uint32_t mt) +{ + if (mt & mtReadable) + initprot_ |= VM_PROT_READ; + if (mt & mtWritable) + initprot_ |= VM_PROT_WRITE; + if (mt & mtExecutable) + initprot_ |= VM_PROT_EXECUTE; + + maxprot_ = (initprot_ == VM_PROT_NONE) ? initprot_ : VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; +} + +void MacSegment::Rebase(uint64_t delta_base) +{ + address_ += delta_base; +} + +/** + * MacSegmentList + */ + +MacSegmentList::MacSegmentList(MacArchitecture *owner) + : BaseSectionList(owner) +{ + +} + +MacSegmentList::MacSegmentList(MacArchitecture *owner, const MacSegmentList &src) + : BaseSectionList(owner, src) +{ + +} + +MacSegmentList *MacSegmentList::Clone(MacArchitecture *owner) const +{ + MacSegmentList *list = new MacSegmentList(owner, *this); + return list; +} + +MacSegment *MacSegmentList::item(size_t index) const +{ + return reinterpret_cast<MacSegment*>(BaseSectionList::item(index)); +} + +MacSegment *MacSegmentList::Add() +{ + MacSegment *segment = new MacSegment(this); + AddObject(segment); + return segment; +} + +void MacSegmentList::ReadFromFile(MacArchitecture &file) +{ + uint32_t cmd_type = (file.cpu_address_size() == osDWord) ? LC_SEGMENT : LC_SEGMENT_64; + MacLoadCommandList *command_list = file.command_list(); + for (size_t i = 0; i < command_list->count(); i++) { + MacLoadCommand *command = command_list->item(i); + if (command->type() == cmd_type) { + file.Seek(command->address()); + Add()->ReadFromFile(file); + } + } +} + +void MacSegmentList::WriteToFile(MacArchitecture &file) +{ + uint32_t cmd_type = (file.cpu_address_size() == osDWord) ? LC_SEGMENT : LC_SEGMENT_64; + for (size_t i = 0; i < count(); i++) { + MacSegment *segment = item(i); + file.command_list()->Add(cmd_type, segment); + } +} + +MacSegment *MacSegmentList::last() const +{ + return reinterpret_cast<MacSegment *>(BaseSectionList::last()); +} + +MacSegment *MacSegmentList::Add(uint64_t address, uint64_t size, uint32_t physical_offset, uint32_t physical_size, uint32_t initprot, const std::string &name) +{ + MacSegment *segment = new MacSegment(this, address, size, physical_offset, physical_size, initprot, name); + AddObject(segment); + return segment; +} + +MacSegment *MacSegmentList::GetSectionByName(const std::string &name) const +{ + return reinterpret_cast<MacSegment *>(BaseSectionList::GetSectionByName(name)); +} + +MacSegment *MacSegmentList::GetSectionByAddress(uint64_t address) const +{ + return reinterpret_cast<MacSegment *>(BaseSectionList::GetSectionByAddress(address)); +} + +MacSegment *MacSegmentList::GetBaseSegment() const +{ + for (size_t i = 0; i < count(); i++) { + MacSegment *segment = item(i); + if (segment->physical_offset() == 0 && segment->physical_size()) + return segment; + } + return NULL; +} + +/** + * MacStringTable + */ + +std::string MacStringTable::GetString(uint32_t pos) const +{ + size_t i, len; + + if (pos >= data_.size()) + throw std::runtime_error("Invalid index for string table"); + + len = data_.size() - pos; + for (i = 0; i < len; i++) { + if (data_[pos + i] == 0) { + len = i; + break; + } + } + if (len == data_.size() - pos) + throw std::runtime_error("Invalid format"); + + return std::string(&data_[pos], len); +} + +uint32_t MacStringTable::AddString(const std::string &str) +{ + if (str.empty()) + return 1; + + uint32_t res = static_cast<uint32_t>(data_.size()); + data_.insert(data_.end(), str.c_str(), str.c_str() + str.size() + 1); + + return res; +}; + +void MacStringTable::clear() +{ + data_.clear(); + AddString(" "); +} + +void MacStringTable::ReadFromFile(MacArchitecture &file) +{ + symtab_command *symtab = file.symtab(); + + if (!symtab->cmd) + return; + + data_.resize(symtab->strsize); + file.Seek(symtab->stroff); + file.Read(data_.data(), data_.size()); +} + +void MacStringTable::WriteToFile(MacArchitecture &file) +{ + symtab_command *symtab = file.symtab(); + // align table size + size_t aligned_size = AlignValue(data_.size(), OperandSizeToValue(file.cpu_address_size())); + while (data_.size() < aligned_size) { + data_.push_back(0); + } + + symtab->strsize = (uint32_t)data_.size(); + if (!symtab->strsize) { + symtab->stroff = 0; + return; + } + + symtab->stroff = static_cast<uint32_t>(file.Tell()); + file.Write(data_.data(), data_.size()); +} + +/** + * MacSymbol + */ + +MacSymbol::MacSymbol(MacSymbolList *owner) + : IObject(), owner_(owner), type_(0), sect_(0), desc_(0), value_(0), is_deleted_(false) +{ + +} + +MacSymbol::MacSymbol(MacSymbolList *owner, const MacSymbol &src) + : IObject(src), owner_(owner) +{ + type_ = src.type_; + sect_ = src.sect_; + desc_ = src.desc_; + value_ = src.value_; + name_ = src.name_; + is_deleted_ = src.is_deleted_; +} + +MacSymbol::~MacSymbol() +{ + if (owner_) + owner_->RemoveObject(this); +} + +MacSymbol *MacSymbol::Clone(MacSymbolList *owner) const +{ + MacSymbol *symbol = new MacSymbol(owner, *this); + return symbol; +} + +void MacSymbol::ReadFromFile(MacArchitecture &file) +{ + uint32_t strx; + if (file.cpu_address_size() == osDWord) { + struct nlist nl; + file.Read(&nl, sizeof(nl)); + strx = nl.n_un.n_strx; + type_ = nl.n_type; + sect_ = nl.n_sect; + desc_ = nl.n_desc; + value_ = nl.n_value; + } else { + nlist_64 nl; + file.Read(&nl, sizeof(nl)); + strx = nl.n_un.n_strx; + type_ = nl.n_type; + sect_ = nl.n_sect; + desc_ = nl.n_desc; + value_ = nl.n_value; + } + + name_ = file.string_table()->GetString(strx); +} + +void MacSymbol::WriteToFile(MacArchitecture &file) +{ + uint32_t strx = file.string_table()->AddString(name_); + + if ((type_ & (N_STAB | N_TYPE)) == N_SECT && sect_ != NO_SECT) { + for (size_t i = 0; i < file.section_list()->count(); i++) { + MacSection *section = file.section_list()->item(i); + if (value_ < section->address() + section->size()) { + sect_ = static_cast<uint8_t>(i + 1); + break; + } + } + } + + if (file.cpu_address_size() == osDWord) { + struct nlist nl; + nl.n_un.n_strx = strx; + nl.n_type = type_; + nl.n_sect = sect_; + nl.n_desc = desc_; + nl.n_value = static_cast<uint32_t>(value_); + file.Write(&nl, sizeof(nl)); + } else { + nlist_64 nl; + nl.n_un.n_strx = strx; + nl.n_type = type_; + nl.n_sect = sect_; + nl.n_desc = desc_; + nl.n_value = value_; + file.Write(&nl, sizeof(nl)); + } +} + +uint8_t MacSymbol::library_ordinal() const +{ + if ((type_ & (N_STAB | N_TYPE)) == N_UNDF) + return (desc_ >> 8) & 0xff; + + return 0; +} + +void MacSymbol::set_library_ordinal(uint8_t library_ordinal) +{ + if ((type_ & (N_STAB | N_TYPE)) == N_UNDF) { + desc_ &= 0xff; + desc_ |= library_ordinal << 8; + } +} + +/** + * MacSymbolList + */ + +MacSymbolList::MacSymbolList() + : ObjectList<MacSymbol>() +{ + +} + +MacSymbolList::MacSymbolList(const MacSymbolList &src) + : ObjectList<MacSymbol>(src) +{ + for (size_t i = 0; i < src.count(); i++) { + AddObject(src.item(i)->Clone(this)); + } +} + +MacSymbolList *MacSymbolList::Clone() const +{ + MacSymbolList *list = new MacSymbolList(*this); + return list; +} + +void MacSymbolList::ReadFromFile(MacArchitecture &file, size_t count) +{ + symtab_command *symtab = file.symtab(); + if (!symtab->cmd) + return; + + file.Seek(symtab->symoff); + for (size_t i = 0; i < count; i++) { + MacSymbol *symbol = new MacSymbol(this); + symbol->ReadFromFile(file); + AddObject(symbol); + } +} + +void MacSymbolList::WriteToFile(MacArchitecture &file) +{ + symtab_command *symtab = file.symtab(); + symtab->nsyms = static_cast<uint32_t>(count()); + symtab->symoff = static_cast<uint32_t>(file.Tell()); + + dysymtab_command *dysymtab = file.dysymtab(); + if (!dysymtab->cmd) + return; + + dysymtab->ilocalsym = 0; + dysymtab->nlocalsym = 0; + dysymtab->iextdefsym = 0; + dysymtab->nextdefsym = 0; + dysymtab->iundefsym = 0; + dysymtab->nundefsym = 0; + + for (size_t i = 0; i < count(); i++) { + MacSymbol *symbol = item(i); + symbol->WriteToFile(file); + + if ((symbol->type() & (N_STAB | N_TYPE)) == N_UNDF) { + // undefined symbol + if (!dysymtab->nundefsym) + dysymtab->iundefsym = static_cast<uint32_t>(i); + dysymtab->nundefsym++; + } else if (symbol->type() & N_EXT) { + // external symbol + if (!dysymtab->nextdefsym) + dysymtab->iextdefsym = static_cast<uint32_t>(i); + dysymtab->nextdefsym++; + } else { + // local symbol + if (!dysymtab->nlocalsym) + dysymtab->ilocalsym = static_cast<uint32_t>(i); + dysymtab->nlocalsym++; + } + } +} + +MacSymbol *MacSymbolList::GetSymbol(const std::string &name, int library_ordinal) const +{ + for (size_t i = 0; i < count(); i++) { + MacSymbol *symbol = item(i); + if ((symbol->type() & (N_STAB | N_TYPE)) == N_UNDF && symbol->name() == name) { + if (library_ordinal < 0 || library_ordinal == symbol->library_ordinal()) + return symbol; + } + } + return NULL; +} + +void MacSymbolList::Pack() +{ + for (size_t i = count(); i > 0 ; i--) { + MacSymbol *symbol = item(i - 1); + if (symbol->is_deleted()) + delete symbol; + } +} + +/** + * MacSection + */ + +MacSection::MacSection(MacSectionList *owner, MacSegment *parent) + : BaseSection(owner), address_(0), size_(0), offset_(0), align_(0), reloff_(0), + nreloc_(0), flags_(0), reserved1_(0), reserved2_(0), parent_(parent) +{ + +} + +MacSection::MacSection(MacSectionList *owner, MacSegment *parent, uint64_t address, uint32_t size, uint32_t offset, uint32_t flags, const std::string &name, const std::string &segment_name) + : BaseSection(owner), name_(name), address_(address), size_(size), offset_(offset), align_(0), reloff_(0), + nreloc_(0), flags_(flags), reserved1_(0), reserved2_(0), parent_(parent), segment_name_(segment_name) +{ + +} + +MacSection::~MacSection() +{ + +} + +MacSection::MacSection(MacSectionList *owner, const MacSection &src) + : BaseSection(owner, src) +{ + parent_ = src.parent_; + address_ = src.address_; + size_ = src.size_; + offset_ = src.offset_; + name_ = src.name_; + align_ = src.align_; + reloff_ = src.reloff_; + nreloc_ = src.nreloc_; + flags_ = src.flags_; + reserved1_ = src.reserved1_; + reserved2_ = src.reserved2_; + segment_name_ = src.segment_name_; +} + +MacSection *MacSection::Clone(ISectionList *owner) const +{ + MacSection *section = new MacSection(reinterpret_cast<MacSectionList *>(owner), *this); + return section; +} + +void MacSection::ReadFromFile(MacArchitecture &file) +{ + if (file.cpu_address_size() == osDWord) { + section sec; + file.Read(&sec, sizeof(sec)); + name_ = std::string(sec.sectname, strnlen(sec.sectname, sizeof(sec.sectname))); + segment_name_ = std::string(sec.segname, strnlen(sec.segname, sizeof(sec.segname))); + address_ = sec.addr; + size_ = sec.size; + offset_ = sec.offset; + align_ = sec.align; + reloff_ = sec.reloff; + nreloc_ = sec.nreloc; + flags_ = sec.flags; + reserved1_ = sec.reserved1; + reserved2_ = sec.reserved2; + } else { + section_64 sec; + file.Read(&sec, sizeof(sec)); + name_ = std::string(sec.sectname, strnlen(sec.sectname, sizeof(sec.sectname))); + segment_name_ = std::string(sec.segname, strnlen(sec.segname, sizeof(sec.segname))); + address_ = sec.addr; + size_ = static_cast<uint32_t>(sec.size); + offset_ = sec.offset; + align_ = sec.align; + reloff_ = sec.reloff; + nreloc_ = sec.nreloc; + flags_ = sec.flags; + reserved1_ = sec.reserved1; + reserved2_ = sec.reserved2; + } +} + +void MacSection::WriteToFile(MacArchitecture &file) +{ + if (file.cpu_address_size() == osDWord) { + section sec = section(); + sec.addr = static_cast<uint32_t>(address_); + sec.size = static_cast<uint32_t>(size_); + sec.offset = offset_; + sec.align = align_; + sec.reloff = reloff_; + sec.nreloc = nreloc_; + sec.flags = flags_; + sec.reserved1 = reserved1_; + sec.reserved2 = reserved2_; + memcpy(sec.sectname, name_.c_str(), std::min(name_.size(), sizeof(sec.sectname))); + memcpy(sec.segname, segment_name_.c_str(), std::min(segment_name_.size(), sizeof(sec.segname))); + file.Write(&sec, sizeof(sec)); + } else { + section_64 sec = section_64(); + sec.addr = address_; + sec.size = size_; + sec.offset = offset_; + sec.align = align_; + sec.reloff = reloff_; + sec.nreloc = nreloc_; + sec.flags = flags_; + sec.reserved1 = reserved1_; + sec.reserved2 = reserved2_; + memcpy(sec.sectname, name_.c_str(), std::min(name_.size(), sizeof(sec.sectname))); + memcpy(sec.segname, segment_name_.c_str(), std::min(segment_name_.size(), sizeof(sec.segname))); + file.Write(&sec, sizeof(sec)); + } +} + +void MacSection::set_alignment(size_t value) +{ + align_ = 0; + for (uint32_t i = 0; value && i < 8 * sizeof(size_t); i++) { + if (value & 1) { + align_ = i; + break; + } + value >>= 1; + } +} + +void MacSection::Rebase(uint64_t delta_base) +{ + address_ += delta_base; +} + +/** + * MacSectionList + */ + +MacSectionList::MacSectionList(MacArchitecture *owner) + : BaseSectionList(owner) +{ + +} + +MacSectionList::MacSectionList(MacArchitecture *owner, const MacSectionList &src) + : BaseSectionList(owner, src) +{ + +} + +MacSectionList *MacSectionList::Clone(MacArchitecture *owner) const +{ + MacSectionList *list = new MacSectionList(owner, *this); + return list; +} + +MacSection *MacSectionList::Add(MacSegment *segment, uint64_t address, uint32_t size, uint32_t offset, uint32_t flags, const std::string &name, const std::string &segment_name) +{ + MacSection *section = new MacSection(this, segment, address, size, offset, flags, name, segment_name); + AddObject(section); + return section; +} + +MacSection *MacSectionList::GetSectionByAddress(uint64_t address) const +{ + return reinterpret_cast<MacSection *>(BaseSectionList::GetSectionByAddress(address)); +} + +MacSection *MacSectionList::GetSectionByName(const std::string &name) const +{ + return reinterpret_cast<MacSection *>(BaseSectionList::GetSectionByName(name)); +} + +MacSection *MacSectionList::GetSectionByName(ISection *segment, const std::string &name) const +{ + return reinterpret_cast<MacSection *>(BaseSectionList::GetSectionByName(segment, name)); +} + +void MacSectionList::ReadFromFile(MacArchitecture &file, size_t count, MacSegment *segment) +{ + for (size_t i = 0; i < count; i++) { + MacSection *section = new MacSection(this, segment); + AddObject(section); + section->ReadFromFile(file); + } +} + +MacSection *MacSectionList::item(size_t index) const +{ + return reinterpret_cast<MacSection*>(BaseSectionList::item(index)); +} + +/** + * MacImportFunction + */ + +MacImportFunction::MacImportFunction(IImport *owner, uint64_t address, uint8_t bind_type, size_t bind_offset, const std::string &name, uint32_t flags, int64_t addend, bool is_lazy, MacSymbol *symbol) + : BaseImportFunction(owner), address_(address), bind_type_(bind_type), name_(name), + flags_(flags), addend_(addend), is_lazy_(is_lazy), symbol_(symbol), bind_offset_(bind_offset) +{ + +} + +MacImportFunction::MacImportFunction(IImport *owner, uint64_t address, APIType type, MapFunction *map_function) + : BaseImportFunction(owner), address_(address), bind_type_(0), flags_(0), + addend_(0), is_lazy_(false), symbol_(NULL), bind_offset_(0) +{ + set_type(type); + set_map_function(map_function); +} + +MacImportFunction::MacImportFunction(IImport *owner, const MacImportFunction &src) + : BaseImportFunction(owner, src) +{ + address_ = src.address_; + bind_type_ = src.bind_type_; + name_ = src.name_; + flags_ = src.flags_; + addend_ = src.addend_; + is_lazy_ = src.is_lazy_; + bind_offset_ = src.bind_offset_; + symbol_ = src.symbol_; +} + +MacImportFunction *MacImportFunction::Clone(IImport *owner) const +{ + MacImportFunction *func = new MacImportFunction(owner, *this); + return func; +} + +int MacImportFunction::library_ordinal() const +{ + return reinterpret_cast<MacImport *>(owner())->library_ordinal(); +} + +bool MacImportFunction::is_weak() const +{ + if (!symbol_) + return false; + + if ((symbol_->type() & N_TYPE) == N_SECT) + return ((symbol_->desc() & N_WEAK_DEF) != 0); + else + return ((symbol_->desc() & N_REF_TO_WEAK) != 0); //-V +} + +void MacImportFunction::Rebase(uint64_t delta_base) +{ + if (address_) + address_ += delta_base; +} + +std::string MacImportFunction::display_name(bool show_ret) const +{ + return DemangleName(name_).display_name(show_ret); +} + +/** + * MacImport + */ + +MacImport::MacImport(MacImportList *owner, int library_ordinal, bool is_sdk) + : BaseImport(owner), library_ordinal_(library_ordinal), is_sdk_(is_sdk), timestamp_(0), current_version_(0), compatibility_version_(0), + is_weak_(false) +{ + +} + +MacImport::MacImport(MacImportList *owner, int library_ordinal, const std::string &name, uint32_t current_version, uint32_t compatibility_version) + : BaseImport(owner), library_ordinal_(library_ordinal), name_(name), is_sdk_(false), timestamp_(0), current_version_(current_version), compatibility_version_(compatibility_version), + is_weak_(false) +{ + +} + +MacImport::MacImport(MacImportList *owner, const MacImport &src) + : BaseImport(owner, src) +{ + library_ordinal_ = src.library_ordinal_; + name_ = src.name_; + is_sdk_ = src.is_sdk_; + timestamp_ = src.timestamp_; + current_version_ = src.current_version_; + compatibility_version_ = src.compatibility_version_; + is_weak_ = src.is_weak_; +} + +MacImport *MacImport::Clone(IImportList *owner) const +{ + MacImport *list = new MacImport(reinterpret_cast<MacImportList *>(owner), *this); + return list; +} + +void MacImport::ReadFromFile(MacArchitecture &file) +{ + dylib_command command; + std::vector<char> namebuf; + size_t namebufsize; + uint64_t pos; + + pos = file.Tell(); + /* Read command header, verify the command */ + file.Read(&command, sizeof(command)); + if (command.cmdsize < sizeof(dylib_command) || command.dylib.name.offset >= command.cmdsize) + throw std::runtime_error("Invalid dylib_command"); + + timestamp_ = command.dylib.timestamp; + current_version_ = command.dylib.current_version; + compatibility_version_ = command.dylib.compatibility_version; + namebufsize = command.cmdsize - command.dylib.name.offset; + namebuf.resize(namebufsize); + file.Seek(command.dylib.name.offset + pos); + file.Read(&namebuf[0], namebuf.size()); + name_ = NameToString(&namebuf[0], namebuf.size()); +} + +MacImportFunction *MacImport::Add(uint64_t address, uint8_t bind_type, size_t bind_offset, const std::string & name, uint32_t flags, int64_t addend, bool is_lazy, MacSymbol *symbol) +{ + MacImportFunction *import_function = new MacImportFunction(this, address, bind_type, bind_offset, name, flags, addend, is_lazy, symbol); + AddObject(import_function); + return import_function; +} + +void MacImport::WriteToFile(MacArchitecture &file) +{ + size_t size; + dylib_command command; + + command.cmd = is_weak_ ? LC_LOAD_WEAK_DYLIB : LC_LOAD_DYLIB; + command.cmdsize = (uint32_t)AlignValue(sizeof(command) + name_.size() + 1, OperandSizeToValue(file.cpu_address_size())); + command.dylib.name.offset = sizeof(command); + command.dylib.timestamp = timestamp_; + command.dylib.current_version = current_version_; + command.dylib.compatibility_version = compatibility_version_; + // write command + size = file.Write(&command, sizeof(command)); + size += file.Write(name_.c_str(), name_.size() + 1); + // align + uint8_t b = 0; + while (size < command.cmdsize) { + size += file.Write(&b, sizeof(b)); + } +} + +MacImportFunction *MacImport::item(size_t index) const +{ + return reinterpret_cast<MacImportFunction*>(BaseImport::item(index)); +} + +MacImportFunction *MacImport::GetFunctionByAddress(uint64_t address) const +{ + return reinterpret_cast<MacImportFunction*>(BaseImport::GetFunctionByAddress(address)); +} + +MacImportFunction *MacImport::Add(uint64_t address, APIType type, MapFunction *map_function) +{ + MacImportFunction *import_function = new MacImportFunction(this, address, type, map_function); + AddObject(import_function); + return import_function; +} + +/** + * MacImportList + */ + +MacImportList::MacImportList(MacArchitecture *owner) + : BaseImportList(owner) +{ + +} + +MacImportList::MacImportList(MacArchitecture *owner, const MacImportList &src) + : BaseImportList(owner, src) +{ + +} + +MacImportList *MacImportList::Clone(MacArchitecture *owner) const +{ + MacImportList *list = new MacImportList(owner, *this); + return list; +} + +MacImport *MacImportList::Add(int library_ordinal) +{ + MacImport *imp = new MacImport(this, library_ordinal); + AddObject(imp); + return imp; +} + +MacImport *MacImportList::AddSDK() +{ + MacImport *sdk = new MacImport(this, 0, true); + AddObject(sdk); + return sdk; +} + +MacImport *MacImportList::item(size_t index) const +{ + return reinterpret_cast<MacImport*>(IImportList::item(index)); +} + +MacImportFunction *MacImportList::GetFunctionByAddress(uint64_t address) const +{ + return reinterpret_cast<MacImportFunction *>(BaseImportList::GetFunctionByAddress(address)); +} + +MacImport *MacImportList::GetLibraryByOrdinal(int library_ordinal) const +{ + for (size_t i = 0; i < count(); i++) { + MacImport *import = item(i); + if (import->library_ordinal() == library_ordinal) + return import; + } + + return NULL; +} + +MacImport *MacImportList::GetImportByName(const std::string &name) const +{ + return reinterpret_cast<MacImport *>(BaseImportList::GetImportByName(name)); +} + +void MacImportList::ReadFromFile(MacArchitecture &file) +{ + size_t i, j; + + ILoadCommandList *command_list = file.command_list(); + int library_ordinal = 1; + size_t c = command_list->count(); + for (i = 0; i < c; i++) { + ILoadCommand *command = command_list->item(i); + if (command->type() == LC_LOAD_DYLIB || command->type() == LC_LOAD_WEAK_DYLIB) { + file.Seek(command->address()); + MacImport *import = Add(library_ordinal++); + import->ReadFromFile(file); + if (command->type() == LC_LOAD_WEAK_DYLIB) + import->set_is_weak(true); + } + } + + dyld_info_command *dyld_info = file.dyld_info(); + if (dyld_info->cmd) { + ReadBindInfo(file, dyld_info->bind_off, dyld_info->bind_size); + ReadLazyBindInfo(file, dyld_info->lazy_bind_off, dyld_info->lazy_bind_size); + } else { + MacIndirectSymbolList *indirect_symbol_list = file.indirect_symbol_list(); + for (i = 0; i < indirect_symbol_list->count(); i++) { + MacIndirectSymbol *indirect_symbol = indirect_symbol_list->item(i); + MacSymbol *symbol = indirect_symbol->symbol(); + if (symbol && (symbol->type() & (N_STAB | N_TYPE)) == N_UNDF) { + MacSection *section = file.section_list()->GetSectionByAddress(indirect_symbol->address()); + if (section && (section->type() == S_LAZY_SYMBOL_POINTERS + || section->type() == S_NON_LAZY_SYMBOL_POINTERS + || (section->type() == S_SYMBOL_STUBS && file.cpu_address_size() == osDWord && (section->flags() & S_ATTR_SELF_MODIFYING_CODE) && section->reserved2() == 5))) { + library_ordinal = symbol->library_ordinal(); + if (library_ordinal > 0) { + MacImport *import = GetLibraryByOrdinal(library_ordinal); + if (!import) + throw std::runtime_error("Invalid library ordinal"); + MacImportFunction *import_function = import->Add(indirect_symbol->address(), 0, 0, symbol->name(), 0, 0, section->type() != S_NON_LAZY_SYMBOL_POINTERS, symbol); + if (section->type() == S_SYMBOL_STUBS) + import_function->include_option(ioIsRelative); + } + } else if (section && section->type() != S_SYMBOL_STUBS) { + throw std::runtime_error("Invalid indirect symbol"); + } + } + } + + MacFixupList *fixup_list = file.fixup_list(); + for (i = 0; i < fixup_list->count(); i++) { + MacFixup *fixup = fixup_list->item(i); + MacSymbol *symbol = fixup->symbol(); + if (symbol && (symbol->type() & (N_STAB | N_TYPE)) == N_UNDF) { + library_ordinal = symbol->library_ordinal(); + if (library_ordinal > 0) { + MacImport *import = GetLibraryByOrdinal(library_ordinal); + if (!import) + throw std::runtime_error("Invalid library ordinal"); + import->Add(fixup->address(), 0, 0, symbol->name(), 0, 0, false, symbol); + } + } + } + } + + static const ImportInfo default_info[] = { + {atNone, "_exit", ioNoReturn, ctNone}, + {atNone, "_abort", ioNoReturn, ctNone}, + {atNone, "__Unwind_Resume", ioNoReturn, ctNone}, + {atNone, "__Unwind_Resume_or_Rethrow", ioNoReturn, ctNone}, + {atNone, "__Unwind_RaiseException", ioNoReturn, ctNone}, + {atNone, "___assert_rtn", ioNoReturn, ctNone}, + {atNone, "___stack_chk_fail", ioNoReturn, ctNone}, + {atNone, "___cxa_throw", ioNoReturn, ctNone}, + {atNone, "___cxa_end_cleanup", ioNoReturn, ctNone}, + {atNone, "___cxa_rethrow", ioNoReturn, ctNone}, + {atNone, "___cxa_bad_cast", ioNoReturn, ctNone}, + {atNone, "___cxa_bad_typeid", ioNoReturn, ctNone}, + {atNone, "___cxa_call_terminate", ioNoReturn, ctNone}, + {atNone, "___cxa_pure_virtual", ioNoReturn, ctNone}, + {atNone, "___cxa_call_unexpected", ioNoReturn, ctNone}, + {atNone, "__ZSt9terminatev", ioNoReturn, ctNone}, + {atNone, "@System@@Halt0$qqrv", ioNoReturn, ctNone}, + {atNone, "@System@@UnhandledException$qqrv", ioNoReturn, ctNone}, + }; + + MacImportFunction *func; + for (size_t k = 0; k < count(); k++) { + MacImport *import = item(k); + + std::string dll_name = os::ExtractFileName(import->name().c_str()); + std::transform(dll_name.begin(), dll_name.end(), dll_name.begin(), tolower); + std::string sdk_name = "libvmprotectsdk.dylib"; + + if (dll_name.compare(sdk_name) == 0) { + import->set_is_sdk(true); + if (import->count() == 0) { + if (MacImport *flat_lookup_import = GetLibraryByOrdinal(BIND_SPECIAL_DYLIB_FLAT_LOOKUP)) { + for (i = 0; i < flat_lookup_import->count(); ) { + func = flat_lookup_import->item(i); + if (GetSDKInfo(func->name())) { + func->set_owner(import); + continue; + } + i++; + } + } + } + + for (i = 0; i < import->count(); i++) { + func = import->item(i); + const ImportInfo *import_info = GetSDKInfo(func->name()); + if (import_info) { + func->set_type(import_info->type); + if (import_info->options & ioHasCompilationType) { + func->include_option(ioHasCompilationType); + func->set_compilation_type(import_info->compilation_type); + if (import_info->options & ioLockToKey) + func->include_option(ioLockToKey); + } + } + } + } else { + size_t c = _countof(default_info); + const ImportInfo *import_info = default_info; + + if (import_info) { + for (i = 0; i < import->count(); i++) { + func = import->item(i); + for (j = 0; j < c; j++) { + if (func->name().compare(import_info[j].name) == 0) { + func->set_type(import_info[j].type); + if (import_info[j].options & ioNative) + func->include_option(ioNative); + if (import_info[j].options & ioNoReturn) + func->include_option(ioNoReturn); + break; + } + } + } + } + } + } +} + +const ImportInfo *MacImportList::GetSDKInfo(const std::string &name) const +{ + if (!name.empty() && name[0] == '_') + return BaseImportList::GetSDKInfo(name.substr(1)); + return NULL; +} + +int MacImportList::GetMaxLibraryOrdinal() const +{ + int res = 0; + for (size_t i = 0; i < count(); i++) { + MacImport *import = item(i); + if (import->library_ordinal() <= 0) + continue; + + if (res < import->library_ordinal()) + res = import->library_ordinal(); + } + return res; +} + +void MacImportList::ReadBindInfo(MacArchitecture &file, uint32_t bind_off, uint32_t bind_size) +{ + size_t pos, seg_index = 0, i, c, skip, ptr_size; + uint8_t imm, opcode, bind_type = 0, sym_flags = 0; + int library_ordinal = 0; + bool done; + std::string sym_name; + int64_t addend = 0; + uint64_t addr, end_addr; + ISectionList *segment_list = file.segment_list(); + ISection *segment; + MacImport *import = NULL; + EncodedData buf; + MacSymbol *symbol = NULL; + + if (!bind_size) + return; + + if (segment_list->count() < 1) + throw std::runtime_error("Invalid segment count in bind info"); + + file.Seek(bind_off); + buf.ReadFromFile(file, bind_size); + + ptr_size = file.cpu_address_size() == osQWord ? 8 : 4; + + segment = segment_list->item(0); + addr = segment->address(); + end_addr = segment->address() + segment->size(); + + done = false; + for (pos = 0; !done && pos < bind_size; ) { + uint8_t b = buf.ReadByte(&pos); + imm = b & BIND_IMMEDIATE_MASK; + opcode = b & BIND_OPCODE_MASK; + switch (opcode) { + case BIND_OPCODE_DONE: + done = true; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + library_ordinal = imm; + import = NULL; + symbol = NULL; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + library_ordinal = static_cast<int>(buf.ReadUleb128(&pos)); + import = NULL; + symbol = NULL; + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + /* the special ordinals are negative numbers */ + if (imm == 0) { + library_ordinal = 0; + } else { + int8_t sign_extended = BIND_OPCODE_MASK | imm; + library_ordinal = sign_extended; + } + import = NULL; + symbol = NULL; + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + sym_flags = imm; + sym_name = buf.ReadString(&pos); + symbol = NULL; + break; + case BIND_OPCODE_SET_TYPE_IMM: + bind_type = imm; + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + addend = buf.ReadSleb128(&pos); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + seg_index = imm; + if (seg_index >= segment_list->count()) + throw std::runtime_error("Invalid segment index in bind info"); + segment = segment_list->item(seg_index); + addr = segment->address() + buf.ReadUleb128(&pos); + end_addr = segment->address() + segment->size(); + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + addr += buf.ReadUleb128(&pos); + break; + case BIND_OPCODE_DO_BIND: + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + if (addr >= end_addr) + throw std::runtime_error("Invalid binding address in bind info"); + if (!import) { + import = GetLibraryByOrdinal(library_ordinal); + if (!import) { + if (library_ordinal <= 0) { + // special library + import = Add(library_ordinal); + } + else { + throw std::runtime_error("Invalid library ordinal in bind info"); + } + } + } + if (!symbol) { + symbol = file.symbol_list()->GetSymbol(sym_name, library_ordinal); + if (!symbol) + throw std::runtime_error("Invalid symbol in bind info"); + } + import->Add(addr, bind_type, 0, sym_name, sym_flags, addend, false, symbol); + if (opcode == BIND_OPCODE_DO_BIND) { + addr += ptr_size; + } else if (opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) { + addr += buf.ReadUleb128(&pos) + ptr_size; + } else if (opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED) { + addr += (imm + 1) * ptr_size; + } + break; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + c = static_cast<size_t>(buf.ReadUleb128(&pos)); + skip = static_cast<size_t>(buf.ReadUleb128(&pos)); + for (i = 0; i < c; i++) { + if (addr >= end_addr) + throw std::runtime_error("Invalid binding address in bind info"); + if (!import) { + import = GetLibraryByOrdinal(library_ordinal); + if (!import) { + if (library_ordinal <= 0) { + // special library + import = Add(library_ordinal); + } + else { + throw std::runtime_error("Invalid library ordinal in bind info"); + } + } + } + if (!symbol) { + symbol = file.symbol_list()->GetSymbol(sym_name, library_ordinal); + if (!symbol) + throw std::runtime_error("Invalid symbol in bind info"); + } + import->Add(addr, bind_type, 0, sym_name, sym_flags, addend, false, symbol); + addr += skip + ptr_size; + } + break; + default: + throw std::runtime_error("Invalid bind opcode in bind info"); + } + } +} + +void MacImportList::ReadLazyBindInfo(MacArchitecture &file, uint32_t lazy_bind_off, uint32_t lazy_bind_size) +{ + uint8_t bind_type = BIND_TYPE_POINTER, imm, opcode, sym_flags = 0; + int library_ordinal = 0; + size_t pos, seg_index, ptrsize; + uint64_t addr, end_addr; + ISectionList *segment_list = file.segment_list(); + ISection *segment; + MacImport *import = NULL; + std::string sym_name; + int64_t addend = 0; + EncodedData buf; + MacSymbol *symbol = NULL; + + if (!lazy_bind_size) + return; + + if (segment_list->count() < 1) + throw std::runtime_error("Invalid segment count in lazy bind info"); + + file.Seek(lazy_bind_off); + buf.ReadFromFile(file, lazy_bind_size); + + ptrsize = OperandSizeToValue(file.cpu_address_size()); + + segment = segment_list->item(0); + addr = segment->address(); + end_addr = segment->address() + segment->size(); + + size_t bind_offset = (size_t)-1; + for (pos = 0; pos < lazy_bind_size; ) { + if (bind_offset == (size_t)-1) + bind_offset = pos; + uint8_t b = buf.ReadByte(&pos); + imm = b & BIND_IMMEDIATE_MASK; + opcode = b & BIND_OPCODE_MASK; + switch (opcode) { + case BIND_OPCODE_DONE: + /* There is BIND_OPCODE_DONE at end of each lazy bind, + * don't stop until end of whole sequence. */ + bind_offset = (size_t)-1; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + library_ordinal = imm; + import = NULL; + symbol = NULL; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + library_ordinal = static_cast<int>(buf.ReadUleb128(&pos)); + import = NULL; + symbol = NULL; + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + /* the special ordinals are negative numbers */ + if (imm == 0) { + library_ordinal = 0; + } else { + int8_t sign_extended = BIND_OPCODE_MASK | imm; + library_ordinal = sign_extended; + } + import = NULL; + symbol = NULL; + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + sym_flags = imm; + sym_name = buf.ReadString(&pos); + symbol = NULL; + break; + case BIND_OPCODE_SET_TYPE_IMM: + bind_type = imm; + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + addend = buf.ReadSleb128(&pos); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + seg_index = imm; + if (seg_index >= segment_list->count()) + throw std::runtime_error("Invalid segment index in lazy bind info"); + segment = segment_list->item(seg_index); + addr = segment->address() + buf.ReadUleb128(&pos); + end_addr = segment->address() + segment->size(); + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + addr += buf.ReadUleb128(&pos); + break; + case BIND_OPCODE_DO_BIND: + if (addr >= end_addr) + throw std::runtime_error("Invalid binding address in lazy bind info"); + if (!import) { + import = GetLibraryByOrdinal(library_ordinal); + if (!import) { + if (library_ordinal <= 0) { + // special library + import = Add(library_ordinal); + } + else { + throw std::runtime_error("Invalid library ordinal in lazy bind info"); + } + } + } + if (!symbol) { + symbol = file.symbol_list()->GetSymbol(sym_name, library_ordinal); + if (!symbol) + throw std::runtime_error("Invalid symbol in lazy bind info"); + } + import->Add(addr, bind_type, bind_offset, sym_name, sym_flags, addend, true, symbol); + addr += ptrsize; + break; + default: + throw std::runtime_error("Invalid bind opcode in lazy bind info"); + } + } +} + +void MacImportList::Pack() +{ + if (!has_sdk()) + return; + + size_t i, j; + MacImport *import; + uint8_t library_ordinal = 1; + + for (i = 0; i < count(); i++) { + import = item(i); + if (import->is_sdk() || import->library_ordinal() <= 0) + continue; + + import->set_library_ordinal(library_ordinal); + for (j = 0; j < import->count(); j++) { + import->item(j)->symbol()->set_library_ordinal(library_ordinal); + } + library_ordinal++; + } + + for (i = count(); i > 0; i--) { + import = item(i - 1); + if (!import->is_sdk()) + continue; + + for (j = 0; j < import->count(); j++) { + import->item(j)->symbol()->set_deleted(true); + } + delete import; + } +} + +void MacImportList::WriteToFile(MacArchitecture &file) +{ + for (size_t i = 0; i < count(); i++) { + MacImport *import = item(i); + if (import->library_ordinal() <= 0) + continue; + + file.command_list()->Add(import->is_weak() ? LC_LOAD_WEAK_DYLIB : LC_LOAD_DYLIB, import); + } + + dyld_info_command *dyld_info = file.dyld_info(); + if (!dyld_info->cmd) + return; + + dyld_info->bind_off = static_cast<uint32_t>(file.Tell()); + dyld_info->bind_size = static_cast<uint32_t>(WriteBindInfo(file)); + if (!dyld_info->bind_size) + dyld_info->bind_off = 0; + + dyld_info->weak_bind_off = static_cast<uint32_t>(file.Tell()); + dyld_info->weak_bind_size = static_cast<uint32_t>(WriteWeakBindInfo(file)); + if (!dyld_info->weak_bind_size) + dyld_info->weak_bind_off = 0; + + dyld_info->lazy_bind_off = static_cast<uint32_t>(file.Tell()); + dyld_info->lazy_bind_size = static_cast<uint32_t>(WriteLazyBindInfo(file)); + if (!dyld_info->lazy_bind_size) + dyld_info->lazy_bind_off = 0; +} + +struct BindingInfo { + uint8_t opcode; + uint64_t operand1; + uint64_t operand2; + std::string name; + BindingInfo(uint8_t opcode_, uint64_t operand1_, uint64_t operand2_ = 0, const std::string &name_ = "") + { + opcode = opcode_; + operand1 = operand1_; + operand2 = operand2_; + name = name_; + } +}; + +size_t MacImportList::WriteBindInfo(MacArchitecture &file) +{ + size_t i, j; + std::vector<MacImportFunction *> info; + MacImport *import; + MacImportFunction *import_func; + + for (i = 0; i < count(); i++) { + import = item(i); + for (j = 0; j < import->count(); j++) { + import_func = import->item(j); + if (import_func->is_lazy()) + continue; + info.push_back(import_func); + } + } + if (info.size() == 0) + return 0; + + std::sort(info.begin(), info.end(), BindInfoHelper()); + + std::vector<BindingInfo> mid; + MacSegment *segment = NULL; + int library_ordinal = 0x80000000; + std::string symbol_name; + uint8_t bind_type = 0; + uint64_t address = -1; + int64_t addend = 0; + size_t ptrsize = OperandSizeToValue(file.cpu_address_size()); + + for (std::vector<MacImportFunction *>::iterator it = info.begin(); it != info.end() ; ++it) { + import_func = *it; + if (library_ordinal != import_func->library_ordinal()) { + if (import_func->library_ordinal() <= 0) { + // special lookups are encoded as negative numbers in BindingInfo + mid.push_back(BindingInfo(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, import_func->library_ordinal())); + } else { + mid.push_back(BindingInfo(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB, import_func->library_ordinal())); + } + library_ordinal = import_func->library_ordinal(); + } + + if (symbol_name != import_func->name()) { + mid.push_back(BindingInfo(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM, import_func->flags(), 0, import_func->name())); + symbol_name = import_func->name(); + } + if (bind_type != import_func->bind_type()) { + mid.push_back(BindingInfo(BIND_OPCODE_SET_TYPE_IMM, import_func->bind_type())); + bind_type = import_func->bind_type(); + } + if (address != import_func->address()) { + if (!segment || (import_func->address() < segment->address()) || (import_func->address() >= segment->address() + segment->size()) || import_func->address() < address) { + segment = file.segment_list()->GetSectionByAddress(import_func->address()); + if (!segment) + throw std::runtime_error("Invalid binding address in bind info"); + mid.push_back(BindingInfo(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB, file.segment_list()->IndexOf(segment), import_func->address() - segment->address())); + } else { + mid.push_back(BindingInfo(BIND_OPCODE_ADD_ADDR_ULEB, import_func->address() - address)); + } + address = import_func->address(); + } + if (addend != import_func->addend()) { + mid.push_back(BindingInfo(BIND_OPCODE_SET_ADDEND_SLEB, import_func->addend())); + addend = import_func->addend(); + } + mid.push_back(BindingInfo(BIND_OPCODE_DO_BIND, 0)); + address += ptrsize; + } + mid.push_back(BindingInfo(BIND_OPCODE_DONE, 0)); + + // optimize phase 1, combine bind/add pairs + BindingInfo *dst = &mid[0]; + for (const BindingInfo *src = &mid[0]; src->opcode != BIND_OPCODE_DONE; ++src) { + if ((src->opcode == BIND_OPCODE_DO_BIND) + && (src[1].opcode == BIND_OPCODE_ADD_ADDR_ULEB)) { + dst->opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB; + dst->operand1 = src[1].operand1; + ++src; + ++dst; + } else { + *dst++ = *src; + } + } + dst->opcode = BIND_OPCODE_DONE; + + // optimize phase 2, compress packed runs of BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB with + // same addr delta into one BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB + dst = &mid[0]; + for (const BindingInfo *src = &mid[0]; src->opcode != BIND_OPCODE_DONE; ++src) { + uint64_t delta = src->operand1; + if ((src->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (src[1].opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (src[1].operand1 == delta)) { + // found at least two in a row, this is worth compressing + dst->opcode = BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB; + dst->operand1 = 1; + dst->operand2 = delta; + ++src; + while ((src->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (src->operand1 == delta)) { + dst->operand1++; + ++src; + } + --src; + ++dst; + } else { + *dst++ = *src; + } + } + dst->opcode = BIND_OPCODE_DONE; + + // optimize phase 3, use immediate encodings + for (BindingInfo *p = &mid[0]; p->opcode != REBASE_OPCODE_DONE; ++p) { + if ((p->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (p->operand1 < (15 * ptrsize)) + && ((p->operand1 % ptrsize) == 0)) { + p->opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED; + p->operand1 = p->operand1 / ptrsize; + } else if ((p->opcode == BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB) && (p->operand1 <= 15)) { + p->opcode = BIND_OPCODE_SET_DYLIB_ORDINAL_IMM; + } + } + dst->opcode = BIND_OPCODE_DONE; //-V519 + + // convert to compressed encoding + EncodedData data; + data.reserve(info.size() * 2); + bool done = false; + for (std::vector<BindingInfo>::iterator it = mid.begin(); !done && it != mid.end() ; ++it) { + switch ( it->opcode ) { + case BIND_OPCODE_DONE: + done = true; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + data.WriteByte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | static_cast<uint8_t>(it->operand1)); + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + data.WriteByte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB); + data.WriteUleb128(it->operand1); + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + data.WriteByte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | (it->operand1 & BIND_IMMEDIATE_MASK)); + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + data.WriteByte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | static_cast<uint8_t>(it->operand1)); + data.WriteString(it->name); + break; + case BIND_OPCODE_SET_TYPE_IMM: + data.WriteByte(BIND_OPCODE_SET_TYPE_IMM | static_cast<uint8_t>(it->operand1)); + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + data.WriteByte(BIND_OPCODE_SET_ADDEND_SLEB); + data.WriteSleb128(it->operand1); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + data.WriteByte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | static_cast<uint8_t>(it->operand1)); + data.WriteUleb128(it->operand2); + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + data.WriteByte(BIND_OPCODE_ADD_ADDR_ULEB); + data.WriteUleb128(it->operand1); + break; + case BIND_OPCODE_DO_BIND: + data.WriteByte(BIND_OPCODE_DO_BIND); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + data.WriteByte(BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB); + data.WriteUleb128(it->operand1); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + data.WriteByte(BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED | static_cast<uint8_t>(it->operand1)); + break; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + data.WriteByte(BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB); + data.WriteUleb128(it->operand1); + data.WriteUleb128(it->operand2); + break; + } + } + data.resize(AlignValue(data.size(), ptrsize), 0); + + return (data.size() == 0) ? 0 : file.Write(data.data(), data.size()); +} + +size_t MacImportList::WriteWeakBindInfo(MacArchitecture &file) +{ + size_t i, j; + std::vector<MacImportFunction *> info; + std::vector<BindingInfo> mid; + MacSegment *segment = NULL; + std::string symbol_name; + uint8_t bind_type = 0; + uint64_t address = -1; + int64_t addend = 0; + size_t ptrsize = OperandSizeToValue(file.cpu_address_size()); + MacImport *import; + MacImportFunction *import_func; + + for (i = 0; i < count(); i++) { + import = item(i); + for (j = 0; j < import->count(); j++) { + import_func = import->item(j); + if (!import_func->is_weak()) + continue; + info.push_back(import_func); + } + } + if (info.size() == 0) { + // short circuit if no weak binding needed + return 0; + } + std::sort(info.begin(), info.end(), WeakBindInfoHelper()); + + for (std::vector<MacImportFunction *>::const_iterator it = info.begin(); it != info.end(); ++it) { + import_func = *it; + if (symbol_name != import_func->name()) { + mid.push_back(BindingInfo(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM, import_func->flags(), 0, import_func->name())); + symbol_name = import_func->name(); + } + // non-weak symbols just have BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM + // weak symbols have SET_SEG, ADD_ADDR, SET_ADDED, DO_BIND + if (import_func->bind_type() != BIND_TYPE_OVERRIDE_OF_WEAKDEF_IN_DYLIB) { + if (bind_type != import_func->bind_type()) { + mid.push_back(BindingInfo(BIND_OPCODE_SET_TYPE_IMM, import_func->bind_type())); + bind_type = import_func->bind_type(); + } + if (address != import_func->address()) { + if (!segment || (import_func->address() < segment->address()) || (import_func->address() >= segment->address() + segment->size())) { + segment = file.segment_list()->GetSectionByAddress(import_func->address()); + if (!segment) + throw std::runtime_error("binding address outside range of any segment"); + mid.push_back(BindingInfo(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB, file.segment_list()->IndexOf(segment), import_func->address() - segment->address())); + } + else { + mid.push_back(BindingInfo(BIND_OPCODE_ADD_ADDR_ULEB, import_func->address() - address)); + } + address = import_func->address(); + } + if (addend != import_func->addend()) { + mid.push_back(BindingInfo(BIND_OPCODE_SET_ADDEND_SLEB, import_func->addend())); + addend = import_func->addend(); + } + mid.push_back(BindingInfo(BIND_OPCODE_DO_BIND, 0)); + address += ptrsize; + } + } + mid.push_back(BindingInfo(BIND_OPCODE_DONE, 0)); + + // optimize phase 1, combine bind/add pairs + BindingInfo *dst = &mid[0]; + for (const BindingInfo *src = &mid[0]; src->opcode != BIND_OPCODE_DONE; ++src) { + if ( (src->opcode == BIND_OPCODE_DO_BIND) + && (src[1].opcode == BIND_OPCODE_ADD_ADDR_ULEB) ) { + dst->opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB; + dst->operand1 = src[1].operand1; + ++src; + ++dst; + } + else { + *dst++ = *src; + } + } + dst->opcode = BIND_OPCODE_DONE; + + // optimize phase 2, compress packed runs of BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB with + // same addr delta into one BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB + dst = &mid[0]; + for (const BindingInfo *src = &mid[0]; src->opcode != BIND_OPCODE_DONE; ++src) { + uint64_t delta = src->operand1; + if ( (src->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (src[1].opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (src[1].operand1 == delta) ) { + // found at least two in a row, this is worth compressing + dst->opcode = BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB; + dst->operand1 = 1; + dst->operand2 = delta; + ++src; + while ( (src->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (src->operand1 == delta) ) { + dst->operand1++; + ++src; + } + --src; + ++dst; + } + else { + *dst++ = *src; + } + } + dst->opcode = BIND_OPCODE_DONE; + + // optimize phase 3, use immediate encodings + for (BindingInfo *p = &mid[0]; p->opcode != REBASE_OPCODE_DONE; ++p) { + if ( (p->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (p->operand1 < (15 * ptrsize)) + && ((p->operand1 % ptrsize) == 0) ) { + p->opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED; + p->operand1 = p->operand1 / ptrsize; + } + } + dst->opcode = BIND_OPCODE_DONE; //-V519 + + // convert to compressed encoding + EncodedData data; + data.reserve(info.size() * 2); + bool done = false; + for (std::vector<BindingInfo>::iterator it = mid.begin(); !done && it != mid.end(); ++it) { + switch (it->opcode) { + case BIND_OPCODE_DONE: + data.WriteByte(BIND_OPCODE_DONE); + done = true; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + data.WriteByte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | static_cast<uint8_t>(it->operand1)); + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + data.WriteByte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB); + data.WriteUleb128(it->operand1); + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + data.WriteByte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | (it->operand1 & BIND_IMMEDIATE_MASK)); + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + data.WriteByte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | static_cast<uint8_t>(it->operand1)); + data.WriteString(it->name); + break; + case BIND_OPCODE_SET_TYPE_IMM: + data.WriteByte(BIND_OPCODE_SET_TYPE_IMM | static_cast<uint8_t>(it->operand1)); + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + data.WriteByte(BIND_OPCODE_SET_ADDEND_SLEB); + data.WriteSleb128(it->operand1); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + data.WriteByte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | static_cast<uint8_t>(it->operand1)); + data.WriteUleb128(it->operand2); + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + data.WriteByte(BIND_OPCODE_ADD_ADDR_ULEB); + data.WriteUleb128(it->operand1); + break; + case BIND_OPCODE_DO_BIND: + data.WriteByte(BIND_OPCODE_DO_BIND); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + data.WriteByte(BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB); + data.WriteUleb128(it->operand1); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + data.WriteByte(BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED | static_cast<uint8_t>(it->operand1)); + break; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + data.WriteByte(BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB); + data.WriteUleb128(it->operand1); + data.WriteUleb128(it->operand2); + break; + } + } + data.resize(AlignValue(data.size(), ptrsize), 0); + + return (data.size() == 0) ? 0 : file.Write(data.data(), data.size()); +} + +size_t MacImportList::WriteLazyBindInfo(MacArchitecture &file) +{ + std::vector<MacImportFunction *> info; + MacImport *import; + MacImportFunction *import_function; + + for (size_t i = 0; i < count(); i++) { + import = item(i); + for (size_t j = 0; j < import->count(); j++) { + import_function = import->item(j); + if (!import_function->is_lazy()) + continue; + info.push_back(import_function); + } + } + if (info.empty()) + return 0; + + std::sort(info.begin(), info.end(), LazyBindInfoHelper()); + + size_t ptrsize = OperandSizeToValue(file.cpu_address_size()); + EncodedData data; + for (std::vector<MacImportFunction *>::iterator it = info.begin(); it != info.end() ; ++it) { + import_function = *it; + + if (data.size() > import_function->bind_offset()) + throw std::runtime_error("binding offset out of range"); + for (size_t i = data.size(); i < import_function->bind_offset(); i++) { + data.WriteByte(BIND_OPCODE_DONE); + } + + // write address to bind + MacSegment *segment = file.segment_list()->GetSectionByAddress(import_function->address()); + if (!segment) + throw std::runtime_error("lazy binding address outside range of any segment"); + data.WriteByte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | static_cast<uint8_t>(file.segment_list()->IndexOf(segment))); + data.WriteUleb128(import_function->address() - segment->address()); + + // write ordinal + if (import_function->library_ordinal() <= 0) { + // special lookups are encoded as negative numbers in BindingInfo + data.WriteByte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | (import_function->library_ordinal() & BIND_IMMEDIATE_MASK) ); + } else if (import_function->library_ordinal() <= 15 ) { + // small ordinals are encoded in opcode + data.WriteByte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | import_function->library_ordinal()); + } else { + data.WriteByte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB); + data.WriteUleb128(import_function->library_ordinal()); + } + // write symbol name + data.WriteByte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | import_function->flags()); + data.WriteString(import_function->name()); + // write do bind + data.WriteByte(BIND_OPCODE_DO_BIND); + data.WriteByte(BIND_OPCODE_DONE); + } + data.resize(AlignValue(data.size(), ptrsize), 0); + + return (data.size() == 0) ? 0 : file.Write(data.data(), data.size()); +} + +void MacImportList::RebaseBindInfo(MacArchitecture &file, size_t delta_base) +{ + std::vector<MacImportFunction *> info; + MacImport *import; + MacImportFunction *import_function; + + for (size_t i = 0; i < count(); i++) { + import = item(i); + for (size_t j = 0; j < import->count(); j++) { + import_function = import->item(j); + if (!import_function->is_lazy()) + continue; + info.push_back(import_function); + } + } + if (info.empty()) + return; + + std::sort(info.begin(), info.end(), LazyBindInfoHelper()); + + size_t value_size = OperandSizeToValue(file.cpu_address_size()); + for (std::vector<MacImportFunction *>::iterator it = info.begin(); it != info.end() ; ++it) { + import_function = *it; + import_function->set_bind_offset(import_function->bind_offset() + delta_base); + delta_base += 4; + + file.AddressSeek(import_function->address()); + uint64_t value = 0; + assert(sizeof(value) >= value_size); + file.Read(&value, value_size); + if (value && file.AddressSeek(value)) { + if (file.ReadByte() != 0x68) // push XXXX + throw std::runtime_error("Runtime error at RebaseBindInfo"); + file.WriteDWord(static_cast<uint32_t>(import_function->bind_offset())); + } + } +} + +/** + * MacIndirectSymbol + */ + +MacIndirectSymbol::MacIndirectSymbol(MacIndirectSymbolList *owner, uint64_t address, uint32_t value, MacSymbol *symbol) + : IObject(), owner_(owner), address_(address), value_(value), symbol_(symbol) +{ + +} + +MacIndirectSymbol::MacIndirectSymbol(MacIndirectSymbolList *owner, const MacIndirectSymbol &src) + : IObject(src), owner_(owner) +{ + address_ = src.address_; + value_ = src.value_; + symbol_ = src.symbol_; +} + +MacIndirectSymbol::~MacIndirectSymbol() +{ + if (owner_) + owner_->RemoveObject(this); +} + +MacIndirectSymbol *MacIndirectSymbol::Clone(MacIndirectSymbolList *owner) const +{ + MacIndirectSymbol *symbol = new MacIndirectSymbol(owner, *this); + return symbol; +} + +void MacIndirectSymbol::Rebase(uint64_t delta_base) +{ + if (address_) + address_ += delta_base; +} + +/** + * MacIndirectSymbolList + */ + +MacIndirectSymbolList::MacIndirectSymbolList() + : ObjectList<MacIndirectSymbol>() +{ + +} + +MacIndirectSymbolList::MacIndirectSymbolList(const MacIndirectSymbolList &src) + : ObjectList<MacIndirectSymbol>(src) +{ + for (size_t i = 0; i < src.count(); i++) { + AddObject(src.item(i)->Clone(this)); + } +} + +MacIndirectSymbolList *MacIndirectSymbolList::Clone() const +{ + MacIndirectSymbolList *list = new MacIndirectSymbolList(*this); + return list; +} + +MacIndirectSymbol *MacIndirectSymbolList::Add(uint64_t address, uint32_t value, MacSymbol *symbol) +{ + MacIndirectSymbol *sym = new MacIndirectSymbol(this, address, value, symbol); + AddObject(sym); + return sym; +} + +MacIndirectSymbol *MacIndirectSymbolList::GetSymbol(MacSymbol *symbol) const +{ + for (size_t i = 0; i < count(); i++) { + MacIndirectSymbol *indirect_symbol = item(i); + if (indirect_symbol->symbol() == symbol) + return indirect_symbol; + } + return NULL; +} + +void MacIndirectSymbolList::ReadFromFile(MacArchitecture &file) +{ + dysymtab_command *dysymtab = file.dysymtab(); + if (!dysymtab->cmd) + return; + + file.Seek(dysymtab->indirectsymoff); + uint32_t ptr_size = OperandSizeToValue(file.cpu_address_size()); + + for (size_t i = 0; i < dysymtab->nindirectsyms; i++) { + uint32_t value = file.ReadDWord(); + + uint64_t address = 0; + for (size_t j = 0; j < file.section_list()->count(); j++) { + MacSection *section = file.section_list()->item(j); + if (section->type() == S_SYMBOL_STUBS || section->type() == S_LAZY_SYMBOL_POINTERS || section->type() == S_NON_LAZY_SYMBOL_POINTERS || section->type() == S_THREAD_LOCAL_VARIABLE_POINTERS) { + uint32_t value_size = (section->type() == S_SYMBOL_STUBS) ? section->reserved2() : ptr_size; + if (i >= section->reserved1() && i < section->reserved1() + section->size() / value_size) { + address = section->address() + (i - section->reserved1()) * value_size; + break; + } + } + } + + if (!address) + throw std::runtime_error("Invalid indirect symbol address"); + + MacSymbol *symbol; + if (value & (INDIRECT_SYMBOL_LOCAL | INDIRECT_SYMBOL_ABS)) { + symbol = NULL; + } else { + if (value >= file.symbol_list()->count()) + throw std::runtime_error("Invalid indirect symbol value"); + symbol = file.symbol_list()->item(value); + } + + Add(address, value, symbol); + } +} + +void MacIndirectSymbolList::Pack() +{ + for (size_t i = 0; i < count(); i++) { + MacIndirectSymbol *indirect_symbol = item(i); + MacSymbol *symbol = indirect_symbol->symbol(); + if (symbol && symbol->is_deleted()) { + indirect_symbol->set_symbol(NULL); + indirect_symbol->set_value(INDIRECT_SYMBOL_ABS); + } + } +} + +void MacIndirectSymbolList::WriteToFile(MacArchitecture &file) +{ + dysymtab_command *dysymtab = file.dysymtab(); + + dysymtab->nindirectsyms = static_cast<uint32_t>(count()); + if (!dysymtab->nindirectsyms) { + dysymtab->indirectsymoff = 0; + return; + } + + dysymtab->indirectsymoff = static_cast<uint32_t>(file.Tell()); + for (size_t i = 0; i < count(); i++) { + MacIndirectSymbol *indirect_symbol = item(i); + uint32_t value = (indirect_symbol->symbol()) ? (uint32_t)file.symbol_list()->IndexOf(indirect_symbol->symbol()) : indirect_symbol->value(); + file.Write(&value, sizeof(value)); + } +} + +void MacIndirectSymbolList::Rebase(uint64_t delta_base) +{ + for (size_t i = 0; i < count(); i++) { + item(i)->Rebase(delta_base); + } +} + +/** + * MacExtRefSymbol + */ + +MacExtRefSymbol::MacExtRefSymbol(MacExtRefSymbolList *owner, MacSymbol *symbol, uint8_t flags) + : IObject(), owner_(owner), flags_(flags), symbol_(symbol) +{ + +} + +MacExtRefSymbol::MacExtRefSymbol(MacExtRefSymbolList *owner, const MacExtRefSymbol &src) + : IObject(src), owner_(owner) +{ + flags_ = src.flags_; + symbol_ = src.symbol_; +} + +MacExtRefSymbol::~MacExtRefSymbol() +{ + if (owner_) + owner_->RemoveObject(this); +} + +MacExtRefSymbol *MacExtRefSymbol::Clone(MacExtRefSymbolList *owner) const +{ + MacExtRefSymbol *symbol = new MacExtRefSymbol(owner, *this); + return symbol; +} + +/** + * MacExtRefSymbolList + */ + +MacExtRefSymbolList::MacExtRefSymbolList() + : ObjectList<MacExtRefSymbol>() +{ + +} + +MacExtRefSymbolList::MacExtRefSymbolList(const MacExtRefSymbolList &src) + : ObjectList<MacExtRefSymbol>(src) +{ + for (size_t i = 0; i < src.count(); i++) { + AddObject(src.item(i)->Clone(this)); + } +} + +MacExtRefSymbolList *MacExtRefSymbolList::Clone() const +{ + MacExtRefSymbolList *list = new MacExtRefSymbolList(*this); + return list; +} + +MacExtRefSymbol *MacExtRefSymbolList::Add(MacSymbol *symbol, uint8_t flags) +{ + MacExtRefSymbol *sym = new MacExtRefSymbol(this, symbol, flags); + AddObject(sym); + return sym; +} + +MacExtRefSymbol *MacExtRefSymbolList::GetSymbol(MacSymbol *symbol) const +{ + for (size_t i = 0; i < count(); i++) { + MacExtRefSymbol *ext_ref_symbol = item(i); + if (ext_ref_symbol->symbol() == symbol) + return ext_ref_symbol; + } + return NULL; +} + +void MacExtRefSymbolList::ReadFromFile(MacArchitecture &file) +{ + dysymtab_command *dysymtab = file.dysymtab(); + if (!dysymtab->cmd) + return; + + file.Seek(dysymtab->extrefsymoff); + + for (size_t i = 0; i < dysymtab->nextrefsyms; i++) { + dylib_reference ref; + file.Read(&ref, sizeof(ref)); + + if (ref.isym >= file.symbol_list()->count()) + throw std::runtime_error("Invalid extern reference symbol value"); + + Add(file.symbol_list()->item(ref.isym), ref.flags); + } +} + +void MacExtRefSymbolList::Pack() +{ + for (size_t i = count(); i > 0; i--) { + MacExtRefSymbol *ext_ref_symbol = item(i - 1); + if (ext_ref_symbol->symbol()->is_deleted()) + delete ext_ref_symbol; + } +} + +void MacExtRefSymbolList::WriteToFile(MacArchitecture &file) +{ + dysymtab_command *dysymtab = file.dysymtab(); + + dysymtab->nextrefsyms = static_cast<uint32_t>(count()); + if (!dysymtab->nextrefsyms) { + dysymtab->extrefsymoff = 0; + return; + } + + dysymtab->extrefsymoff = static_cast<uint32_t>(file.Tell()); + for (size_t i = 0; i < count(); i++) { + MacExtRefSymbol *ext_ref_symbol = item(i); + dylib_reference ref; + ref.isym = file.symbol_list()->IndexOf(ext_ref_symbol->symbol()); + ref.flags = ext_ref_symbol->flags(); + file.Write(&ref, sizeof(ref)); + } +} + +/** + * MacExport + */ + +MacExport::MacExport(IExportList *parent, uint64_t address, const std::string &name, uint64_t flags, uint64_t other) + : BaseExport(parent), symbol_(NULL), address_(address), name_(name), flags_(flags), other_(other) +{ + +} + +MacExport::MacExport(IExportList *parent, MacSymbol *symbol) + : BaseExport(parent), symbol_(symbol), address_(0), flags_(0), other_(0) +{ + if (symbol_) { + address_ = symbol_->value(); + name_ = symbol_->name(); + } +} + +MacExport::MacExport(IExportList *parent, const MacExport &src) + : BaseExport(parent, src) +{ + address_ = src.address_; + name_ = src.name_; + forwarded_name_ = src.forwarded_name_; + flags_ = src.flags_; + other_ = src.other_; + symbol_ = src.symbol_; +} + +MacExport *MacExport::Clone(IExportList *parent) const +{ + MacExport *exp = new MacExport(parent, *this); + return exp; +} + +void MacExport::set_address(uint64_t address) +{ + address_ = address; + if (symbol_) + symbol_->set_value(address); +} + +void MacExport::Rebase(uint64_t delta_base) +{ + address_ += delta_base; +} + +std::string MacExport::display_name(bool show_ret) const +{ + return DemangleName(name_).display_name(show_ret); +} + +/** + * MacExportList + */ + +MacExportList::MacExportList(MacArchitecture *owner) + : BaseExportList(owner) +{ + +} + +MacExportList::MacExportList(MacArchitecture *owner, const MacExportList &src) + : BaseExportList(owner, src) +{ + name_ = src.name_; +} + +MacExportList *MacExportList::Clone(MacArchitecture *owner) const +{ + MacExportList *list = new MacExportList(owner, *this); + return list; +} + +MacExport *MacExportList::item(size_t index) const +{ + return reinterpret_cast<MacExport *>(IExportList::item(index)); +} + +MacExport *MacExportList::Add(uint64_t address, const std::string & name, uint64_t flags, uint64_t other) +{ + MacExport *exp = new MacExport(this, address, name, flags, other); + AddObject(exp); + return exp; +} + +MacExport *MacExportList::Add(MacSymbol *symbol) +{ + MacExport *exp = new MacExport(this, symbol); + AddObject(exp); + return exp; +} + +void MacExportList::ParseExportNode(const EncodedData &buf, size_t pos, const std::string &name, uint64_t base_address) +{ + uint8_t terminal_size, children_count; + uint64_t address, flags, other; + size_t i, children_pos; + std::string children_name, import_name; + uint32_t childNodeOffset; + MacExport *exp; + + terminal_size = static_cast<uint8_t>(buf.ReadUleb128(&pos)); + children_pos = pos + terminal_size; + if (terminal_size) { + flags = buf.ReadUleb128(&pos); + + if (flags & EXPORT_SYMBOL_FLAGS_REEXPORT) { + address = 0; + other = buf.ReadUleb128(&pos); + import_name = buf.ReadString(&pos); + } else { + address = base_address + buf.ReadUleb128(&pos); + other = (flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) ? buf.ReadUleb128(&pos) : 0; + } + + exp = Add(address, name, flags, other); + if (import_name.size() > 0) + exp->set_forwarded_name(import_name); + } + + children_count = buf[children_pos++]; + for (i = 0; i < children_count; i++) { + children_name = name + buf.ReadString(&children_pos); + childNodeOffset = static_cast<uint32_t>(buf.ReadUleb128(&children_pos)); + ParseExportNode(buf, childNodeOffset, children_name, base_address); + } +} + +void MacExportList::ReadFromFile(MacArchitecture &file) +{ + dyld_info_command *dyld_info = file.dyld_info(); + + ILoadCommand *command = file.command_list()->GetCommandByType(LC_ID_DYLIB); + if (command) { + dylib_command dylib; + + file.Seek(command->address()); + file.Read(&dylib, sizeof(dylib)); + + file.Seek(command->address() + dylib.dylib.name.offset); + name_ = file.ReadString(); + } + + if (dyld_info->cmd) { + if (!dyld_info->export_size) + return; + + MacSegment *base_segment = file.segment_list()->GetBaseSegment(); + if (!base_segment) + throw std::runtime_error("Invalid base segment for export info"); + + EncodedData buf; + file.Seek(dyld_info->export_off); + buf.ReadFromFile(file, dyld_info->export_size); + ParseExportNode(buf, 0, "", base_segment->address()); + + std::map<uint64_t, MacExport *> address_map; + for (size_t i = 0; i < count(); i++) { + MacExport *exp = item(i); + if (!exp->address()) + continue; + address_map[exp->address()] = exp; + } + + MacSymbolList *symbol_list = file.symbol_list(); + for (size_t i = 0; i < symbol_list->count(); i++) { + MacSymbol *symbol = symbol_list->item(i); + + if (symbol->value()) { + std::map<uint64_t, MacExport *>::const_iterator it = address_map.find(symbol->value()); + if (it != address_map.end()) + it->second->set_symbol(symbol); + } + } + } else { + MacSymbolList *symbol_list = file.symbol_list(); + for (size_t i = 0; i < symbol_list->count(); i++) { + MacSymbol *symbol = symbol_list->item(i); + if ((symbol->type() & (N_STAB | N_TYPE | N_EXT)) == (N_SECT | N_EXT)) + Add(symbol); + } + } +} + +void MacExportList::Pack() +{ + for (size_t i = count(); i > 0 ; i--) { + MacExport *exp = item(i - 1); + if (exp->symbol() && exp->symbol()->is_deleted()) + delete exp; + } +} + +void MacExportList::WriteToFile(MacArchitecture &file) +{ + dyld_info_command *dyld_info = file.dyld_info(); + if (!dyld_info->cmd) + return; + + if (count() == 0) { + dyld_info->export_size = 0; + dyld_info->export_off = 0; + return; + } + + size_t i, j; + MacExportNode root_node; + MacExportNode *node; + EncodedData data; + std::vector<MacExportNode *> stack; + + for (i = 0; i < count(); i++) { + root_node.AddSymbol(item(i)); + } + + stack.push_back(&root_node); + for (i = 0; i < stack.size(); i++) { + node = stack[i]; + for (j = 0; j < node->count(); j++) { + stack.push_back(node->item(j)); + } + } + + uint64_t base_address = file.segment_list()->GetBaseSegment()->address(); + + bool more; + do { + more = false; + data.clear(); + for (i = 0; i < stack.size(); i++) { + node = stack[i]; + if (node->offset() != data.size()) + more = true; + stack[i]->WriteToData(data, base_address); + } + } while (more); + data.resize(AlignValue(data.size(), OperandSizeToValue(file.cpu_address_size())), 0); + + dyld_info->export_size = static_cast<uint32_t>(data.size()); + if (!dyld_info->export_size) { + dyld_info->export_off = 0; + } else { + dyld_info->export_off = static_cast<uint32_t>(file.Tell()); + file.Write(data.data(), data.size()); + } +} + +MacExport *MacExportList::GetExportByAddress(uint64_t address) const +{ + return reinterpret_cast<MacExport *>(BaseExportList::GetExportByAddress(address)); +} + +void MacExportList::ReadFromBuffer(Buffer &buffer, IArchitecture &file) +{ + static const APIType export_function_types[] = { + atSetupImage, + atFreeImage, + atDecryptStringA, + atDecryptStringW, + atFreeString, + atSetSerialNumber, + atGetSerialNumberState, + atGetSerialNumberData, + atGetCurrentHWID, + atActivateLicense, + atDeactivateLicense, + atGetOfflineActivationString, + atGetOfflineDeactivationString, + atIsValidImageCRC, + atIsDebuggerPresent, + atIsVirtualMachinePresent, + atDecryptBuffer, + atIsProtected, + atCalcCRC, + atLoaderData, + atRuntimeInit + }; + + BaseExportList::ReadFromBuffer(buffer, file); + + assert(count() == _countof(export_function_types)); + for (size_t i = 0; i < count(); i++) { + item(i)->set_type(export_function_types[i]); + } +} + +/** + * MacExportNode + */ + +MacExportNode::MacExportNode(MacExportNode *owner) + : ObjectList<MacExportNode>(), owner_(owner), symbol_(NULL), offset_(0) +{ + +} + +MacExportNode::MacExportNode(MacExportNode *owner, MacExport *symbol, const std::string &cummulative_string) + : ObjectList<MacExportNode>(), owner_(owner), symbol_(symbol), cummulative_string_(cummulative_string), offset_(0) +{ + +} + +MacExportNode::~MacExportNode() +{ + if (owner_) + owner_->RemoveObject(this); +} + +void MacExportNode::set_owner(MacExportNode *owner) +{ + if (owner == owner_) + return; + if (owner_) + owner_->RemoveObject(this); + owner_ = owner; + if (owner_) + owner_->AddObject(this); +} + +MacExportNode *MacExportNode::Add(MacExport *symbol, const std::string &cummulative_string) +{ + MacExportNode *node = new MacExportNode(this, symbol, cummulative_string); + AddObject(node); + return node; +} + +void MacExportNode::AddSymbol(MacExport *symbol) +{ + std::string symbol_name = symbol->name(); + const char *partial_str = symbol_name.c_str() + cummulative_string_.size(); + for (size_t j = 0; j < count(); j++) { + MacExportNode *node = item(j); + std::string node_cummulative_string = node->cummulative_string(); + const char *sub_string = node_cummulative_string.c_str() + cummulative_string_.size(); + size_t sub_string_len = strlen(sub_string); + if (strncmp(sub_string, partial_str, sub_string_len) == 0) { + node->AddSymbol(symbol); + return; + } else for (size_t i = sub_string_len; i > 1; i--) { + if (strncmp(sub_string, partial_str, i - 1) == 0) { + MacExportNode *new_node = Add(NULL, node_cummulative_string.substr(0, cummulative_string_.size() + i - 1)); + node->set_owner(new_node); + new_node->AddSymbol(symbol); + return; + } + } + } + + Add(symbol, symbol->name()); +} + +void MacExportNode::WriteToData(EncodedData &data, uint64_t base_address) +{ + size_t i; + + offset_ = (uint32_t)data.size(); + data.WriteByte(0); + if (symbol_) { + size_t old_size = data.size(); + if (symbol_->flags() & EXPORT_SYMBOL_FLAGS_REEXPORT) { + data.WriteUleb128(symbol_->flags()); + data.WriteUleb128(symbol_->other()); + data.WriteString(symbol_->forwarded_name()); + } else if (symbol_->flags() & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) { + data.WriteUleb128(symbol_->flags()); + data.WriteUleb128(symbol_->address() - base_address); + data.WriteUleb128(symbol_->other()); + } else { + data.WriteUleb128(symbol_->flags()); + data.WriteUleb128(symbol_->address() - base_address); + } + data[old_size - 1] = static_cast<uint8_t>(data.size() - old_size); + } + + // write number of children + data.push_back(static_cast<uint8_t>(count())); + // write each child + for (i = 0; i < count(); i++) { + MacExportNode *child = item(i); + data.WriteString(child->cummulative_string().substr(cummulative_string().size())); + data.WriteUleb128(child->offset()); + } +} + +/** + * MacFixup + */ + +MacFixup::MacFixup(MacFixupList *owner, uint64_t address, uint32_t data, OperandSize size, bool is_relocation) + : BaseFixup(owner), address_(address), data_(data), size_(size), is_relocation_(is_relocation), symbol_(NULL) +{ + if (is_relocation && size == osDefault) { + relocation_info reloc = relocation(); + switch (reloc.r_length) { + case 0: + size_ = osByte; + break; + case 1: + size_ = osWord; + break; + case 2: + size_ = osDWord; + break; + default: + size_ = osQWord; + break; + } + } +} + +MacFixup::MacFixup(MacFixupList *owner, const MacFixup &src) + : BaseFixup(owner, src) +{ + address_ = src.address_; + data_ = src.data_; + size_ = src.size_; + is_relocation_ = src.is_relocation_; + symbol_ = src.symbol_; +} + +MacFixup *MacFixup::Clone(IFixupList *owner) const +{ + MacFixup *fixup = new MacFixup(reinterpret_cast<MacFixupList *>(owner), *this); + return fixup; +} + +relocation_info MacFixup::relocation() const +{ + relocation_info res = relocation_info(); + if (is_relocation_) + reinterpret_cast<uint32_t *>(&res)[1] = data_; + return res; +} + +FixupType MacFixup::type() const +{ + switch (internal_type()) { + case REBASE_TYPE_POINTER: + case REBASE_TYPE_TEXT_ABSOLUTE32: + return ftHighLow; + default: + return ftUnknown; + } +} + +uint8_t MacFixup::internal_type() const +{ + if (is_relocation_) { + relocation_info reloc = relocation(); + switch (reloc.r_type) { + case 0: + return REBASE_TYPE_POINTER; + default: + return -1; + } + } else { + switch (data_) { + case REBASE_TYPE_POINTER: + case REBASE_TYPE_TEXT_ABSOLUTE32: + return static_cast<uint8_t>(data_); + default: + return -1; + } + } +} + +void MacFixup::set_is_relocation(bool is_relocation) +{ + if (is_relocation == is_relocation_) + return; + + if (is_relocation_) { + data_ = REBASE_TYPE_POINTER; + } else { + relocation_info reloc; + reloc.r_symbolnum = 0; + switch (size_) { + case osByte: + reloc.r_length = 0; + break; + case osWord: + reloc.r_length = 1; + break; + case osDWord: + reloc.r_length = 2; + break; + default: + reloc.r_length = 3; + break; + } + reloc.r_extern = 0; + reloc.r_type = 0; + data_ = reinterpret_cast<uint32_t *>(&reloc)[1]; + } + is_relocation_ = is_relocation; +} + +void MacFixup::Rebase(IArchitecture &file, uint64_t delta_base) +{ + if (!file.AddressSeek(address_)) + return; + + uint64_t pos = file.Tell(); + uint64_t value = 0; + size_t value_size = OperandSizeToValue(size_); + switch (internal_type()) { + case REBASE_TYPE_POINTER: + case REBASE_TYPE_TEXT_ABSOLUTE32: + assert(sizeof(value) >= value_size); + file.Read(&value, value_size); + value += delta_base; + file.Seek(pos); + file.Write(&value, value_size); + break; + } + address_ += delta_base; +} + +/** + * MacFixupList + */ + +MacFixupList::MacFixupList() + : BaseFixupList() +{ + +} + +MacFixupList::MacFixupList(const MacFixupList &src) + : BaseFixupList(src) +{ + +} + +MacFixupList *MacFixupList::Clone() const +{ + MacFixupList *list = new MacFixupList(*this); + return list; +} + +MacFixup *MacFixupList::item(size_t index) const +{ + return reinterpret_cast<MacFixup *>(BaseFixupList::item(index)); +} + +MacFixup *MacFixupList::Add(uint64_t address, uint32_t data, OperandSize size, bool is_relocation) +{ + MacFixup *fixup = new MacFixup(this, address, data, size, is_relocation); + AddObject(fixup); + + if (fixup->type() == ftUnknown) + throw std::runtime_error("Invalid rebase type"); + + return fixup; +} + +MacFixup *MacFixupList::AddRelocation(uint64_t address, MacSymbol *symbol, OperandSize size) +{ + relocation_info reloc = relocation_info(); + reloc.r_extern = 1; + reloc.r_length = size; + MacFixup *fixup = Add(address, reinterpret_cast<uint32_t *>(&reloc)[1], size, true); + fixup->set_symbol(symbol); + return fixup; +} + +void MacFixupList::ReadRebaseInfo(MacArchitecture &file, uint32_t rebase_off, uint32_t rebase_size) +{ + uint8_t bind_type = 0; + uint64_t address, segment_end; + uint32_t count; + uint32_t skip; + uint8_t immediate; + uint8_t opcode; + EncodedData buf; + ISectionList *segment_list = file.segment_list(); + ISection *segment; + size_t pos, i, ptr_size, seg_index = 0; + OperandSize cpu_address_size; + + if (rebase_size == 0) + return; + + if (segment_list->count() < 1) + throw std::runtime_error("Runtime error at ReadRebaseInfo: Invalid segment count"); + + file.Seek(rebase_off); + buf.ReadFromFile(file, rebase_size); + + cpu_address_size = file.cpu_address_size(); + ptr_size = cpu_address_size == osQWord ? 8 : 4; + + segment = segment_list->item(0); + address = segment->address(); + segment_end = segment->address() + segment->size(); + for (pos = 0; pos < rebase_size; ) { + uint8_t b = buf.ReadByte(&pos); + immediate = b & REBASE_IMMEDIATE_MASK; + opcode = b & REBASE_OPCODE_MASK; + switch (opcode) { + case REBASE_OPCODE_DONE: + return; + break; + case REBASE_OPCODE_SET_TYPE_IMM: + bind_type = immediate; + break; + case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + seg_index = immediate; + if (seg_index >= segment_list->count()) + throw std::runtime_error("Invalid segment index"); + segment = segment_list->item(seg_index); + address = segment->address() + buf.ReadUleb128(&pos); + segment_end = segment->address() + segment->size(); + break; + case REBASE_OPCODE_ADD_ADDR_ULEB: + address += buf.ReadUleb128(&pos); + break; + case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: + address += immediate * ptr_size; + break; + case REBASE_OPCODE_DO_REBASE_IMM_TIMES: + for (i = 0; i < immediate; i++) { + if (address >= segment_end) + throw std::runtime_error("Invalid rebase address"); + Add(address, bind_type, cpu_address_size, false); + address += ptr_size; + } + break; + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: + count = static_cast<uint32_t>(buf.ReadUleb128(&pos)); + for (i = 0; i < count; i++) { + if (address >= segment_end) + throw std::runtime_error("Invalid rebase address"); + Add(address, bind_type, cpu_address_size, false); + address += ptr_size; + } + break; + case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: + if (address >= segment_end) + throw std::runtime_error("Invalid rebase address"); + Add(address, bind_type, cpu_address_size, false); + address += buf.ReadUleb128(&pos) + ptr_size; + break; + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: + count = static_cast<uint32_t>(buf.ReadUleb128(&pos)); + skip = static_cast<uint32_t>(buf.ReadUleb128(&pos)); + for (i = 0; i < count; i++) { + if (address >= segment_end) + throw std::runtime_error("Invalid rebase address"); + Add(address, bind_type, cpu_address_size, false); + address += skip + ptr_size; + } + break; + default: + throw std::runtime_error("Invalid rebase opcode"); + } + } +} + +void MacFixupList::ReadRelocations(MacArchitecture &file, uint32_t offset, uint32_t count, bool need_external) +{ + if (!offset) + return; + + size_t i; + relocation_info reloc; + uint64_t reloc_base = file.GetRelocBase(); + + file.Seek(offset); + for (i = 0; i < count; i++) { + file.Read(&reloc, sizeof(reloc)); + + if (reloc.r_type != 0 || reloc.r_extern != need_external) + throw std::runtime_error("Invalid relocation type"); + + MacFixup *fixup = Add(reloc.r_address + reloc_base, reinterpret_cast<uint32_t *>(&reloc)[1], osDefault, true); + if (reloc.r_extern) { + if (reloc.r_symbolnum >= file.symbol_list()->count()) + throw std::runtime_error("Invalid symbol index"); + fixup->set_symbol(file.symbol_list()->item(reloc.r_symbolnum)); + } + if (file.cpu_address_size() != fixup->size()) + throw std::runtime_error("Invalid relocation size"); + } +} + +void MacFixupList::ReadFromFile(MacArchitecture &file) +{ + dyld_info_command *dyld_info = file.dyld_info(); + if (dyld_info->cmd) + ReadRebaseInfo(file, dyld_info->rebase_off, dyld_info->rebase_size); + + dysymtab_command *dysymtab = file.dysymtab(); + if (dysymtab->cmd) { + ReadRelocations(file, dysymtab->extreloff, dysymtab->nextrel, true); + ReadRelocations(file, dysymtab->locreloff, dysymtab->nlocrel, false); + } +} + +size_t MacFixupList::WriteRelocations(MacArchitecture &file, bool need_external) +{ + size_t i; + relocation_info reloc; + size_t res = 0; + uint64_t reloc_base = file.GetRelocBase(); + + for (i = 0; i < count(); i++) { + MacFixup *fixup = item(i); + if (!fixup->is_relocation()) + continue; + + reloc = fixup->relocation(); + if (reloc.r_extern != need_external) + continue; + + reloc.r_address = static_cast<uint32_t>(fixup->address() - reloc_base); + if (fixup->symbol()) + reloc.r_symbolnum = static_cast<uint32_t>(file.symbol_list()->IndexOf(fixup->symbol())); + file.Write(&reloc, sizeof(reloc)); + res++; + } + + return res; +} + +void MacFixupList::WriteToFile(MacArchitecture &file) +{ + Pack(); + + dyld_info_command *dyld_info = file.dyld_info(); + dysymtab_command *dysymtab = file.dysymtab(); + if (dyld_info->cmd) { + dyld_info->rebase_off = static_cast<uint32_t>(file.Tell()); + dyld_info->rebase_size = (uint32_t)WriteRebaseInfo(file); + if (!dyld_info->rebase_size) + dyld_info->rebase_off = 0; + } else { + // need convert fixups to relocations + for (size_t i = 0; i < count(); i++) { + MacFixup *fixup = item(i); + if (!fixup->is_relocation()) + fixup->set_is_relocation(true); + } + } + if (dysymtab->cmd) { + dysymtab->extreloff = static_cast<uint32_t>(file.Tell()); + dysymtab->nextrel = static_cast<uint32_t>(WriteRelocations(file, true)); + if (!dysymtab->nextrel) + dysymtab->extreloff = 0; + dysymtab->locreloff = static_cast<uint32_t>(file.Tell()); + dysymtab->nlocrel = static_cast<uint32_t>(WriteRelocations(file, false)); + if (!dysymtab->nlocrel) + dysymtab->locreloff = 0; + } +} + +size_t MacFixupList::WriteRebaseInfo(MacArchitecture &file) +{ + size_t i; + std::vector<MacFixup *> info; + + for (i = 0; i < count(); i++) { + MacFixup *fixup = item(i); + if (fixup->is_relocation()) + continue; + info.push_back(fixup); + } + + if (info.size() == 0) + return 0; + + // sort rebase info by type, then address + std::sort(info.begin(), info.end(), RebaseInfoHelper()); + + // convert to temp encoding that can be more easily optimized + std::vector<RebaseInfo> mid; + MacSegment *segment = NULL; + uint8_t bind_type = 0; + uint64_t address = -1; + size_t ptrsize = OperandSizeToValue(file.cpu_address_size()); + for (std::vector<MacFixup *>::iterator it = info.begin(); it != info.end(); ++it) { + MacFixup *fixup = *it; + + if (bind_type != fixup->bind_type()) { + mid.push_back(RebaseInfo(REBASE_OPCODE_SET_TYPE_IMM, fixup->bind_type())); + bind_type = fixup->bind_type(); + } + if (address != fixup->address()) { + if (!segment || (fixup->address() < segment->address()) || (fixup->address() >= segment->address() + segment->size())) { + segment = file.segment_list()->GetSectionByAddress(fixup->address()); + if (!segment) + throw std::runtime_error("binding address outside range of any segment"); + mid.push_back(RebaseInfo(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB, file.segment_list()->IndexOf(segment), fixup->address() - segment->address())); + } + else { + mid.push_back(RebaseInfo(REBASE_OPCODE_ADD_ADDR_ULEB, fixup->address() - address)); + } + address = fixup->address(); + } + mid.push_back(RebaseInfo(REBASE_OPCODE_DO_REBASE_ULEB_TIMES, 1)); + address += ptrsize; + } + mid.push_back(RebaseInfo(REBASE_OPCODE_DONE, 0)); + + // optimize phase 1, compress packed runs of pointers + RebaseInfo *dst = &mid[0]; + for (const RebaseInfo *src = &mid[0]; src->opcode != REBASE_OPCODE_DONE; ++src) { + if ( (src->opcode == REBASE_OPCODE_DO_REBASE_ULEB_TIMES) && (src->operand1 == 1) ) { + *dst = *src++; + while (src->opcode == REBASE_OPCODE_DO_REBASE_ULEB_TIMES ) { + dst->operand1 += src->operand1; + ++src; + } + --src; + ++dst; + } + else { + *dst++ = *src; + } + } + dst->opcode = REBASE_OPCODE_DONE; + + // optimize phase 2, combine rebase/add pairs + dst = &mid[0]; + for (const RebaseInfo *src = &mid[0]; src->opcode != REBASE_OPCODE_DONE; ++src) { + if ( (src->opcode == REBASE_OPCODE_DO_REBASE_ULEB_TIMES) + && (src->operand1 == 1) + && (src[1].opcode == REBASE_OPCODE_ADD_ADDR_ULEB)) { + dst->opcode = REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB; + dst->operand1 = src[1].operand1; + ++src; + ++dst; + } + else { + *dst++ = *src; + } + } + dst->opcode = REBASE_OPCODE_DONE; + + // optimize phase 3, compress packed runs of REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB with + // same addr delta into one REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB + dst = &mid[0]; + for (const RebaseInfo *src = &mid[0]; src->opcode != REBASE_OPCODE_DONE; ++src) { + uint64_t delta = src->operand1; + if ( (src->opcode == REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB) + && (src[1].opcode == REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB) + && (src[2].opcode == REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB) + && (src[1].operand1 == delta) + && (src[2].operand1 == delta) ) { + // found at least three in a row, this is worth compressing + dst->opcode = REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB; + dst->operand1 = 1; + dst->operand2 = delta; + ++src; + while ( (src->opcode == REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB) + && (src->operand1 == delta) ) { + dst->operand1++; + ++src; + } + --src; + ++dst; + } + else { + *dst++ = *src; + } + } + dst->opcode = REBASE_OPCODE_DONE; + + // optimize phase 4, use immediate encodings + for (RebaseInfo *p = &mid[0]; p->opcode != REBASE_OPCODE_DONE; ++p) { + if ( (p->opcode == REBASE_OPCODE_ADD_ADDR_ULEB) + && (p->operand1 < (15 * ptrsize)) + && ((p->operand1 % ptrsize) == 0) ) { + p->opcode = REBASE_OPCODE_ADD_ADDR_IMM_SCALED; + p->operand1 = p->operand1 / ptrsize; + } + else if ( (p->opcode == REBASE_OPCODE_DO_REBASE_ULEB_TIMES) && (p->operand1 < 15) ) { + p->opcode = REBASE_OPCODE_DO_REBASE_IMM_TIMES; + } + } + + // convert to compressed encoding + EncodedData data; + data.reserve(info.size() * 2); + bool done = false; + for (std::vector<RebaseInfo>::iterator it = mid.begin(); !done && it != mid.end() ; ++it) { + switch ( it->opcode ) { + case REBASE_OPCODE_DONE: + done = true; + break; + case REBASE_OPCODE_SET_TYPE_IMM: + data.WriteByte(REBASE_OPCODE_SET_TYPE_IMM | static_cast<uint8_t>(it->operand1)); + break; + case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + data.WriteByte(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | static_cast<uint8_t>(it->operand1)); + data.WriteUleb128(it->operand2); + break; + case REBASE_OPCODE_ADD_ADDR_ULEB: + data.WriteByte(REBASE_OPCODE_ADD_ADDR_ULEB); + data.WriteUleb128(it->operand1); + break; + case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: + data.WriteByte(REBASE_OPCODE_ADD_ADDR_IMM_SCALED | static_cast<uint8_t>(it->operand1)); + break; + case REBASE_OPCODE_DO_REBASE_IMM_TIMES: + data.WriteByte(REBASE_OPCODE_DO_REBASE_IMM_TIMES | static_cast<uint8_t>(it->operand1)); + break; + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: + data.WriteByte(REBASE_OPCODE_DO_REBASE_ULEB_TIMES); + data.WriteUleb128(it->operand1); + break; + case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: + data.WriteByte(REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB); + data.WriteUleb128(it->operand1); + break; + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: + data.WriteByte(REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB); + data.WriteUleb128(it->operand1); + data.WriteUleb128(it->operand2); + break; + } + } + // align to pointer size + data.resize(AlignValue(data.size(), ptrsize), 0); + + return (data.size() == 0) ? 0 : file.Write(data.data(), data.size()); +} + +MacFixup *MacFixupList::AddDefault(OperandSize cpu_address_size, bool is_code) +{ + return Add(0, is_code ? REBASE_TYPE_TEXT_ABSOLUTE32 : REBASE_TYPE_POINTER, cpu_address_size, false); +} + +size_t MacFixupList::Pack() +{ + for (size_t i = 0; i < count(); i++) { + MacFixup *fixup = item(i); + MacSymbol *symbol = fixup->symbol(); + if (symbol && symbol->is_deleted()) + fixup->set_deleted(true); + } + + return BaseFixupList::Pack(); +} + +void MacFixupList::WriteToData(Data &data, uint64_t image_base) +{ + size_t i, size_pos; + MacFixup *fixup; + IMAGE_BASE_RELOCATION reloc; + uint32_t rva, block_rva; + uint16_t type_offset, empty_offset; + + Sort(); + + size_pos = 0; + reloc.VirtualAddress = 0; + reloc.SizeOfBlock = 0; + + for (i = 0; i < count(); i++) { + fixup = item(i); + if (fixup->symbol()) + continue; + + rva = static_cast<uint32_t>(fixup->address() - image_base); + block_rva = rva & 0xfffff000; + if (reloc.SizeOfBlock == 0 || block_rva != reloc.VirtualAddress) { + if (reloc.SizeOfBlock > 0) { + if ((reloc.SizeOfBlock & 3) != 0) { + data.PushWord(empty_offset); + reloc.SizeOfBlock += sizeof(empty_offset); + } + data.WriteDWord(size_pos, reloc.SizeOfBlock); + } + size_pos = data.size() + 4; + reloc.VirtualAddress = block_rva; + reloc.SizeOfBlock = sizeof(reloc); + data.PushBuff(&reloc, sizeof(reloc)); + empty_offset = (static_cast<uint16_t>(rva - block_rva) & 0xf00) << 4 | R_ABS; + } + type_offset = (static_cast<uint16_t>(rva - block_rva) & 0xfff) << 4 | fixup->internal_type(); + data.PushWord(type_offset); + reloc.SizeOfBlock += sizeof(type_offset); + } + + if (reloc.SizeOfBlock > 0) { + if ((reloc.SizeOfBlock & 3) != 0) { + data.PushWord(empty_offset); + reloc.SizeOfBlock += sizeof(empty_offset); + } + data.WriteDWord(size_pos, reloc.SizeOfBlock); + } +} + +/** + * MacRuntimeFunction + */ + +MacRuntimeFunction::MacRuntimeFunction(MacRuntimeFunctionList *owner, uint64_t address, uint64_t begin, uint64_t end, uint64_t unwind_address, + CommonInformationEntry *cie, const std::vector<uint8_t> &call_frame_instructions, uint32_t compact_encoding) + : BaseRuntimeFunction(owner), address_(address), begin_(begin), end_(end), unwind_address_(unwind_address), cie_(cie), + call_frame_instructions_(call_frame_instructions), compact_encoding_(compact_encoding) +{ + +} + +MacRuntimeFunction::MacRuntimeFunction(MacRuntimeFunctionList *owner, const MacRuntimeFunction &src) + : BaseRuntimeFunction(owner) +{ + address_ = src.address_; + begin_ = src.begin_; + end_ = src.end_; + unwind_address_ = src.unwind_address_; + cie_ = src.cie_; + call_frame_instructions_ = src.call_frame_instructions_; + compact_encoding_ = src.compact_encoding_; +} + +MacRuntimeFunction *MacRuntimeFunction::Clone(IRuntimeFunctionList *owner) const +{ + MacRuntimeFunction *func = new MacRuntimeFunction(reinterpret_cast<MacRuntimeFunctionList *>(owner), *this); + return func; +} + +void MacRuntimeFunction::Parse(IArchitecture &file, IFunction &dest) +{ + if (!file.AddressSeek(address_) || dest.GetCommandByAddress(address_)) + return; + + if (cie_) + ParseDwarf(file, dest); + else + ParseBorland(file, dest); +} + +void MacRuntimeFunction::ParseDwarf(IArchitecture &file, IFunction &dest) +{ + uint64_t address = address_; + IntelFunction &func = reinterpret_cast<IntelFunction &>(dest); + + size_t c = func.count(); + IntelCommand *command; + uint64_t value; + size_t pos; + CommandLink *link; + FunctionInfo *info; + std::vector<ICommand *> unwind_opcodes; + + if (cie_->version() == 0) { + command = func.Add(address); + command->set_comment(CommentInfo(ttComment, "Compact Entry")); + command->ReadValueFromFile(file, osDWord); + } else { + command = func.Add(address); + command->set_comment(CommentInfo(ttComment, "FDE Length")); + uint32_t fde_length = static_cast<uint32_t>(command->ReadValueFromFile(file, osDWord)); + address = command->next_address(); + + if (fde_length) { + EncodedData fde(command->next_address(), file.cpu_address_size()); + fde.ReadFromFile(file, fde_length); + pos = 0; + + command = func.Add(address); + command->set_comment(CommentInfo(ttComment, "CIE Pointer")); + value = command->ReadDataDWord(fde, &pos); + uint64_t cie_address = address - value; + address = command->next_address(); + + IntelCommand *cie_command = func.GetCommandByAddress(cie_address); + if (!cie_command) { + size_t fde_pos = pos; + uint64_t fde_address = address; + + address = cie_address; + file.AddressSeek(address); + + command = func.Add(address); + cie_command = command; + command->set_comment(CommentInfo(ttComment, "CIE Length")); + uint32_t cie_length = static_cast<uint32_t>(command->ReadValueFromFile(file, osDWord)); + address = command->next_address(); + + if (cie_length) { + EncodedData cie(command->next_address(), file.cpu_address_size()); + cie.ReadFromFile(file, cie_length); + pos = 0; + + command = func.Add(address); + command->set_comment(CommentInfo(ttComment, "CIE ID")); + command->ReadDataDWord(cie, &pos); + address = command->next_address(); + + command = func.Add(address); + command->set_comment(CommentInfo(ttComment, "CIE Version")); + command->ReadDataByte(cie, &pos); + address = command->next_address(); + + command = func.Add(address); + command->ReadString(cie, &pos); + command->set_comment(CommentInfo(ttComment, string_format("Augmentation String: %s", cie_->augmentation().c_str()))); + address = command->next_address(); + + command = func.Add(address); + command->ReadUleb128(cie, &pos); + command->set_comment(CommentInfo(ttComment, "Code Alignment Factor")); + address = command->next_address(); + + command = func.Add(address); + command->ReadSleb128(cie, &pos); + command->set_comment(CommentInfo(ttComment, "Data Alignment Factor")); + address = command->next_address(); + + command = func.Add(address); + command->ReadDataByte(cie, &pos); + command->set_comment(CommentInfo(ttComment, "Return Address Register")); + address = command->next_address(); + + if (*cie_->augmentation().c_str() == 'z') { + command = func.Add(address); + command->ReadUleb128(cie, &pos); + command->set_comment(CommentInfo(ttComment, "Augmentation Length")); + address = command->next_address(); + + for (size_t j = 1; j < cie_->augmentation().size(); j++) { + switch (cie_->augmentation().at(j)) { + case 'L': + command = func.Add(address); + command->set_comment(CommentInfo(ttComment, "LSDA Encoding")); + command->ReadDataByte(cie, &pos); + address = command->next_address(); + break; + case 'R': + command = func.Add(address); + command->set_comment(CommentInfo(ttComment, "FDE Encoding")); + command->ReadDataByte(cie, &pos); + address = command->next_address(); + break; + case 'P': + { + command = func.Add(address); + command->set_comment(CommentInfo(ttComment, "Personality Encoding")); + command->ReadDataByte(cie, &pos); + address = command->next_address(); + + command = func.Add(address); + command->ReadEncoding(cie, cie_->personality_encoding(), &pos); + command->set_comment(CommentInfo(ttComment, "Personality Routine")); + address = command->next_address(); + } + break; + } + } + } + + command = func.Add(address); + command->ReadData(cie, cie.size() - pos, &pos); + command->set_comment(CommentInfo(ttComment, "Initial Instructions")); + address = command->next_address(); + } + + file.AddressSeek(fde_address); + address = fde_address; + pos = fde_pos; + } + + command = func.Add(address); + command->ReadEncoding(fde, cie_->fde_encoding(), &pos); + command->set_comment(CommentInfo(ttComment, string_format("Begin: %llX", begin()))); + address = command->next_address(); + + command = func.Add(address); + command->ReadEncoding(fde, cie_->fde_encoding() & 0x0f, &pos); + command->set_comment(CommentInfo(ttComment, string_format("End: %llX", end()))); + address = command->next_address(); + + if (*cie_->augmentation().c_str() == 'z') { + command = func.Add(address); + value = command->ReadUleb128(fde, &pos); + command->set_comment(CommentInfo(ttComment, "Augmentation Length")); + address = command->next_address(); + + if (cie_->augmentation().find('L') != std::string::npos) { + command = func.Add(address); + command->ReadEncoding(fde, cie_->lsda_encoding(), &pos); + command->set_comment(CommentInfo(ttComment, "LSDA")); + if (unwind_address_) + command->AddLink(0, ltOffset, unwind_address_); + + address = command->next_address(); + } + } + + uint64_t pc = begin(); + while (pos < fde.size()) { + command = func.Add(address); + size_t cur_pos = pos; + uint8_t b = fde.ReadByte(&pos); + switch (b) { + case DW_CFA_nop: + command->ReadData(fde, fde.size() - cur_pos, &cur_pos); + command->set_comment(CommentInfo(ttComment, "DW_CFA_nop")); + break; + case DW_CFA_set_loc: + pc = fde.ReadEncoding(cie_->fde_encoding(), &pos); + command->ReadData(fde, pos - cur_pos, &cur_pos); + command->set_comment(CommentInfo(ttComment, "DW_CFA_set_loc")); + break; + case DW_CFA_advance_loc1: + value = fde.ReadByte(&pos); + command->ReadData(fde, pos - cur_pos, &cur_pos); + command->set_comment(CommentInfo(ttComment, "DW_CFA_advance_loc1")); + func.range_list()->Add(pc, pc + value, NULL, NULL, command); + pc += value; + break; + case DW_CFA_advance_loc2: + value = fde.ReadWord(&pos); + command->ReadData(fde, pos - cur_pos, &cur_pos); + command->set_comment(CommentInfo(ttComment, "DW_CFA_advance_loc2")); + func.range_list()->Add(pc, pc + value, NULL, NULL, command); + pc += value; + break; + case DW_CFA_advance_loc4: + value = fde.ReadDWord(&pos); + command->ReadData(fde, pos - cur_pos, &cur_pos); + command->set_comment(CommentInfo(ttComment, "DW_CFA_advance_loc4")); + func.range_list()->Add(pc, pc + value, NULL, NULL, command); + pc += value; + break; + case DW_CFA_offset_extended: + fde.ReadUleb128(&pos); + fde.ReadUleb128(&pos); + command->ReadData(fde, pos - cur_pos, &cur_pos); + command->set_comment(CommentInfo(ttComment, "DW_CFA_offset_extended")); + break; + case DW_CFA_restore_extended: + fde.ReadUleb128(&pos); + command->ReadData(fde, pos - cur_pos, &cur_pos); + command->set_comment(CommentInfo(ttComment, "DW_CFA_restore_extended")); + break; + case DW_CFA_undefined: + fde.ReadUleb128(&pos); + command->ReadData(fde, pos - cur_pos, &cur_pos); + command->set_comment(CommentInfo(ttComment, "DW_CFA_undefined")); + break; + case DW_CFA_same_value: + fde.ReadUleb128(&pos); + command->ReadData(fde, pos - cur_pos, &cur_pos); + command->set_comment(CommentInfo(ttComment, "DW_CFA_same_value")); + break; + case DW_CFA_register: + fde.ReadUleb128(&pos); + fde.ReadUleb128(&pos); + command->ReadData(fde, pos - cur_pos, &cur_pos); + command->set_comment(CommentInfo(ttComment, "DW_CFA_register")); + break; + case DW_CFA_remember_state: + command->ReadData(fde, pos - cur_pos, &cur_pos); + command->set_comment(CommentInfo(ttComment, "DW_CFA_remember_state")); + break; + case DW_CFA_restore_state: + command->ReadData(fde, pos - cur_pos, &cur_pos); + command->set_comment(CommentInfo(ttComment, "DW_CFA_restore_state")); + break; + case DW_CFA_def_cfa: + fde.ReadUleb128(&pos); + fde.ReadUleb128(&pos); + command->ReadData(fde, pos - cur_pos, &cur_pos); + command->set_comment(CommentInfo(ttComment, "DW_CFA_def_cfa")); + break; + case DW_CFA_def_cfa_register: + fde.ReadUleb128(&pos); + command->ReadData(fde, pos - cur_pos, &cur_pos); + command->set_comment(CommentInfo(ttComment, "DW_CFA_def_cfa_register")); + break; + case DW_CFA_def_cfa_offset: + fde.ReadUleb128(&pos); + command->ReadData(fde, pos - cur_pos, &cur_pos); + command->set_comment(CommentInfo(ttComment, "DW_CFA_def_cfa_offset")); + break; + case DW_CFA_def_cfa_expression: + value = fde.ReadUleb128(&pos); + command->ReadData(fde, pos - cur_pos, &cur_pos); + command->set_comment(CommentInfo(ttComment, "DW_CFA_def_cfa_expression")); + pos += static_cast<size_t>(value); + break; + case DW_CFA_expression: + fde.ReadUleb128(&pos); + value = fde.ReadUleb128(&pos); + command->ReadData(fde, pos - cur_pos, &cur_pos); + command->set_comment(CommentInfo(ttComment, "DW_CFA_def_cfa_expression")); + pos += static_cast<size_t>(value); + break; + case DW_CFA_offset_extended_sf: + fde.ReadUleb128(&pos); + fde.ReadSleb128(&pos); + command->ReadData(fde, pos - cur_pos, &cur_pos); + command->set_comment(CommentInfo(ttComment, "DW_CFA_offset_extended_sf")); + break; + case DW_CFA_def_cfa_sf: + fde.ReadUleb128(&pos); + fde.ReadSleb128(&pos); + command->ReadData(fde, pos - cur_pos, &cur_pos); + command->set_comment(CommentInfo(ttComment, "DW_CFA_def_cfa_sf")); + break; + case DW_CFA_def_cfa_offset_sf: + fde.ReadSleb128(&pos); + command->ReadData(fde, pos - cur_pos, &cur_pos); + command->set_comment(CommentInfo(ttComment, "DW_CFA_def_cfa_offset_sf")); + break; + case DW_CFA_val_offset: + fde.ReadUleb128(&pos); + fde.ReadUleb128(&pos); + command->ReadData(fde, pos - cur_pos, &cur_pos); + command->set_comment(CommentInfo(ttComment, "DW_CFA_val_offset")); + break; + case DW_CFA_val_offset_sf: + fde.ReadUleb128(&pos); + fde.ReadSleb128(&pos); + command->ReadData(fde, pos - cur_pos, &cur_pos); + command->set_comment(CommentInfo(ttComment, "DW_CFA_val_offset_sf")); + break; + case DW_CFA_val_expression: + fde.ReadUleb128(&pos); + value = fde.ReadUleb128(&pos); + command->ReadData(fde, pos - cur_pos, &cur_pos); + command->set_comment(CommentInfo(ttComment, "DW_CFA_val_expression")); + pos += static_cast<size_t>(value); + break; + case DW_CFA_GNU_window_save: + command->ReadData(fde, pos - cur_pos, &cur_pos); + command->set_comment(CommentInfo(ttComment, "DW_CFA_GNU_window_save")); + break; + case DW_CFA_GNU_args_size: + fde.ReadUleb128(&pos); + command->ReadData(fde, pos - cur_pos, &cur_pos); + command->set_comment(CommentInfo(ttComment, "DW_CFA_GNU_args_size")); + break; + case DW_CFA_GNU_negative_offset_extended: + fde.ReadUleb128(&pos); + fde.ReadUleb128(&pos); + command->ReadData(fde, pos - cur_pos, &cur_pos); + command->set_comment(CommentInfo(ttComment, "DW_CFA_GNU_negative_offset_extended")); + break; + default: + switch (b & 0xc0) { + case DW_CFA_advance_loc: + value = (b & 0x3f); + command->ReadData(fde, pos - cur_pos, &cur_pos); + command->set_comment(CommentInfo(ttComment, "DW_CFA_advance_loc")); + func.range_list()->Add(pc, pc + value, NULL, NULL, command); + pc += value; + break; + case DW_CFA_offset: + fde.ReadUleb128(&pos); + command->ReadData(fde, pos - cur_pos, &cur_pos); + command->set_comment(CommentInfo(ttComment, "DW_CFA_offset")); + break; + case DW_CFA_restore: + command->ReadData(fde, pos - cur_pos, &cur_pos); + command->set_comment(CommentInfo(ttComment, "DW_CFA_restore")); + break; + default: + command->ReadData(fde, fde.size() - cur_pos, &cur_pos); + break; + } + } + pos = cur_pos; + address = command->next_address(); + + if (b != DW_CFA_nop) + unwind_opcodes.push_back(command); + } + } + } + for (size_t i = c; i < func.count(); i++) { + command = func.item(i); + command->exclude_option(roClearOriginalCode); + command->exclude_option(roNeedCompile); + } + + if (unwind_address_ && file.AddressSeek(unwind_address_)) { + address = unwind_address_; + + EncodedData lsda(address, file.cpu_address_size()); + lsda.ReadFromFile(file, static_cast<size_t>(file.selected_segment()->address() + file.selected_segment()->physical_size() - address)); + pos = 0; + + command = func.Add(address); + command->set_comment(CommentInfo(ttComment, "LPStart Encoding")); + uint8_t start_encoding = command->ReadDataByte(lsda, &pos); + command->include_option(roCreateNewBlock); + address = command->next_address(); + + IntelCommand *entry = command; + + uint64_t start = begin(); + AddressBaseType base_type = btFunctionBegin; + if (start_encoding != DW_EH_PE_omit) { + command = func.Add(address); + command->set_comment(CommentInfo(ttComment, "LPStart")); + start = command->ReadEncoding(lsda, start_encoding, &pos); + address = command->next_address(); + base_type = btValue; + } + + info = func.function_info_list()->Add(begin(), end(), base_type, (base_type == btValue) ? start : 0, 0, 0xff, this, entry); + info->set_unwind_opcodes(unwind_opcodes); + + command = func.Add(address); + command->set_comment(CommentInfo(ttComment, "TTable Encoding")); + uint8_t ttable_encoding = command->ReadDataByte(lsda, &pos); + address = command->next_address(); + + size_t ttable_offset = 0; + IntelCommand *ttable_offset_entry = NULL; + if (ttable_encoding != DW_EH_PE_omit) { + ttable_offset_entry = func.Add(address); + ttable_offset_entry->set_comment(CommentInfo(ttComment, "TTable Offset")); + ttable_offset_entry->include_option(roFillNop); + ttable_offset = static_cast<size_t>(ttable_offset_entry->ReadUleb128(lsda, &pos)) + pos; + address = ttable_offset_entry->next_address(); + } + + command = func.Add(address); + command->set_comment(CommentInfo(ttComment, "Call Site Encoding")); + uint8_t call_site_encoding = command->ReadDataByte(lsda, &pos); + address = command->next_address(); + + command = func.Add(address); + command->set_comment(CommentInfo(ttComment, "Call Site Length")); + uint64_t call_site_length = command->ReadUleb128(lsda, &pos); + address = command->next_address(); + + std::set<int64_t> action_list; + size_t old_pos = pos; + while (pos - old_pos < call_site_length) { + IntelCommand *begin_entry = func.Add(address); + uint64_t begin_address = start + begin_entry->ReadEncoding(lsda, call_site_encoding, &pos); + begin_entry->set_comment(CommentInfo(ttComment, string_format("Begin: %llX", begin_address))); + address = begin_entry->next_address(); + + IntelCommand *size_entry = func.Add(address); + uint64_t end_address = begin_address + size_entry->ReadEncoding(lsda, call_site_encoding, &pos); + size_entry->set_comment(CommentInfo(ttComment, string_format("End: %llX", end_address))); + address = size_entry->next_address(); + + func.range_list()->Add(begin_address, end_address, begin_entry, NULL, size_entry); + + command = func.Add(address); + value = command->ReadEncoding(lsda, call_site_encoding, &pos); + if (value) { + value += begin(); + link = command->AddLink(0, ltMemSEHBlock, value); + link->set_sub_value(begin()); + link->set_base_function_info(info); + } + command->set_comment(CommentInfo(ttComment, string_format("Landing Pad: %llX", value))); + address = command->next_address(); + + command = func.Add(address); + command->set_comment(CommentInfo(ttComment, "Action")); + value = command->ReadUleb128(lsda, &pos); + address = command->next_address(); + + if (value) + action_list.insert(value); + } + + if (ttable_encoding != DW_EH_PE_omit) { + std::set<int64_t> type_index_list; + std::set<int64_t> spec_index_list; + int64_t action = 1; + while (action_list.size()) { + action_list.erase(action); + + old_pos = pos; + + command = func.Add(address); + command->set_comment(CommentInfo(ttComment, "Type Filter")); + int64_t index = command->ReadSleb128(lsda, &pos); + address = command->next_address(); + + if (index > 0) + type_index_list.insert(index); + else if (index < 0) + spec_index_list.insert(index); + + command = func.Add(address); + command->set_comment(CommentInfo(ttComment, "Next Action")); + int64_t next_action = command->ReadSleb128(lsda, &pos); + address = command->next_address(); + + action += pos - old_pos; + + if (next_action >= action) + action_list.insert(next_action); + } + + size_t old_count = func.count(); + + pos = ttable_offset; + address = lsda.address() + pos; + for (size_t i = 0; i < spec_index_list.size(); i++) { + command = func.Add(address); + command->set_comment(CommentInfo(ttComment, "Exception Spec")); + value = command->ReadUleb128(lsda, &pos); + address = command->next_address(); + + if (value > 0) + type_index_list.insert(value); + } + + pos = ttable_offset - type_index_list.size() * lsda.encoding_size(ttable_encoding); + address = lsda.address() + pos; + for (size_t i = 0; i < type_index_list.size(); i++) { + command = func.Add(address); + command->set_comment(CommentInfo(ttComment, "Type Info")); + value = command->ReadEncoding(lsda, ttable_encoding, &pos); + if (command->operand(0).value) + link = command->AddLink(0, (ttable_encoding & 0x70) == DW_EH_PE_pcrel ? ltDelta : ltOffset, value); + address = command->next_address(); + } + + if (old_count < func.count()) { + address = func.item(old_count)->address(); + link = ttable_offset_entry->AddLink(0, ltDelta, address); + link->set_sub_value(ttable_offset_entry->dump_size() + address - lsda.address() - ttable_offset); + } + } + } else { + // no LSDA + info = func.function_info_list()->Add(begin(), end(), btFunctionBegin, 0, 0, 0xff, this, NULL); + info->set_unwind_opcodes(unwind_opcodes); + } + + for (size_t i = c; i < func.count(); i++) { + command = func.item(i); + command->exclude_option(roClearOriginalCode); + } +} + +void MacRuntimeFunction::ParseBorland(IArchitecture &file, IFunction &dest) +{ + uint64_t address = address_; + IntelFunction &func = reinterpret_cast<IntelFunction &>(dest); + + size_t c = func.count(); + IntelCommand *command; + CommandLink *link; + uint64_t value; + + command = func.Add(address); + command->set_comment(CommentInfo(ttComment, "Begin")); + command->ReadValueFromFile(file, osDWord); + address = command->next_address(); + + command = func.Add(address); + command->set_comment(CommentInfo(ttComment, "End")); + command->ReadValueFromFile(file, osDWord); + /*address =*/ command->next_address(); + + EncodedData data; + data.resize(30); + file.AddressSeek(end_ - data.size()); + for (size_t i = 0; i < data.size(); i++) { + data[data.size() - 1 - i] = file.ReadByte(); + } + + //uint32_t v1 = 0; + //uint32_t v2 = 0; + uint32_t v3 = 0; + uint32_t v4 = 0; + uint32_t v5 = 0; + //uint32_t v6 = 0; + + uint8_t b = data[0]; + size_t pos = 1; + + if (b & 0x80) { + /*v6 = */data.ReadUnsigned(&pos); + } + + if (b & 0x40) { + if (b & 0x10) { + /*v1 = */data.ReadUnsigned(&pos); + } else { //-V523 + /*v2 = */data.ReadUnsigned(&pos); + } + } + + if (b & 0x20) { + v3 = data.ReadUnsigned(&pos); + } + + if ((b & 0x18) != 0x10) { + v4 = data.ReadUnsigned(&pos); + v5 = data.ReadUnsigned(&pos); + } else if (b & 0x20) { + v5 = data.ReadUnsigned(&pos); + } + + address = end_ - pos - sizeof(uint32_t); + file.AddressSeek(address); + command = func.Add(address); + value = command->ReadValueFromFile(file, osDWord); + + command = func.Add(address + command->dump_size()); + command->ReadArray(file, pos); + + if (v3) { + uint64_t base_address = address - value - v3 * 12 - v4 - v5; + address = base_address + v5; + file.AddressSeek(address); + for (size_t i = 0; i < v3; i++) { + command = func.Add(address); + command->set_comment(CommentInfo(ttComment, "Begin")); + command->ReadValueFromFile(file, osDWord); + //command->include_option(roRangeBeginEntry); + address = command->next_address(); + + command = func.Add(address); + command->set_comment(CommentInfo(ttComment, "End")); + command->ReadValueFromFile(file, osDWord); + //command->include_option(roRangeEndEntry); + address = command->next_address(); + + command = func.Add(address); + command->set_comment(CommentInfo(ttComment, "Handler")); + value = command->ReadValueFromFile(file, osDWord); + link = command->AddLink(0, ltExtSEHHandler, value + base_address); + link->set_sub_value(base_address); + address = command->next_address(); + } + } + + for (size_t i = c; i < func.count(); i++) { + command = func.item(i); + command->exclude_option(roClearOriginalCode); + command->exclude_option(roNeedCompile); + } +} + +void MacRuntimeFunction::Rebase(uint64_t delta_base) +{ + address_ += delta_base; + begin_ += delta_base; + end_ += delta_base; + if (unwind_address_) + unwind_address_ += delta_base; +} + +/** + * MacRuntimeFunctionList + */ + +MacRuntimeFunctionList::MacRuntimeFunctionList() + : BaseRuntimeFunctionList(), address_(0) +{ + cie_list_ = new CommonInformationEntryList(); +} + +MacRuntimeFunctionList::MacRuntimeFunctionList(const MacRuntimeFunctionList &src) + : BaseRuntimeFunctionList(src) +{ + cie_list_ = src.cie_list_->Clone(); + address_ = src.address_; + for (size_t i = 0; i < count(); i++) { + MacRuntimeFunction *func = item(i); + if (func->cie()) + func->set_cie(cie_list_->item(src.cie_list_->IndexOf(func->cie()))); + } +} + +MacRuntimeFunctionList::~MacRuntimeFunctionList() +{ + delete cie_list_; +} + +MacRuntimeFunctionList *MacRuntimeFunctionList::Clone() const +{ + MacRuntimeFunctionList *list = new MacRuntimeFunctionList(*this); + return list; +} + +void MacRuntimeFunctionList::clear() +{ + cie_list_->clear(); + IRuntimeFunctionList::clear(); +} + +MacRuntimeFunction *MacRuntimeFunctionList::item(size_t index) const +{ + return reinterpret_cast<MacRuntimeFunction *>(IRuntimeFunctionList::item(index)); +} + +MacRuntimeFunction *MacRuntimeFunctionList::Add(uint64_t address, uint64_t begin, uint64_t end, uint64_t unwind_address, IRuntimeFunction *source, const std::vector<uint8_t> &call_frame_instructions) +{ + if (!source) + throw std::runtime_error("Invalid runtime function"); + + MacRuntimeFunction *src = reinterpret_cast<MacRuntimeFunction *>(source); + return Add(address, begin, end, unwind_address, src->cie(), call_frame_instructions, src->compact_encoding()); +} + +MacRuntimeFunction *MacRuntimeFunctionList::Add(uint64_t address, uint64_t begin, uint64_t end, uint64_t unwind_address, CommonInformationEntry *cie, const std::vector<uint8_t> &call_frame_instructions, uint32_t compact_encoding) +{ + MacRuntimeFunction *func = new MacRuntimeFunction(this, address, begin, end, unwind_address, cie, call_frame_instructions, compact_encoding); + AddObject(func); + return func; +} + +MacRuntimeFunction *MacRuntimeFunctionList::GetFunctionByAddress(uint64_t address) const +{ + return reinterpret_cast<MacRuntimeFunction *>(BaseRuntimeFunctionList::GetFunctionByAddress(address)); +} + +void MacRuntimeFunctionList::ReadFromFile(MacArchitecture &file) +{ + MacSection *section = file.runtime_functions_section() ? file.runtime_functions_section() : file.unwind_info_section(); + if (!section) { + if (file.import_list()->GetImportByName("@rpath/libcgunwind.1.0.dylib")) { + MacSegment *segment = file.segment_list()->GetSectionByName(SEG_TEXT); + MacSection *section = segment ? file.section_list()->GetSectionByName(segment, SECT_TEXT) : NULL; + if (segment && section) { + Signature sign(NULL, "0700DEFB", 0); + size_t read_size = 0; + uint8_t buf[0x1000]; + while (read_size < segment->physical_size()) { + file.Seek(segment->physical_offset() + read_size); + size_t n = file.Read(buf, std::min(static_cast<size_t>(segment->physical_size() - read_size), sizeof(buf))); + for (size_t i = 0; i < n; i++) { + if (sign.SearchByte(buf[i])) { + uint64_t address = segment->address() + read_size + i + 1 - sign.size(); + file.AddressSeek(address + sign.size()); + uint32_t delta = file.ReadDWord(); + if (address - delta == section->address()) { + ReadBorlandInfo(file, address); + return; + } + } + } + read_size += n; + } + } + } + } else if (section->name() == SECT_EH_FRAME) { + ReadDwarfInfo(file, section->address(), static_cast<uint32_t>(section->size())); + section = file.unwind_info_section(); + if (section && section->name() == SECT_UNWIND_INFO) { + MacRuntimeFunctionList tmp_list; + std::map<CommonInformationEntry *, CommonInformationEntry *> cie_map; + tmp_list.ReadCompactInfo(file, section->address(), static_cast<uint32_t>(section->size())); + for (size_t i = 0; i < tmp_list.count(); i++) { + MacRuntimeFunction *func = tmp_list.item(i); + if (!GetFunctionByAddress(func->begin())) { + CommonInformationEntry *cie; + MacRuntimeFunction *new_func = func->Clone(this); + std::map<CommonInformationEntry *, CommonInformationEntry *>::const_iterator it = cie_map.find(func->cie()); + if (it != cie_map.end()) { + cie = it->second; + } + else { + cie = func->cie()->Clone(cie_list_); + cie_list_->AddObject(cie); + cie_map[func->cie()] = cie; + } + new_func->set_cie(cie); + AddObject(new_func); + } + } + } + } + else if (section->name() == SECT_UNWIND_INFO) + ReadCompactInfo(file, section->address(), static_cast<uint32_t>(section->size())); + else if (section->name() == SECT_INIT_TEXT) + ReadBorlandInfo(file, section->address()); +} + +void MacRuntimeFunctionList::ReadDwarfInfo(MacArchitecture &file, uint64_t address, uint32_t size) +{ + if (!file.AddressSeek(address)) + throw std::runtime_error("Invalid format"); + + size_t pos = 0; + std::map<uint64_t, CommonInformationEntry*> cie_map; + for (uint32_t i = 0; i < size; ) { + uint32_t length = file.ReadDWord(); + if (!length) + break; + + uint64_t cur_address = address + i; + EncodedData data(cur_address + sizeof(length), file.cpu_address_size()); + data.ReadFromFile(file, length); + pos = 0; + + uint32_t cie_id = data.ReadDWord(&pos); + if (cie_id == 0) { + // CIE + uint8_t fde_encoding = DW_EH_PE_absptr; + uint8_t lsda_encoding = DW_EH_PE_omit; + uint8_t personality_encoding = DW_EH_PE_omit; + uint64_t personality_routine = 0; + + uint8_t version = data.ReadByte(&pos); + if (version != 1 && version != 3) + throw std::runtime_error("Invalid CIE version"); + std::string augmentation = data.ReadString(&pos); + uint64_t code_alignment_factor = data.ReadUleb128(&pos); + uint64_t data_alignment_factor = data.ReadSleb128(&pos); + uint8_t return_address_register = data.ReadByte(&pos); + if (*augmentation.c_str() == 'z') { + data.ReadUleb128(&pos); + for (size_t j = 1; j < augmentation.size(); j++) { + switch (augmentation[j]) { + case 'L': + lsda_encoding = data.ReadByte(&pos); + break; + case 'R': + fde_encoding = data.ReadByte(&pos); + break; + case 'P': + { + personality_encoding = data.ReadByte(&pos); + personality_routine = data.ReadEncoding(personality_encoding, &pos); + } + break; + } + } + } + + std::vector<uint8_t> initial_instructions; + initial_instructions.resize(length - pos); + if (!initial_instructions.empty()) + data.Read(initial_instructions.data(), initial_instructions.size(), &pos); + + CommonInformationEntry *cie = cie_list_->Add(version, augmentation, code_alignment_factor, data_alignment_factor, return_address_register, fde_encoding, lsda_encoding, personality_encoding, personality_routine, initial_instructions); + cie_map[cur_address] = cie; + } else { + // FDE + std::map<uint64_t, CommonInformationEntry*>::iterator it = cie_map.find(cur_address + sizeof(length) - cie_id); + if (it == cie_map.end()) + throw std::runtime_error("Invalid CIE pointer"); + + CommonInformationEntry *cie = it->second; + uint64_t begin = data.ReadEncoding(cie->fde_encoding(), &pos); + uint64_t end = begin + data.ReadEncoding(cie->fde_encoding() & 0x0f, &pos); + + uint64_t lsda_address = 0; + if (*cie->augmentation().c_str() == 'z') { + data.ReadUleb128(&pos); + if (cie->augmentation().find('L') != std::string::npos) { + size_t old_pos = pos; + if (data.ReadEncoding(cie->lsda_encoding() & 0x0f, &pos)) { + pos = old_pos; + lsda_address = data.ReadEncoding(cie->lsda_encoding(), &pos); + } + } + } + std::vector<uint8_t> call_frame_instructions; + call_frame_instructions.resize(length - pos); + if (!call_frame_instructions.empty()) + data.Read(call_frame_instructions.data(), call_frame_instructions.size(), &pos); + + uint32_t compact_encoding = DwarfParser::CreateCompactEncoding(file, call_frame_instructions, cie, begin); + if (lsda_address) + compact_encoding |= UNWIND_HAS_LSDA; + Add(cur_address, begin, end, lsda_address, cie, call_frame_instructions, compact_encoding); + } + + i += sizeof(length) + length; + } +} + +void MacRuntimeFunctionList::ReadCompactInfo(MacArchitecture &file, uint64_t address, uint32_t size) +{ + if (!file.AddressSeek(address)) + throw std::runtime_error("Invalid format"); + + uint64_t base_address = file.segment_list()->GetBaseSegment()->address(); + + uint64_t pos = file.Tell(); + + unwind_info_section_header header; + file.Read(&header, sizeof(header)); + if (header.version != UNWIND_SECTION_VERSION) + throw std::runtime_error("Invalid unwind section version"); + + file.Seek(pos + header.commonEncodingsArraySectionOffset); + std::vector<uint32_t> common_encoding_list; + for (size_t i = 0; i < header.commonEncodingsArrayCount; i++) { + uint32_t entry = file.ReadDWord(); + common_encoding_list.push_back(entry); + } + + file.Seek(pos + header.personalityArraySectionOffset); + std::vector<uint32_t> personality_list; + for (size_t i = 0; i < header.personalityArrayCount; i++) { + uint32_t entry = file.ReadDWord(); + personality_list.push_back(entry); + } + + file.Seek(pos + header.indexSectionOffset); + std::vector<unwind_info_section_header_index_entry> index_entry_list; + for (size_t i = 0; i < header.indexCount; i++) { + unwind_info_section_header_index_entry entry; + file.Read(&entry, sizeof(entry)); + index_entry_list.push_back(entry); + } + + size_t lsda_entry_count = (index_entry_list[index_entry_list.size() - 1].lsdaIndexArraySectionOffset - index_entry_list[0].lsdaIndexArraySectionOffset) / sizeof(unwind_info_section_header_lsda_index_entry); + file.Seek(pos + index_entry_list[0].lsdaIndexArraySectionOffset); + std::vector<unwind_info_section_header_lsda_index_entry> lsda_entry_list; + for (size_t i = 0; i < lsda_entry_count; i++) { + unwind_info_section_header_lsda_index_entry entry; + file.Read(&entry, sizeof(entry)); + lsda_entry_list.push_back(entry); + } + + struct Info { + uint64_t address; + uint32_t offset; + uint32_t encoding; + Info(uint64_t address_, uint32_t offset_, uint32_t encoding_) : address(address_), offset(offset_), encoding(encoding_) {} + }; + + if (index_entry_list.empty() || index_entry_list[index_entry_list.size() - 1].secondLevelPagesSectionOffset) + throw std::runtime_error("Invalid format"); + + std::vector<Info> info_list; + for (size_t i = 0; i < index_entry_list.size() - 1; i++) { + size_t page_offset = index_entry_list[i].secondLevelPagesSectionOffset; + + file.Seek(pos + page_offset); + uint32_t kind = file.ReadDWord(); + file.Seek(pos + page_offset); + + switch (kind) { + case UNWIND_SECOND_LEVEL_REGULAR: + { + unwind_info_regular_second_level_page_header page_header; + file.Read(&page_header, sizeof(page_header)); + + file.Seek(pos + page_offset + page_header.entryPageOffset); + uint64_t cur_address = address + page_offset + page_header.entryPageOffset; + for (size_t j = 0; j < page_header.entryCount; j++) { + unwind_info_regular_second_level_entry entry; + file.Read(&entry, sizeof(entry)); + + info_list.push_back(Info(cur_address, entry.functionOffset, entry.encoding)); + cur_address += sizeof(entry); + } + } + break; + case UNWIND_SECOND_LEVEL_COMPRESSED: + { + unwind_info_compressed_second_level_page_header page_header; + file.Read(&page_header, sizeof(page_header)); + + file.Seek(pos + page_offset + page_header.encodingsPageOffset); + std::vector<uint32_t> encoding_list; + for (size_t j = 0; j < page_header.encodingsCount; j++) { + uint32_t entry = file.ReadDWord(); + encoding_list.push_back(entry); + } + + file.Seek(pos + page_offset + page_header.entryPageOffset); + uint64_t cur_address = address + page_offset + page_header.entryPageOffset; + for (size_t j = 0; j < page_header.entryCount; j++) { + uint32_t entry = file.ReadDWord(); + uint32_t function_offset = index_entry_list[i].functionOffset + UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entry); + uint32_t encoding_index = UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entry); + uint32_t encoding = (encoding_index < common_encoding_list.size()) ? common_encoding_list[encoding_index] : encoding_list[encoding_index - common_encoding_list.size()]; + + info_list.push_back(Info(cur_address, function_offset, encoding)); + cur_address += sizeof(entry); + } + } + break; + default: + throw std::runtime_error("Invalid page header"); + } + } + info_list.push_back(Info(0, index_entry_list[index_entry_list.size() - 1].functionOffset - 1, 0)); + + std::map<uint64_t, CommonInformationEntry *> cie_map; + std::vector<uint8_t> dummy; + for (size_t i = 0; i < info_list.size() - 1; i++) { + uint32_t function_offset = info_list[i].offset; + uint32_t encoding = info_list[i].encoding; + uint64_t lsda_address = 0; + if (encoding & UNWIND_HAS_LSDA) { + for (size_t k = 0; k < lsda_entry_list.size(); k++) { + if (lsda_entry_list[k].functionOffset == function_offset) { + lsda_address = base_address + lsda_entry_list[k].lsdaOffset; + break; + } + } + if (!lsda_address) + throw std::runtime_error("Invalid lsda index"); + } + + uint64_t personality_routine = 0; + if (encoding & UNWIND_PERSONALITY_MASK) { + uint32_t personality_index = (encoding & UNWIND_PERSONALITY_MASK) >> 28; + if (personality_index > personality_list.size()) + throw std::runtime_error("Invalid personality index"); + + personality_routine = base_address + personality_list[personality_index - 1]; + encoding &= ~UNWIND_PERSONALITY_MASK; + } + + CommonInformationEntry *cie; + std::map<uint64_t, CommonInformationEntry *>::const_iterator it = cie_map.find(personality_routine); + if (it == cie_map.end()) { + cie = cie_list_->Add(0, std::string(), 0, 0, 0, 0, 0, 0, personality_routine, std::vector<uint8_t>()); + cie_map[personality_routine] = cie; + } else { + cie = it->second; + } + + Add(info_list[i].address, base_address + function_offset, base_address + info_list[i + 1].offset, lsda_address, cie, dummy, encoding); + } +} + +void MacRuntimeFunctionList::ReadBorlandInfo(MacArchitecture &file, uint64_t address) +{ + struct BORLAND_UNWIND_HEADER { + uint32_t magic; + uint32_t delta; + uint32_t count; + }; + + struct BORLAND_UNWIND_BLOCK { + uint32_t begin; + uint32_t end; + uint32_t count; + uint32_t offset; + }; + + struct BORLAND_UNWIND_INFO { + uint32_t begin; + uint32_t end; + }; + + if (!file.AddressSeek(address)) + return; + + BORLAND_UNWIND_HEADER unwind_header; + file.Read(&unwind_header, sizeof(unwind_header)); + if (unwind_header.magic != 0xFBDE0007) + return; + + address_ = address; + + uint64_t base_address = address - unwind_header.delta; + std::vector<uint8_t> dummy; + for (size_t i = 0; i < unwind_header.count; i++) { + BORLAND_UNWIND_BLOCK unwind_block; + file.Read(&unwind_block, sizeof(unwind_block)); + if (unwind_block.end > unwind_block.begin && unwind_block.count) { + address = address_ + unwind_block.offset; + uint64_t pos = file.Tell(); + if (!file.AddressSeek(address)) + throw std::runtime_error("Invalid runtime function address"); + BORLAND_UNWIND_INFO unwind_info; + for (size_t j = 0; j < unwind_block.count; j++) { + file.Read(&unwind_info, sizeof(unwind_info)); + Add(address, unwind_info.begin + base_address, unwind_info.end + base_address, unwind_info.end + base_address - 30, NULL, dummy, 0); + address += sizeof(unwind_info); + } + file.Seek(pos); + } + } +} + +size_t MacRuntimeFunctionList::WriteToFile(MacArchitecture &file, bool compact_info) +{ + Sort(); + + if (cie_list_->count()) + return compact_info ? WriteCompactInfo(file) : WriteDwarfInfo(file); + return 0; +} + +size_t MacRuntimeFunctionList::WriteDwarfInfo(MacArchitecture &file) +{ + size_t res = 0; + uint64_t address = file.AddressTell(); + std::map<CommonInformationEntry*, uint64_t> cie_map; + for (size_t i = 0; i < count(); i++) { + MacRuntimeFunction *func = item(i); + CommonInformationEntry *cie = func->cie(); + if (cie->version() == 0) + continue; + + std::map<CommonInformationEntry*, uint64_t>::iterator it = cie_map.find(cie); + uint64_t cie_address; + if (it == cie_map.end()) { + // write CIE + cie_address = address + res; + + EncodedData data(cie_address + sizeof(uint32_t), file.cpu_address_size()); + data.WriteDWord(0); + data.WriteByte(cie->version()); + data.WriteString(cie->augmentation()); + data.WriteUleb128(cie->code_alignment_factor()); + data.WriteSleb128(cie->data_alignment_factor()); + data.WriteByte(cie->return_address_register()); + if (*cie->augmentation().c_str() == 'z') { + EncodedData tmp(data.address() + data.size() + 1, file.cpu_address_size()); + for (size_t j = 1; j < cie->augmentation().size(); j++) { + switch (cie->augmentation().at(j)) { + case 'L': + tmp.WriteByte(cie->lsda_encoding()); + break; + case 'R': + tmp.WriteByte(cie->fde_encoding()); + break; + case 'P': + { + tmp.WriteByte(cie->personality_encoding()); + tmp.WriteEncoding(cie->personality_encoding(), cie->personality_routine()); + } + break; + } + } + data.WriteByte(static_cast<uint8_t>(tmp.size())); + data.Write(tmp.data(), tmp.size()); + } + data.Write(cie->initial_instructions().data(), cie->initial_instructions().size()); + data.resize(AlignValue(data.size(), sizeof(uint32_t)), 0); + + uint32_t size = static_cast<uint32_t>(data.size()); + res += file.Write(&size, sizeof(size)); + res += file.Write(data.data(), data.size()); + cie_map[cie] = cie_address; + } else { + cie_address = it->second; + } + + // write FDE + EncodedData data(address + res + sizeof(uint32_t), file.cpu_address_size()); + data.WriteDWord(static_cast<uint32_t>(data.address() - cie_address)); + data.WriteEncoding(cie->fde_encoding(), func->begin()); + data.WriteEncoding(cie->fde_encoding() & 0x0f, func->end() - func->begin()); + if (*cie->augmentation().c_str() == 'z') { + EncodedData tmp(data.address() + data.size() + 1, file.cpu_address_size()); + if (cie->augmentation().find('L') != std::string::npos) { + if (func->unwind_address()) + tmp.WriteEncoding(cie->lsda_encoding(), func->unwind_address()); + else + tmp.WriteEncoding(cie->lsda_encoding() & 0x0f, 0); + } + data.WriteByte(static_cast<uint8_t>(tmp.size())); + data.Write(tmp.data(), tmp.size()); + } + data.Write(func->call_frame_instructions().data(), func->call_frame_instructions().size()); + data.resize(AlignValue(data.size(), sizeof(uint32_t)), 0); + + uint32_t size = static_cast<uint32_t>(data.size()); + res += file.Write(&size, sizeof(size)); + res += file.Write(data.data(), data.size()); + } + + return res; +} + +size_t MacRuntimeFunctionList::WriteCompactInfo(MacArchitecture &file) +{ + size_t i, j; + MacRuntimeFunction *func; + uint32_t encoding; + size_t res = 0; + + std::vector<uint32_t> encoding_list; + std::map<uint64_t, uint32_t> personality_map; + std::map<uint32_t, uint8_t> common_encoding_map; + std::map<uint32_t, size_t> used_encoding_map; + std::map<uint32_t, uint32_t> lsda_map; + std::map<uint32_t, uint32_t> lsda_offset_map; + std::map<uint32_t, uint32_t> page_offset_map; + std::map<uint32_t, std::vector<uint8_t> > index_map; + + uint64_t start_pos = file.Tell(); + + uint64_t base_address = file.segment_list()->GetBaseSegment()->address(); + + size_t max_used_count = 0; + + for (i = 0; i < count(); i++) { + func = item(i); + encoding = func->compact_encoding(); + CommonInformationEntry *cie = func->cie(); + if (cie->personality_routine()) { + std::map<uint64_t, uint32_t>::const_iterator it = personality_map.find(cie->personality_routine()); + if (it == personality_map.end()) { + uint32_t next = static_cast<uint32_t>(personality_map.size() + 1); + personality_map[cie->personality_routine()] = next; + } + uint32_t personality_index = personality_map[cie->personality_routine()]; + encoding |= personality_index << __builtin_ctz(UNWIND_PERSONALITY_MASK); + } + encoding_list.push_back(encoding); + + if ((func->compact_encoding() & UNWIND_X86_MODE_MASK) != UNWIND_X86_MODE_DWARF) { + used_encoding_map[encoding] += 1; + if (max_used_count < used_encoding_map[encoding]) + max_used_count = used_encoding_map[encoding]; + } + + uint32_t function_offset = static_cast<uint32_t>(func->begin() - base_address); + lsda_offset_map[function_offset] = static_cast<uint32_t>(lsda_map.size() * sizeof(unwind_info_section_header_lsda_index_entry)); + if (func->unwind_address()) + lsda_map[function_offset] = static_cast<uint32_t>(func->unwind_address() - base_address); + } + + for (size_t used_count = max_used_count; used_count > 1; used_count--) { + for (std::map<uint32_t, size_t>::const_iterator ue_it = used_encoding_map.begin(); ue_it != used_encoding_map.end(); ue_it++) { + if (ue_it->second == used_count) { + uint32_t next = static_cast<uint8_t>(common_encoding_map.size()); + common_encoding_map[ue_it->first] = next; + if (common_encoding_map.size() == 127) { + used_count = 1; + break; + } + } + } + } + + // calc pages + size_t page_size = 0x1000; + for (i = count(); i > 0; ) { + std::map<uint32_t, uint8_t> page_encoding_map; + uint16_t entry_count = 0; + uint16_t regular_entry_count = static_cast<uint16_t>(std::min((page_size - sizeof(unwind_info_regular_second_level_page_header)) / sizeof(unwind_info_regular_second_level_entry), i)); + size_t max_size = page_size - sizeof(unwind_info_compressed_second_level_page_header); + + uint64_t last_address = item(i - 1)->begin(); + for (j = i; j > 0; j--) { + func = item(j - 1); + if (last_address - func->begin() > 0x00ffffff) + break; + + encoding = encoding_list[j - 1]; + if (common_encoding_map.find(encoding) == common_encoding_map.end() && page_encoding_map.find(encoding) == page_encoding_map.end()) { + uint32_t encoding_index = static_cast<uint32_t>(common_encoding_map.size() + page_encoding_map.size()); + if (encoding_index > 0xff) + break; + + page_encoding_map[encoding] = static_cast<uint8_t>(encoding_index); + } + + if ((page_encoding_map.size() + entry_count) * sizeof(uint32_t) > max_size) + break; + + entry_count++; + } + + bool is_regular_page = (regular_entry_count > entry_count); + if (is_regular_page) + entry_count = regular_entry_count; + i -= entry_count; + + uint64_t start_address = item(i)->begin(); + std::vector<uint8_t> page_data; + if (is_regular_page) { + // build regular second level + unwind_info_regular_second_level_page_header header; + header.kind = UNWIND_SECOND_LEVEL_REGULAR; + header.entryPageOffset = sizeof(header); + header.entryCount = entry_count; + + page_data.insert(page_data.end(), reinterpret_cast<const uint8_t *>(&header), reinterpret_cast<const uint8_t *>(&header) + sizeof(header)); + for (j = 0; j < entry_count; j++) { + func = item(i + j); + encoding = encoding_list[i + j]; + + unwind_info_regular_second_level_entry entry; + entry.functionOffset = static_cast<uint32_t>(func->begin() - base_address); + entry.encoding = func->compact_encoding(); + + page_data.insert(page_data.end(), reinterpret_cast<const uint8_t *>(&entry), reinterpret_cast<const uint8_t *>(&entry) + sizeof(entry)); + } + } else { + // build compressed second level + unwind_info_compressed_second_level_page_header header; + header.kind = UNWIND_SECOND_LEVEL_COMPRESSED; + header.entryPageOffset = sizeof(header); + header.entryCount = entry_count; + header.encodingsPageOffset = header.entryPageOffset + entry_count * sizeof(uint32_t); + header.encodingsCount = static_cast<uint16_t>(page_encoding_map.size()); + + page_data.insert(page_data.end(), reinterpret_cast<const uint8_t *>(&header), reinterpret_cast<const uint8_t *>(&header) + sizeof(header)); + for (j = 0; j < entry_count; j++) { + func = item(i + j); + encoding = encoding_list[i + j]; + + uint8_t encoding_index; + std::map<uint32_t, uint8_t>::const_iterator it = common_encoding_map.find(encoding); + if (it != common_encoding_map.end()) { + encoding_index = it->second; + } else { + encoding_index = page_encoding_map[encoding]; + } + + uint32_t entry = (encoding_index << 24) | static_cast<uint32_t>((func->begin() - start_address)); + page_data.insert(page_data.end(), reinterpret_cast<const uint8_t *>(&entry), reinterpret_cast<const uint8_t *>(&entry) + sizeof(entry)); + } + + if (!page_encoding_map.empty()) { + std::vector<uint32_t> page_encoding_list; + page_encoding_list.resize(page_encoding_map.size()); + for (std::map<uint32_t, uint8_t>::const_iterator it = page_encoding_map.begin(); it != page_encoding_map.end(); it++) { + page_encoding_list[it->second - common_encoding_map.size()] = it->first; + } + + for (j = 0; j < page_encoding_list.size(); j++) { + encoding = page_encoding_list[j]; + page_data.insert(page_data.end(), reinterpret_cast<const uint8_t *>(&encoding), reinterpret_cast<const uint8_t *>(&encoding) + sizeof(encoding)); + } + } + } + + index_map[static_cast<uint32_t>(start_address - base_address)] = page_data; + } + + // write section header + unwind_info_section_header header; + header.version = UNWIND_SECTION_VERSION; + header.commonEncodingsArraySectionOffset = sizeof(header); + header.commonEncodingsArrayCount = static_cast<uint32_t>(common_encoding_map.size()); + header.personalityArraySectionOffset = header.commonEncodingsArraySectionOffset + header.commonEncodingsArrayCount * sizeof(uint32_t); + header.personalityArrayCount = static_cast<uint32_t>(personality_map.size()); + header.indexSectionOffset = header.personalityArraySectionOffset + header.personalityArrayCount * sizeof(uint32_t); + header.indexCount = static_cast<uint32_t>(index_map.size() + 1); + res += file.Write(&header, sizeof(header)); + + // write common encoding + if (!common_encoding_map.empty()) { + std::vector<uint32_t> common_encoding_list; + common_encoding_list.resize(common_encoding_map.size()); + for (std::map<uint32_t, uint8_t>::const_iterator it = common_encoding_map.begin(); it != common_encoding_map.end(); it++) { + common_encoding_list[it->second] = it->first; + } + res += file.Write(common_encoding_list.data(), common_encoding_list.size() * sizeof(common_encoding_list[0])); + } + + // write personality + if (!personality_map.empty()) { + std::vector<uint32_t> personality_list; + personality_list.resize(personality_map.size()); + for (std::map<uint64_t, uint32_t>::const_iterator it = personality_map.begin(); it != personality_map.end(); it++) { + personality_list[it->second - 1] = static_cast<uint32_t>(it->first - base_address); + } + res += file.Write(personality_list.data(), personality_list.size() * sizeof(personality_list[0])); + } + + // write index + size_t lsda_start = res + header.indexCount * sizeof(unwind_info_section_header_index_entry); + size_t lsda_end = lsda_start + lsda_map.size() * sizeof(unwind_info_section_header_lsda_index_entry); + size_t pages_offset = lsda_end; + for (std::map<uint32_t, std::vector<uint8_t> >::const_iterator it = index_map.begin(); it != index_map.end(); it++) { + if (it != index_map.begin()) + pages_offset = static_cast<uint32_t>(AlignValue(start_pos + pages_offset, page_size) - start_pos); + page_offset_map[it->first] = static_cast<uint32_t>(pages_offset); + + unwind_info_section_header_index_entry entry; + entry.functionOffset = it->first; + entry.secondLevelPagesSectionOffset = static_cast<uint32_t>(pages_offset); + entry.lsdaIndexArraySectionOffset = static_cast<uint32_t>(lsda_start + lsda_offset_map[it->first]); + res += file.Write(&entry, sizeof(entry)); + + pages_offset += it->second.size(); + } + { + unwind_info_section_header_index_entry entry; + entry.functionOffset = (count() > 0) ? static_cast<uint32_t>(item(count() - 1)->end() + 1 - base_address) : 0; + entry.secondLevelPagesSectionOffset = 0; + entry.lsdaIndexArraySectionOffset = static_cast<uint32_t>(lsda_end); + res += file.Write(&entry, sizeof(entry)); + } + + // write lsda index + if (!lsda_map.empty()) { + std::vector<unwind_info_section_header_lsda_index_entry> lsda_list; + for (std::map<uint32_t, uint32_t>::const_iterator it = lsda_map.begin(); it != lsda_map.end(); it++) { + unwind_info_section_header_lsda_index_entry entry; + entry.functionOffset = it->first; + entry.lsdaOffset = it->second; + lsda_list.push_back(entry); + } + res += file.Write(lsda_list.data(), lsda_list.size() * sizeof(lsda_list[0])); + } + + // write second level data + for (std::map<uint32_t, std::vector<uint8_t> >::const_iterator it = index_map.begin(); it != index_map.end(); it++) { + uint64_t old_size = file.size(); + res += static_cast<size_t>(file.Resize(start_pos + page_offset_map[it->first]) - old_size); + res += file.Write(it->second.data(), it->second.size()); + } + + return res; +} + +void MacRuntimeFunctionList::Rebase(uint64_t delta_base) +{ + cie_list_->Rebase(delta_base); + BaseRuntimeFunctionList::Rebase(delta_base); +} + +/** + * MacArchitecture + */ + +MacArchitecture::MacArchitecture(MacFile *owner, uint64_t offset, uint64_t size) + : BaseArchitecture(owner, offset, size), function_list_(NULL), virtual_machine_list_(NULL), + cpu_type_(0), cpu_subtype_(0), cpu_address_size_(osDWord), image_base_(0), entry_point_(0), file_type_(0), + cmds_size_(0), flags_(0), header_size_(0), segment_alignment_(0x1000), file_alignment_(0x1000), + linkedit_segment_(NULL), optimized_segment_count_(0), header_segment_(NULL), max_header_size_(0), + runtime_functions_section_(NULL), unwind_info_section_(0), sdk_(0) +{ + memset(&symtab_, 0, sizeof(symtab_)); + memset(&dysymtab_, 0, sizeof(dysymtab_)); + memset(&dyld_info_, 0, sizeof(dyld_info_)); + + command_list_ = new MacLoadCommandList(this); + section_list_ = new MacSectionList(this); + segment_list_ = new MacSegmentList(this); + symbol_list_ = new MacSymbolList(); + import_list_ = new MacImportList(this); + export_list_ = new MacExportList(this); + indirect_symbol_list_ = new MacIndirectSymbolList(); + ext_ref_symbol_list_ = new MacExtRefSymbolList(); + fixup_list_ = new MacFixupList(); + runtime_function_list_ = new MacRuntimeFunctionList(); +} + +MacArchitecture::MacArchitecture(MacFile *owner, const MacArchitecture &src) + : BaseArchitecture(owner, src), function_list_(NULL), virtual_machine_list_(NULL), + linkedit_segment_(NULL), runtime_functions_section_(NULL), unwind_info_section_(NULL) +{ + size_t i, j, k; + + entry_point_ = src.entry_point_; + cpu_type_ = src.cpu_type_; + cpu_subtype_ = src.cpu_subtype_; + image_base_ = src.image_base_; + file_type_ = src.file_type_; + cmds_size_ = src.cmds_size_; + flags_ = src.flags_; + header_size_ = src.header_size_; + cpu_address_size_ = src.cpu_address_size_; + symtab_ = src.symtab_; + string_table_ = src.string_table_; + dysymtab_ = src.dysymtab_; + dyld_info_ = src.dyld_info_; + segment_alignment_ = src.segment_alignment_; + file_alignment_ = src.file_alignment_; + max_header_size_ = src.max_header_size_; + sdk_ = src.sdk_; + + command_list_ = src.command_list_->Clone(this); + segment_list_ = src.segment_list_->Clone(this); + section_list_ = src.section_list_->Clone(this); + symbol_list_ = src.symbol_list_->Clone(); + import_list_ = src.import_list_->Clone(this); + export_list_ = src.export_list_->Clone(this); + indirect_symbol_list_ = src.indirect_symbol_list_->Clone(); + ext_ref_symbol_list_ = src.ext_ref_symbol_list_->Clone(); + fixup_list_ = src.fixup_list_->Clone(); + runtime_function_list_ = src.runtime_function_list_->Clone(); + + if (src.linkedit_segment_) + linkedit_segment_ = segment_list_->item(src.segment_list_->IndexOf(src.linkedit_segment_)); + if (src.header_segment_) + header_segment_ = segment_list_->item(src.segment_list_->IndexOf(src.header_segment_)); + if (src.runtime_functions_section_) + runtime_functions_section_ = section_list_->item(src.section_list_->IndexOf(src.runtime_functions_section_)); + if (src.unwind_info_section_) + unwind_info_section_ = section_list_->item(src.section_list_->IndexOf(src.unwind_info_section_)); + if (src.function_list_) + function_list_ = src.function_list_->Clone(this); + if (src.virtual_machine_list_) + virtual_machine_list_ = src.virtual_machine_list_->Clone(); + + for (i = 0; i < src.section_list()->count(); i++) { + MacSegment *segment = src.section_list()->item(i)->parent(); + if (segment) + section_list_->item(i)->set_parent(segment_list_->item(src.segment_list_->IndexOf(segment))); + } + + for (i = 0; i < src.indirect_symbol_list()->count(); i++) { + MacSymbol *symbol = src.indirect_symbol_list()->item(i)->symbol(); + if (symbol) + indirect_symbol_list_->item(i)->set_symbol(symbol_list_->item(src.symbol_list_->IndexOf(symbol))); + } + + for (i = 0; i < src.ext_ref_symbol_list_->count(); i++) { + MacSymbol *symbol = src.ext_ref_symbol_list_->item(i)->symbol(); + if (symbol) + ext_ref_symbol_list_->item(i)->set_symbol(symbol_list_->item(src.symbol_list_->IndexOf(symbol))); + } + + for (i = 0; i < src.import_list()->count(); i++) { + MacImport *import = src.import_list()->item(i); + for (j = 0; j < import->count(); j++) { + MacImportFunction *import_function = import->item(j); + MapFunction *map_function = import_function->map_function(); + if (map_function) + import_list_->item(i)->item(j)->set_map_function(map_function_list()->item(src.map_function_list()->IndexOf(map_function))); + + MacSymbol *symbol = import_function->symbol(); + if (symbol) + import_list_->item(i)->item(j)->set_symbol(symbol_list_->item(src.symbol_list_->IndexOf(symbol))); + } + } + + for (i = 0; i < src.fixup_list()->count(); i++) { + MacSymbol *symbol = src.fixup_list()->item(i)->symbol(); + if (symbol) + fixup_list_->item(i)->set_symbol(symbol_list_->item(src.symbol_list_->IndexOf(symbol))); + } + + for (i = 0; i < src.export_list()->count(); i++) { + MacSymbol *symbol = src.export_list()->item(i)->symbol(); + if (symbol) + export_list_->item(i)->set_symbol(symbol_list_->item(src.symbol_list_->IndexOf(symbol))); + } + + if (function_list_) { + for (i = 0; i < function_list_->count(); i++) { + IntelFunction *func = reinterpret_cast<IntelFunction *>(function_list_->item(i)); + for (j = 0; j < func->count(); j++) { + IntelCommand *command = func->item(j); + + for (k = 0; k < 3; k++) { + IntelOperand operand = command->operand(k); + if (operand.type == otNone) + break; + + if (operand.fixup) + command->set_operand_fixup(k, fixup_list_->GetFixupByAddress(operand.fixup->address())); + } + } + for (j = 0; j < func->function_info_list()->count(); j++) { + FunctionInfo *info = func->function_info_list()->item(j); + if (info->source()) + info->set_source(runtime_function_list_->GetFunctionByAddress(info->source()->begin())); + } + } + } +} + +MacArchitecture::~MacArchitecture() +{ + delete segment_list_; + delete section_list_; + delete symbol_list_; + delete import_list_; + delete indirect_symbol_list_; + delete ext_ref_symbol_list_; + delete command_list_; + delete export_list_; + delete fixup_list_; + delete runtime_function_list_; + delete function_list_; + delete virtual_machine_list_; +} + +MacArchitecture *MacArchitecture::Clone(IFile *file) const +{ + MacArchitecture *arch = new MacArchitecture(dynamic_cast<MacFile *>(file), *this); + return arch; +} + +std::string MacArchitecture::name() const +{ + static const struct { + const char *name; + cpu_type_t cputype; + cpu_subtype_t cpusubtype; + } arch_flags[] = { + { "any", CPU_TYPE_ANY, CPU_SUBTYPE_MULTIPLE }, + { "little", CPU_TYPE_ANY, CPU_SUBTYPE_LITTLE_ENDIAN }, + { "big", CPU_TYPE_ANY, CPU_SUBTYPE_BIG_ENDIAN }, + + /* 64-bit Mach-O architectures */ + + /* architecture families */ + { "ppc64", CPU_TYPE_POWERPC64, CPU_SUBTYPE_POWERPC_ALL }, + { "x86_64", CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL }, + /* specific architecture implementations */ + { "ppc970-64", CPU_TYPE_POWERPC64, CPU_SUBTYPE_POWERPC_970 }, + + /* 32-bit Mach-O architectures */ + + /* architecture families */ + { "ppc", CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL }, + { "i386", CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL }, + { "m68k", CPU_TYPE_MC680x0, CPU_SUBTYPE_MC680x0_ALL }, + { "hppa", CPU_TYPE_HPPA, CPU_SUBTYPE_HPPA_ALL }, + { "sparc", CPU_TYPE_SPARC, CPU_SUBTYPE_SPARC_ALL }, + { "m88k", CPU_TYPE_MC88000, CPU_SUBTYPE_MC88000_ALL }, + { "i860", CPU_TYPE_I860, CPU_SUBTYPE_I860_ALL }, + { "arm", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_ALL }, + /* specific architecture implementations */ + { "ppc601", CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_601 }, + { "ppc603", CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_603 }, + { "ppc603e",CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_603e }, + { "ppc603ev",CPU_TYPE_POWERPC,CPU_SUBTYPE_POWERPC_603ev }, + { "ppc604", CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_604 }, + { "ppc604e",CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_604e }, + { "ppc750", CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_750 }, + { "ppc7400",CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_7400 }, + { "ppc7450",CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_7450 }, + { "ppc970", CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_970 }, + { "i486", CPU_TYPE_I386, CPU_SUBTYPE_486 }, + { "i486SX", CPU_TYPE_I386, CPU_SUBTYPE_486SX }, + { "pentium",CPU_TYPE_I386, CPU_SUBTYPE_PENT }, /* same as i586 */ + { "i586", CPU_TYPE_I386, CPU_SUBTYPE_586 }, + { "pentpro", CPU_TYPE_I386, CPU_SUBTYPE_PENTPRO }, /* same as i686 */ + { "i686", CPU_TYPE_I386, CPU_SUBTYPE_PENTPRO }, + { "pentIIm3",CPU_TYPE_I386, CPU_SUBTYPE_PENTII_M3 }, + { "pentIIm5",CPU_TYPE_I386, CPU_SUBTYPE_PENTII_M5 }, + { "pentium4",CPU_TYPE_I386, CPU_SUBTYPE_PENTIUM_4 }, + { "m68030", CPU_TYPE_MC680x0, CPU_SUBTYPE_MC68030_ONLY }, + { "m68040", CPU_TYPE_MC680x0, CPU_SUBTYPE_MC68040 }, + { "hppa7100LC", CPU_TYPE_HPPA, CPU_SUBTYPE_HPPA_7100LC }, + { "armv4t", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V4T}, + { "armv5", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V5TEJ}, + { "xscale", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_XSCALE}, + { "armv6", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6 }, + }; + + for(size_t i = 0; i < _countof(arch_flags); i++) { + if(arch_flags[i].cputype == cpu_type_ && + (arch_flags[i].cpusubtype & ~CPU_SUBTYPE_MASK) == + (cpu_subtype_ & ~CPU_SUBTYPE_MASK)) + return std::string(arch_flags[i].name); + } + + return string_format("unknown 0x%X", cpu_type_); +} + +uint64_t MacArchitecture::entry_point() const +{ + return entry_point_; +} + +OpenStatus MacArchitecture::ReadFromFile(uint32_t mode) +{ + size_t i; + mach_header mh; + + Seek(0); + + if (size() < sizeof(mh)) + return osUnknownFormat; + + Read(&mh, sizeof(mh)); + if (mh.magic != MH_MAGIC && mh.magic != MH_MAGIC_64) + return osUnknownFormat; + + if (mh.magic == MH_MAGIC_64) + ReadDWord(); + + cpu_address_size_ = (mh.magic == MH_MAGIC) ? osDWord : osQWord; + cpu_type_ = mh.cputype; + cpu_subtype_ = mh.cpusubtype; + switch (cpu_type_) { + case CPU_TYPE_I386: + case CPU_TYPE_X86_64: + // supported cpu + break; + default: + return osUnsupportedCPU; + } + + file_type_ = mh.filetype; + switch (file_type_) { + case MH_EXECUTE: + case MH_DYLIB: + case MH_BUNDLE: + // supported types + break; + default: + return osUnsupportedSubsystem; + } + + cmds_size_ = mh.sizeofcmds; + flags_ = mh.flags; + header_size_ = static_cast<uint32_t>(Tell()) + cmds_size_; + + command_list_->ReadFromFile(*this, mh.ncmds); + segment_list_->ReadFromFile(*this); + + image_base_ = 0; + for (i = 0; i < segment_list_->count(); i++) { + uint64_t segment_base = segment_list_->item(i)->address() & 0xffffffff00000000ull; + if (!image_base_) { + image_base_ = segment_base; + } else if (image_base_ != segment_base) { + return osInvalidFormat; + } + } + + for (i = 0; i < command_list_->count(); i++) { + MacLoadCommand *lc = command_list_->item(i); + switch (lc->type()) { + case LC_SYMTAB: + if (lc->size() != sizeof(symtab_)) + throw std::runtime_error("Invalid symtab_command size"); + + Seek(lc->address()); + Read(&symtab_, sizeof(symtab_)); + break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + if (lc->size() != sizeof(dyld_info_command)) + throw std::runtime_error("Invalid dyld_info_command size"); + + Seek(lc->address()); + Read(&dyld_info_, sizeof(dyld_info_)); + break; + case LC_DYSYMTAB: + if (lc->size() != sizeof(dysymtab_)) + throw std::runtime_error("Invalid dysymtab_command size"); + + Seek(lc->address()); + Read(&dysymtab_, sizeof(dysymtab_)); + break; + } + } + + // parse entry point command + ILoadCommand *lc = command_list_->GetCommandByType(LC_MAIN); + if (!lc) + lc = command_list_->GetCommandByType(LC_UNIXTHREAD); + if (lc) { + switch (lc->type()) { + case LC_UNIXTHREAD: + { + thread_command command; + Seek(lc->address()); + Read(&command, sizeof(command)); + x86_state_hdr_t state_hdr; + Read(&state_hdr, sizeof(state_hdr)); + if (cpu_type_ == CPU_TYPE_I386 && state_hdr.flavor == x86_THREAD_STATE32) { + x86_thread_state32_t thread_state; + Read(&thread_state, sizeof(thread_state)); + entry_point_ = thread_state.__eip; + } else if (cpu_type_ == CPU_TYPE_X86_64 && state_hdr.flavor == x86_THREAD_STATE64) { + x86_thread_state64_t thread_state; + Read(&thread_state, sizeof(thread_state)); + entry_point_ = thread_state.__rip; + } + } + break; + case LC_MAIN: + { + MacSegment *base_segment = segment_list_->GetBaseSegment(); + if (!base_segment) + throw std::runtime_error("Format error"); + + entry_point_command command; + Seek(lc->address()); + Read(&command, sizeof(command)); + + entry_point_ = command.entryoff + base_segment->address(); + } + break; + } + } + + // parse OSX SDK + for (i = 0; i < command_list_->count(); i++) { + lc = command_list_->item(i); + if (lc->type() == LC_VERSION_MIN_MACOSX) { + Seek(lc->address()); + version_min_command command; + Read(&command, sizeof(command)); + sdk_ = command.sdk; + break; + } else if (lc->type() == LC_BUILD_VERSION) { + Seek(lc->address()); + build_version_command command; + Read(&command, sizeof(command)); + sdk_ = command.sdk; + break; + } + } + + linkedit_segment_ = segment_list_->GetSectionByName(SEG_LINKEDIT); + for (i = 0; i < section_list_->count(); i++) { + MacSection *section = section_list_->item(i); + if (section->parent()->name() == SEG_TEXT) { + if (section->name() == SECT_EH_FRAME || section->name() == SECT_INIT_TEXT) { + if (!runtime_functions_section_) + runtime_functions_section_ = section; + } else if (section->name() == SECT_UNWIND_INFO) { + if (!unwind_info_section_) + unwind_info_section_ = section; + } + } + } + + string_table_.ReadFromFile(*this); + symbol_list_->ReadFromFile(*this, symtab_.nsyms); + ext_ref_symbol_list_->ReadFromFile(*this); + indirect_symbol_list_->ReadFromFile(*this); + fixup_list_->ReadFromFile(*this); + import_list_->ReadFromFile(*this); + export_list_->ReadFromFile(*this); + runtime_function_list_->ReadFromFile(*this); + + header_segment_ = NULL; + max_header_size_ = header_size_; + for (i = 0; i < segment_list_->count(); i++) { + MacSegment *segment = segment_list_->item(i); + if (segment->physical_size() && segment->physical_offset() == 0) { + header_segment_ = segment; + break; + } + } + if (header_segment_) { + for (i = 0; i < section_list_->count(); i++) { + MacSection *section = section_list_->item(i); + if (section->parent() == header_segment_) { + max_header_size_ = section->physical_offset(); + break; + } + } + } + + if ((mode & foHeaderOnly) == 0) { + if (!owner()->file_name().empty()) { + MapFile map_file; + std::vector<uint64_t> segments; + for (size_t i = 0; i < segment_list()->count(); i++) { + segments.push_back(segment_list()->item(i)->address()); + } + if (std::find(segments.begin(), segments.end(), 0) == segments.end()) + segments.insert(segments.begin(), 0); + if (map_file.Parse(map_file_name().c_str(), segments)) + ReadMapFile(map_file); + } + + map_function_list()->ReadFromFile(*this); + + for (i = 0; i < indirect_symbol_list_->count(); i++) { + MacIndirectSymbol *indirect_symbol = indirect_symbol_list_->item(i); + MacSymbol *symbol = indirect_symbol->symbol(); + if (!symbol || (symbol->type() & (N_STAB | N_TYPE)) != N_UNDF) + continue; + + MacSection *section = section_list_->GetSectionByAddress(indirect_symbol->address()); + if (section->type() != S_SYMBOL_STUBS) + continue; + + MapFunction *map_function = map_function_list()->GetFunctionByAddress(indirect_symbol->address()); + if (!map_function) + map_function_list()->Add(indirect_symbol->address(), 0, segment_list_->GetMemoryTypeByAddress(indirect_symbol->address()) & mtExecutable ? otCode : otData, DemangleName(symbol->name())); + } + + for (i = 0; i < symbol_list_->count() - 1; i++) { + MacSymbol *symbol = symbol_list_->item(i); + if ((symbol->type() & (N_STAB | N_TYPE)) != N_SECT || symbol->name().empty()) + continue; + + uint32_t memory_type = segment_list_->GetMemoryTypeByAddress(symbol->value()); + if (memory_type == mtNone) + continue; + + MapFunction *map_function = map_function_list()->GetFunctionByAddress(symbol->value()); + if (!map_function) + map_function = map_function_list()->Add(symbol->value(), 0, otUnknown, DemangleName(symbol->name())); + + ObjectType type = otData; + if (memory_type & mtExecutable) { + MacSection *section = section_list_->GetSectionByAddress(symbol->value()); + if (!section || section->name() != "__const") + type = (symbol->type() & N_EXT) ? otExport : otCode; + } + map_function->set_type(type); + } + + switch (cpu_type_) { + case CPU_TYPE_I386: + case CPU_TYPE_X86_64: + function_list_ = new MacIntelFunctionList(this); + virtual_machine_list_ = new IntelVirtualMachineList(); + { + IntelFileHelper helper; + helper.Parse(*this); + } + break; + default: + return osUnsupportedCPU; + } + } + + return osSuccess; +} + +bool MacArchitecture::WriteToFile() +{ + mach_header mh; + uint32_t pos; + + // read header + Seek(0); + Read(&mh, sizeof(mh)); + if (mh.magic == MH_MAGIC_64) + ReadDWord(); + pos = static_cast<uint32_t>(Tell()); + mh.ncmds = static_cast<uint32_t>(command_list_->count()); + command_list_->WriteToFile(*this); + header_size_ = static_cast<uint32_t>(Tell()); + if (header_size_ > max_header_size_) + throw std::runtime_error("Runtime error at WriteToFile"); + mh.sizeofcmds = header_size_ - pos; + mh.flags = flags_; + // write header + Seek(0); + Write(&mh, sizeof(mh)); + return true; +} + +bool MacArchitecture::Compile(CompileOptions &options, IArchitecture *runtime) +{ + if (owner()->count() > 1) + owner()->Resize(AlignValue(owner()->size(), 0x1000)); + return BaseArchitecture::Compile(options, runtime); +} + +void MacArchitecture::Save(CompileContext &ctx) +{ + uint64_t address, pos, loader_crc_address, file_crc_address, patch_section_address, loader_crc_size_address, loader_crc_hash_address, file_crc_size_address; + size_t i, j, c; + uint8_t b; + uint32_t size, loader_crc_size, file_crc_size; + MacSegment *segment, *last_segment, *vmp_segment; + uint32_t linkedit_segment_flags; + std::string linkedit_segment_name; + MemoryManager *manager; + MemoryRegion *region; + int vmp_index; + + // calc progress maximum + c = 0; + if (ctx.runtime) + c += ctx.runtime->segment_list()->count(); + for (i = 0; i < function_list_->count(); i++) { + IFunction *func = function_list_->item(i); + for (j = 0; j < func->block_list()->count(); j++) { + CommandBlock *block = func->block_list()->item(j); + c += block->end_index() - block->start_index() + 1; + } + } + StartProgress(string_format("%s...", language[lsSaving].c_str()), c); + + linkedit_segment_flags = VM_PROT_READ; + linkedit_segment_name = SEG_LINKEDIT; + + // need erase optimized segments + for (i = segment_list_->count(); i > optimized_segment_count_; i--) { + segment = segment_list_->item(i - 1); + if (linkedit_segment_ == segment) { + linkedit_segment_flags = segment->flags(); + linkedit_segment_name = segment->name(); + linkedit_segment_ = NULL; + } + for (j = section_list_->count(); j > 0; j--) { + MacSection *section = section_list_->item(j - 1); + if (section->parent() == segment) + delete section; + } + delete segment; + } + + // need truncate optimized segments and overlay + for (i = segment_list_->count(); i > 0; i--) { + segment = segment_list_->item(i - 1); + if (segment->physical_size() > 0) { + Resize(segment->physical_offset() + segment->physical_size()); + break; + } + } + + last_segment = segment_list_->last(); + address = AlignValue(last_segment->address() + last_segment->size(), segment_alignment_); + pos = Resize(AlignValue(this->size(), file_alignment_)); + vmp_segment = segment_list_->Add(address, 0xffffffff, static_cast<uint32_t>(pos), 0xffffffff, VM_PROT_READ, ""); + + // merge runtime objects + MacArchitecture *runtime = reinterpret_cast<MacArchitecture*>(ctx.runtime); + if (runtime && runtime->segment_list()->count()) { + // merge segments + for (i = 0; i < runtime->segment_list()->count(); i++) { + segment = runtime->segment_list()->item(i); + if (segment->physical_size()) { + runtime->Seek(segment->physical_offset()); + size = static_cast<uint32_t>(segment->physical_size()); + uint8_t *buffer = new uint8_t[size]; + runtime->Read(buffer, size); + Write(buffer, size); + delete [] buffer; + } + size = static_cast<uint32_t>(AlignValue(segment->size(), runtime->segment_alignment()) - segment->physical_size()); + uint8_t b = 0; + for (j = 0; j < size; j++) { + Write(&b, sizeof(b)); + } + vmp_segment->include_write_type(segment->memory_type() & (~mtWritable)); + + StepProgress(); + } + // merge fixups + for (i = 0; i < runtime->fixup_list()->count(); i++) { + MacFixup *src_fixup = runtime->fixup_list()->item(i); + MacFixup *fixup = src_fixup->Clone(fixup_list_); + if (src_fixup->symbol()) { + MacSymbol *symbol = src_fixup->symbol()->Clone(symbol_list_); + symbol_list_->AddObject(symbol); + fixup->set_symbol(symbol); + } + fixup_list_->AddObject(fixup); + } + // merge import + int library_ordinal = import_list_->GetMaxLibraryOrdinal() + 1; + for (i = 0; i < runtime->import_list()->count(); i++) { + MacImport *src_import = runtime->import_list()->item(i); + if (src_import->is_sdk()) + continue; + + MacImport *import = import_list_->GetImportByName(src_import->name()); + if (!import) { + import = new MacImport(import_list_, library_ordinal++, src_import->name(), src_import->current_version(), src_import->compatibility_version()); + import_list_->AddObject(import); + } + + for (j = 0; j < src_import->count(); j++) { + MacImportFunction *src_import_function = src_import->item(j); + MacImportFunction *import_function = src_import_function->Clone(import); + if (src_import_function->symbol()) { + MacSymbol *symbol = import_function->symbol()->Clone(symbol_list_); + symbol_list_->AddObject(symbol); + + symbol->set_library_ordinal(import->library_ordinal()); + import_function->set_symbol(symbol); + } + import->AddObject(import_function); + } + } + // merge runtime functions + if (runtime_functions_section_ && runtime_functions_section_->name() == SECT_EH_FRAME) { + // dwarf format + size_t old_count = runtime_function_list_->cie_list()->count(); + for (i = 0; i < runtime->runtime_function_list()->cie_list()->count(); i++) { + runtime_function_list_->cie_list()->AddObject(runtime->runtime_function_list()->cie_list()->item(i)->Clone(runtime_function_list_->cie_list())); + } + for (i = 0; i < runtime->runtime_function_list()->count(); i++) { + MacRuntimeFunction *runtime_function = runtime->runtime_function_list()->item(i)->Clone(runtime_function_list_); + if (runtime_function->cie()) + runtime_function->set_cie(runtime_function_list()->cie_list()->item(old_count + runtime->runtime_function_list()->cie_list()->IndexOf(runtime_function->cie()))); + runtime_function_list_->AddObject(runtime_function); + } + } else { + // other formats are not supported + } + } + + // write functions + for (i = 0; i < function_list_->count(); i++) { + function_list_->item(i)->WriteToFile(*this); + } + + // erase not used memory regions + manager = memory_manager(); + if (manager->count() > 1) { + // need skip last big region + for (i = 0; i < manager->count() - 1; i++) { + region = manager->item(i); + if (!AddressSeek(region->address())) + continue; + + for (j = 0; j < region->size(); j++) { + b = (region->type() & mtReadable) ? rand() : 0xcc; + Write(&b, sizeof(b)); + } + } + } + + vmp_index = 0; + + std::map<MacSection *, uint64_t> patch_info_map; + for (i = 0; i < 2; i++) { + MacSection *section = NULL; + switch (i) { + case 0: + if (runtime_functions_section_ && runtime_functions_section_->name() == SECT_EH_FRAME) + section = runtime_functions_section_; + break; + case 1: + section = unwind_info_section_; + break; + } + if (!section) + continue; + + pos = Resize(AlignValue(this->size(), 0x10)); + address = vmp_segment->address() + pos - vmp_segment->physical_offset(); + size = static_cast<uint32_t>(runtime_function_list_->WriteToFile(*this, (section == unwind_info_section_))); + if (runtime) { + section->set_physical_size(0); + patch_info_map[section] = address + size; + if (cpu_address_size() == osDWord) { + WriteDWord(static_cast<uint32_t>(address)); + WriteDWord(size); + } else { + WriteQWord(address); + WriteQWord(size); + } + WriteDWord(static_cast<uint32_t>(pos - vmp_segment->physical_offset())); + } else { + section->set_address(address); + section->set_physical_size(size); + } + vmp_segment->include_write_type(mtReadable); + } + + if (vmp_segment->write_type() == mtNone) { + delete vmp_segment; + } else { + vmp_segment->set_name(string_format("%s%d", ctx.options.section_name.c_str(), vmp_index++)); + + size = static_cast<uint32_t>(this->size() - vmp_segment->physical_offset()); + vmp_segment->set_size(AlignValue(size, segment_alignment_)); + vmp_segment->set_physical_size(static_cast<uint32_t>(AlignValue(size, file_alignment_))); + vmp_segment->update_type(vmp_segment->write_type()); + + Resize(vmp_segment->physical_offset() + vmp_segment->physical_size()); + } + + if ((ctx.options.flags & cpPack) && ctx.options.script) + ctx.options.script->DoBeforePackFile(); + + // write memory CRC table + if (function_list_->crc_table()) { + IntelCRCTable *intel_crc = reinterpret_cast<IntelCRCTable *>(function_list_->crc_table()); + CRCTable crc_table(function_list_->crc_cryptor(), intel_crc->table_size()); + + // add non writable segments + for (i = 0; i < segment_list_->count(); i++) { + segment = segment_list_->item(i); + if ((segment->memory_type() & (mtReadable | mtWritable)) != mtReadable || segment->excluded_from_memory_protection()) + continue; + + size = std::min(static_cast<uint32_t>(segment->size()), segment->physical_size()); + if (size) + crc_table.Add(segment->address(), size); + } + + // skip writable runtime's sections + if (runtime) { + for (i = 0; i < runtime->segment_list()->count(); i++) { + segment = runtime->segment_list()->item(i); + if (segment->memory_type() & mtWritable) + crc_table.Remove(segment->address(), static_cast<uint32_t>(segment->size())); + } + } + + // skip header + if (header_segment_) + crc_table.Remove(header_segment_->address(), max_header_size_); + + // skip IAT + size_t ptr_size = OperandSizeToValue(cpu_address_size()); + size_t k = (runtime && runtime->segment_list()->count() > 0) ? 2 : 1; + for (size_t n = 0; n < k; n++) { + MacImportList *import_list = (n == 0) ? import_list_ : runtime->import_list(); + for (i = 0; i < import_list->count(); i++) { + MacImport *import = import_list->item(i); + for (j = 0; j < import->count(); j++) { + MacImportFunction *import_function = import->item(j); + size = (uint32_t)ptr_size; + if (import_function->options() & ioIsRelative) { + MacSection *section = section_list_->GetSectionByAddress(import_function->address()); + if (section && section->type() == S_SYMBOL_STUBS) + size = section->reserved2(); + } + crc_table.Remove(import_function->address(), size); + } + } + } + + // skip fixups + for (i = 0; i < fixup_list_->count(); i++) { + MacFixup *fixup = fixup_list_->item(i); + if (!fixup->is_deleted() || (ctx.options.flags & cpStripFixups) == 0 || fixup->symbol()) + crc_table.Remove(fixup->address(), OperandSizeToValue(fixup->size())); + } + + // skip loader_data + IntelFunction *loader_data = reinterpret_cast<IntelFunctionList *>(function_list_)->loader_data(); + if (loader_data) + crc_table.Remove(loader_data->entry()->address(), loader_data->entry()->dump_size()); + + // skip memory CRC table + crc_table.Remove(intel_crc->table_entry()->address(), intel_crc->table_size()); + crc_table.Remove(intel_crc->size_entry()->address(), sizeof(uint32_t)); + crc_table.Remove(intel_crc->hash_entry()->address(), sizeof(uint32_t)); + + // write to file + AddressSeek(intel_crc->table_entry()->address()); + uint32_t hash; + size = static_cast<uint32_t>(crc_table.WriteToFile(*this, false, &hash)); + AddressSeek(intel_crc->size_entry()->address()); + WriteDWord(size); + AddressSeek(intel_crc->hash_entry()->address()); + WriteDWord(hash); + + intel_crc->size_entry()->set_operand_value(0, size); + intel_crc->hash_entry()->set_operand_value(0, hash); + } + EndProgress(); + + if (ctx.options.flags & cpStripDebugInfo) { + // strip symbols + for (i = 0; i < symbol_list_->count(); i++) { + MacSymbol *symbol = symbol_list_->item(i); + if (symbol->is_deleted()) + continue; + + if (symbol->type() & N_EXT) { + // global symbol + if ((symbol->type() & N_TYPE) == N_ABS && symbol->value() != 0) + continue; + if ((symbol->type() & N_TYPE) == N_UNDF && symbol->value() == 0) + continue; + if ((symbol->type() & N_TYPE) == N_PBUD) + continue; + if ((symbol->type() & N_TYPE) == N_SECT) + continue; + if (symbol->desc() & REFERENCED_DYNAMICALLY) + continue; + } else { + // local symbol + if (symbol->type() & N_STAB) { + // debug symbol + } else if (symbol->type() & N_PEXT) { + // private extern symbol + MacExtRefSymbol *ext_ref_symbol = ext_ref_symbol_list_->GetSymbol(symbol); + if (ext_ref_symbol && (ext_ref_symbol->flags() == REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY || ext_ref_symbol->flags() == REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY)) + continue; + if (indirect_symbol_list_->GetSymbol(symbol)) + continue; + } + } + + symbol->set_deleted(true); + } + + // strip load commands + for (i = command_list_->count(); i > 0; i--) { + MacLoadCommand *load_command = command_list_->item(i - 1); + switch (load_command->type()) { + case LC_FUNCTION_STARTS: + case LC_SOURCE_VERSION: + delete load_command; + break; + } + } + } + + string_table_.clear(); + import_list_->Pack(); + ext_ref_symbol_list_->Pack(); + indirect_symbol_list_->Pack(); + command_list_->Pack(); + export_list_->Pack(); + symbol_list_->Pack(); + + file_crc_address = 0; + file_crc_size = 0; + file_crc_size_address = 0; + loader_crc_address = 0; + loader_crc_size = 0; + loader_crc_size_address = 0; + loader_crc_hash_address = 0; + patch_section_address = 0; + if (runtime) { + std::vector<IFunction *> processor_list = function_list_->processor_list(); + IntelRuntimeCRCTable *runtime_crc_table = reinterpret_cast<IntelFunctionList *>(function_list_)->runtime_crc_table(); + MacIntelLoader *loader = new MacIntelLoader(NULL, cpu_address_size()); + std::vector<MacSegment *> loader_segment_list; + + last_segment = segment_list_->last(); + address = AlignValue(last_segment->address() + last_segment->size(), segment_alignment_); + + manager->clear(); + manager->Add(address, UINT32_MAX, mtReadable | mtExecutable | mtWritable | (runtime_function_list()->count() ? mtSolid : mtNone)); + + if (!loader->Prepare(ctx)) { + delete loader; + throw std::runtime_error("Runtime error at Save"); + } + size_t write_count = loader->count() + 10000; + size_t processor_count = 0; + for (i = 0; i < processor_list.size(); i++) { + processor_count += processor_list[i]->count(); + } + ctx.file->StartProgress(string_format("%s...", language[lsSavingStartupCode].c_str()), loader->count() + write_count + processor_count); + loader->Compile(ctx); + + pos = Resize(AlignValue(this->size(), file_alignment_)); + segment = segment_list_->Add(address, UINT32_MAX, static_cast<uint32_t>(pos), UINT32_MAX, VM_PROT_READ | VM_PROT_EXECUTE, string_format("%s%d", ctx.options.section_name.c_str(), vmp_index++)); + loader_segment_list.push_back(segment); + + // create import segment + if (loader->import_segment_address()) { + size = static_cast<uint32_t>(loader->import_segment_address() - segment->address()); + segment->set_size(size); + segment->set_physical_size(size); + + address = loader->import_segment_address(); + pos = Resize(segment->physical_offset() + segment->physical_size()); + segment = segment_list_->Add(address, UINT32_MAX, static_cast<uint32_t>(pos), UINT32_MAX, VM_PROT_READ | VM_PROT_EXECUTE | VM_PROT_WRITE, string_format("%s%d", ctx.options.section_name.c_str(), vmp_index++)); + loader_segment_list.push_back(segment); + } + + // create data segment + if (loader->data_segment_address()) { + size = static_cast<uint32_t>(loader->data_segment_address() - segment->address()); + segment->set_size(size); + segment->set_physical_size(size); + + address = loader->data_segment_address(); + pos = Resize(segment->physical_offset() + segment->physical_size()); + segment = segment_list_->Add(address, UINT32_MAX, static_cast<uint32_t>(pos), UINT32_MAX, VM_PROT_READ | VM_PROT_WRITE, string_format("%s%d", ctx.options.section_name.c_str(), vmp_index++)); + loader_segment_list.push_back(segment); + } + + c = loader->WriteToFile(*this); + for (i = 0; i < processor_list.size(); i++) { + processor_list[i]->WriteToFile(*this); + } + if (runtime_crc_table) + c += runtime_crc_table->WriteToFile(*this); + + // correct progress position + write_count -= c; + if (write_count) + StepProgress(write_count); + + size = static_cast<uint32_t>(this->size() - segment->physical_offset()); + segment->set_size(AlignValue(size, segment_alignment_)); + segment->set_physical_size(AlignValue(size, file_alignment_)); + + Resize(segment->physical_offset() + segment->physical_size()); + + if (loader->jmp_table_entry()) { + address = loader->jmp_table_entry()->address(); + segment = segment_list_->GetSectionByAddress(address); + MacSection *section = section_list_->Add(segment, address, loader->jmp_table_size(), static_cast<uint32_t>(segment->physical_offset() + address - segment->address()), S_SYMBOL_STUBS | S_ATTR_SELF_MODIFYING_CODE, SECT_JUMP_TABLE, segment->name()); + section->set_reserved1(static_cast<uint32_t>(loader->jmp_table_entry()->operand(1).value)); + section->set_reserved2(static_cast<uint32_t>(loader->jmp_table_entry()->dump_size())); + } + + if (loader->lazy_import_entry()) { + address = loader->lazy_import_entry()->address(); + segment = segment_list_->GetSectionByAddress(address); + MacSection *section = section_list_->Add(segment, address, loader->lazy_import_size(), static_cast<uint32_t>(segment->physical_offset() + address - segment->address()), S_LAZY_SYMBOL_POINTERS, SECT_LAZY_SYMBOL_PTR, segment->name()); + section->set_reserved1(static_cast<uint32_t>(loader->lazy_import_entry()->operand(1).value)); + section->set_alignment(loader->lazy_import_entry()->alignment()); + } + + if (loader->import_entry()) { + address = loader->import_entry()->address(); + segment = segment_list_->GetSectionByAddress(address); + MacSection *section = section_list_->Add(segment, address, loader->import_size(), static_cast<uint32_t>(segment->physical_offset() + address - segment->address()), S_NON_LAZY_SYMBOL_POINTERS, SECT_NON_LAZY_SYMBOL_PTR, segment->name()); + section->set_reserved1(static_cast<uint32_t>(loader->import_entry()->operand(1).value)); + section->set_alignment(loader->import_entry()->alignment()); + + std::map<MacSection *, MacIndirectSymbol *> indirect_symbol_map; + for (i = 0; i < section_list_->count(); i++) { + section = section_list_->item(i); + if (section->type() == S_SYMBOL_STUBS || section->type() == S_LAZY_SYMBOL_POINTERS || section->type() == S_NON_LAZY_SYMBOL_POINTERS || section->type() == S_THREAD_LOCAL_VARIABLE_POINTERS) + indirect_symbol_map[section] = indirect_symbol_list_->item(section->reserved1()); + } + + // delete S_NON_LAZY_SYMBOL_POINTERS and S_SYMBOL_STUBS sections + size_t ptr_size = OperandSizeToValue(cpu_address_size()); + for (i = section_list_->count(); i > 0; i--) { + section = section_list_->item(i - 1); + if (std::find(loader_segment_list.begin(), loader_segment_list.end(), section->parent()) != loader_segment_list.end()) + continue; + + if (section->type() == S_NON_LAZY_SYMBOL_POINTERS || section->type() == S_SYMBOL_STUBS) { + indirect_symbol_map.erase(section); + size_t value_size = (section->type() == S_SYMBOL_STUBS) ? section->reserved2() : ptr_size; + size_t c = static_cast<uint32_t>(section->size()) / value_size; + for (j = 0; j < c; j++) { + indirect_symbol_list_->Delete(section->reserved1()); + } + delete section; + } + } + + // recalc indirect symbol indexes + for (std::map<MacSection *, MacIndirectSymbol *>::iterator it = indirect_symbol_map.begin(); it != indirect_symbol_map.end(); it++) { + section = it->first; + MacIndirectSymbol *indirect_symbol = it->second; + section->set_reserved1(static_cast<uint32_t>(indirect_symbol_list_->IndexOf(indirect_symbol))); + } + } + + if (loader->init_entry()) { + address = loader->init_entry()->operand(0).value; + MacSegment *base_segment = segment_list()->GetBaseSegment(); + if (base_segment && address - base_segment->address() < max_header_size_) + max_header_size_ = static_cast<uint32_t>(address - base_segment->address()); + + // delete S_MOD_INIT_FUNC_POINTERS sections + for (i = section_list_->count(); i > 0; i--) { + MacSection *section = section_list_->item(i - 1); + if (section->type() == S_MOD_INIT_FUNC_POINTERS) + delete section; + } + + address = loader->init_entry()->address(); + segment = segment_list_->GetSectionByAddress(address); + MacSection *section = section_list_->Add(segment, address, loader->init_size(), static_cast<uint32_t>(segment->physical_offset() + address - segment->address()), S_MOD_INIT_FUNC_POINTERS, SECT_MOD_INIT_FUNC, segment->name()); + section->set_alignment(loader->init_entry()->alignment()); + } + + if (loader->term_entry()) { + // delete S_MOD_TERM_FUNC_POINTERS sections + for (i = section_list_->count(); i > 0; i--) { + MacSection *section = section_list_->item(i - 1); + if (section->type() == S_MOD_TERM_FUNC_POINTERS) + delete section; + } + + address = loader->term_entry()->address(); + segment = segment_list_->GetSectionByAddress(address); + MacSection *section = section_list_->Add(segment, address, loader->term_size(), static_cast<uint32_t>(segment->physical_offset() + address - segment->address()), S_MOD_TERM_FUNC_POINTERS, SECT_MOD_TERM_FUNC, segment->name()); + section->set_alignment(loader->term_entry()->alignment()); + } + + if (loader->thread_variables_entry()) { + // delete S_THREAD_LOCAL_VARIABLES sections + for (i = section_list_->count(); i > 0; i--) { + MacSection *section = section_list_->item(i - 1); + if (section->type() == S_THREAD_LOCAL_VARIABLES) + delete section; + } + + address = loader->thread_variables_entry()->address(); + segment = segment_list_->GetSectionByAddress(address); + MacSection *section = section_list_->Add(segment, address, loader->thread_variables_size(), static_cast<uint32_t>(segment->physical_offset() + address - segment->address()), S_THREAD_LOCAL_VARIABLES, SECT_THREAD_LOCAL_VARIABLES, segment->name()); + section->set_alignment(loader->thread_variables_entry()->alignment()); + } + + if (loader->thread_data_entry()) { + // delete S_THREAD_LOCAL_REGULAR and S_THREAD_LOCAL_ZEROFILL sections + for (i = section_list_->count(); i > 0; i--) { + MacSection *section = section_list_->item(i - 1); + if (section->type() == S_THREAD_LOCAL_REGULAR || section->type() == S_THREAD_LOCAL_ZEROFILL) + delete section; + } + + address = loader->thread_data_entry()->address(); + segment = segment_list_->GetSectionByAddress(address); + MacSection *section = section_list_->Add(segment, address, loader->thread_data_size(), static_cast<uint32_t>(segment->physical_offset() + address - segment->address()), S_THREAD_LOCAL_REGULAR, SECT_THREAD_LOCAL_REGULAR, segment->name()); + section->set_alignment(loader->thread_data_entry()->alignment()); + } + + if (loader->file_entry()) { + address = loader->file_entry()->address(); + MacSegment *base_segment = segment_list()->GetBaseSegment(); + if (base_segment && address - base_segment->address() < max_header_size_) + max_header_size_ = static_cast<uint32_t>(address - base_segment->address()); + entry_point_ = address; + } + + if (loader->file_crc_entry()) { + file_crc_address = loader->file_crc_entry()->address(); + file_crc_size = loader->file_crc_size(); + file_crc_size_address = loader->file_crc_size_entry()->address(); + } + + if (loader->loader_crc_entry()) { + loader_crc_address = loader->loader_crc_entry()->address(); + loader_crc_size = loader->loader_crc_size(); + loader_crc_size_address = loader->loader_crc_size_entry()->address(); + loader_crc_hash_address = loader->loader_crc_hash_entry()->address(); + } + + if (loader->patch_section_entry()) + patch_section_address = loader->patch_section_entry()->address(); + + // codesign requires S_ZEROFILL for packed sections + std::vector<MacSegment *> packed_segment_list = loader->packed_segment_list(); + for (i = 0; i < section_list()->count(); i++) { + MacSection *section = section_list()->item(i); + if (std::find(packed_segment_list.begin(), packed_segment_list.end(), section->parent()) == packed_segment_list.end()) + continue; + + section->set_physical_offset(0); + if (section->flags() != S_THREAD_LOCAL_ZEROFILL) + section->set_flags(S_ZEROFILL); + } + + if (file_type_ == MH_DYLIB || sdk_ >= DYLD_MACOSX_VERSION_10_12) { + // exclude EXECUTABLE flag for packed segments + for (i = 0; i < packed_segment_list.size(); i++) { + segment = packed_segment_list[i]; + if (segment == header_segment_) { + if (header_segment_->size() > header_segment_->physical_size()) { + header_segment_->set_physical_size(AlignValue(header_segment_->physical_size(), segment_alignment_)); + address = header_segment_->address() + header_segment_->physical_size(); + segment = new MacSegment(segment_list_, address, header_segment_->address() + header_segment_->size() - address, + header_segment_->physical_offset() + header_segment_->physical_size(), 0, header_segment_->flags() & ~VM_PROT_EXECUTE, ctx.options.section_name); + segment->include_maxprot(VM_PROT_EXECUTE | VM_PROT_WRITE); + segment_list_->InsertObject(segment_list_->IndexOf(header_segment_) + 1, segment); + header_segment_->set_size(header_segment_->physical_size()); + } + } + else if (segment->flags() & VM_PROT_EXECUTE) + segment->set_flags(segment->flags() & ~VM_PROT_EXECUTE); + } + } + + delete loader; + + ctx.file->EndProgress(); + } + + // add LINK_EDIT segment + last_segment = segment_list_->last(); + address = AlignValue(last_segment->address() + last_segment->size(), segment_alignment_); + pos = Resize(AlignValue(this->size(), file_alignment_)); + linkedit_segment_ = segment_list_->Add(address, UINT32_MAX, static_cast<uint32_t>(pos), UINT32_MAX, linkedit_segment_flags, linkedit_segment_name); + + if (ctx.options.flags & cpStripFixups) { + for (i = fixup_list_->count(); i > 0; i--) { + MacFixup *fixup = fixup_list_->item(i - 1); + if (!fixup->symbol()) + delete fixup; + } + } + fixup_list_->Pack(); + + fixup_list_->WriteToFile(*this); + import_list_->WriteToFile(*this); + export_list_->WriteToFile(*this); + + // write LINK_EDIT data + IArchitecture *source = const_cast<IArchitecture *>(this->source()); + for (i = 0; i < command_list_->count(); i++) { + MacLoadCommand *load_command = command_list_->item(i); + switch (load_command->type()) { + case LC_FUNCTION_STARTS: + case LC_DATA_IN_CODE: + case LC_DYLIB_CODE_SIGN_DRS: + { + linkedit_data_command command; + source->Seek(load_command->address()); + source->Read(&command, sizeof(command)); + if (source->Seek(command.dataoff)) { + load_command->set_offset(static_cast<uint32_t>(Tell())); + CopyFrom(*source, command.datasize); + } + } + break; + } + } + + symbol_list_->WriteToFile(*this); + ext_ref_symbol_list_->WriteToFile(*this); + indirect_symbol_list_->WriteToFile(*this); + segment_list_->WriteToFile(*this); + string_table_.WriteToFile(*this); + + size = static_cast<uint32_t>(this->size() - linkedit_segment_->physical_offset()); + linkedit_segment_->set_size(AlignValue(size, segment_alignment_)); + linkedit_segment_->set_physical_size(size); + + if (ctx.options.script) + ctx.options.script->DoAfterSaveFile(); + + // write header + if ((ctx.options.flags & cpStripFixups) && file_type_ == MH_EXECUTE) + flags_ &= ~MH_PIE; + WriteToFile(); + + std::map<uint64_t, size_t> patch_header_map; + if (patch_section_address && AddressSeek(patch_section_address)) { + for (std::map<MacSection *, uint64_t>::const_iterator it = patch_info_map.begin(); it != patch_info_map.end(); it++) { + MacSection *patch_section = it->first; + uint64_t patch_info_address = it->second; + MacLoadCommand *load_command = command_list_->GetCommandByObject(patch_section->parent()); + if (load_command) { + size_t section_index = 0; + for (size_t i = 0; i < section_list_->count(); i++) { + if (section_list_->item(i) == patch_section) + break; + if (section_list_->item(i)->parent() == patch_section->parent()) + section_index++; + } + + // patch section.adr + section.size + section.offset + address = segment_list_->GetBaseSegment()->address() + load_command->address() + ((cpu_address_size() == osDWord) ? sizeof(segment_command) : sizeof(segment_command_64)) + + section_index * ((cpu_address_size() == osDWord) ? sizeof(section) : sizeof(section_64)) + offsetof(section, addr); + size = (cpu_address_size() == osDWord) ? sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t) : sizeof(uint64_t) + sizeof(uint64_t) + sizeof(uint32_t); + WriteDWord(static_cast<uint32_t>(patch_info_address - image_base())); + WriteDWord(static_cast<uint32_t>(address - image_base())); + WriteDWord(size); + + patch_header_map[address] = size; + } + patch_section_address += 3 * sizeof(uint32_t); + } + } + + // write header and loader CRC table + if (loader_crc_address) { + CRCTable crc_table(function_list_->crc_cryptor(), loader_crc_size); + + // add header + if (header_segment_) { + crc_table.Add(header_segment_->address(), max_header_size_); + // skip place for codesign's commands + crc_table.Remove(header_segment_->address() + header_size_, max_header_size_ - header_size_); + // skip size of commands list + crc_table.Remove(header_segment_->address() + offsetof(mach_header, ncmds), sizeof(uint32_t)); + crc_table.Remove(header_segment_->address() + offsetof(mach_header, sizeofcmds), sizeof(uint32_t)); + // skip sizes of LINKEDIT + MacLoadCommand *linkedit_command = command_list()->GetCommandByObject(linkedit_segment_); + if (linkedit_command) { + if (cpu_address_size() == osDWord) { + crc_table.Remove(header_segment_->address() + linkedit_command->address() + offsetof(segment_command, vmsize), sizeof(uint32_t)); + crc_table.Remove(header_segment_->address() + linkedit_command->address() + offsetof(segment_command, filesize), sizeof(uint32_t)); + } else { + crc_table.Remove(header_segment_->address() + linkedit_command->address() + offsetof(segment_command_64, vmsize), sizeof(uint64_t)); + crc_table.Remove(header_segment_->address() + linkedit_command->address() + offsetof(segment_command_64, filesize), sizeof(uint64_t)); + } + } + for (std::map<uint64_t, size_t>::const_iterator it = patch_header_map.begin(); it != patch_header_map.end(); it++) { + crc_table.Remove(it->first, it->second); + } + } + + // add loader segments + j = segment_list_->IndexOf(segment_list_->GetSectionByAddress(loader_crc_address)); + if (j != NOT_ID) { + c = (ctx.options.flags & cpLoaderCRC) ? j + 1 : segment_list_->count(); + for (i = j; i < c; i++) { + segment = segment_list_->item(i); + if (segment->memory_type() & mtWritable) + continue; + + size = std::min(static_cast<uint32_t>(segment->size()), segment->physical_size()); + if (size) + crc_table.Add(segment->address(), size); + + // skip import + for (j = 0; j < section_list_->count(); j++) { + MacSection *section = section_list_->item(j); + if (section->parent() == segment && (section->type() == S_NON_LAZY_SYMBOL_POINTERS || section->type() == S_SYMBOL_STUBS)) + crc_table.Remove(section->address(), static_cast<size_t>(section->size())); + } + } + } + + // skip fixups + for (i = 0; i < fixup_list_->count(); i++) { + MacFixup *fixup = fixup_list_->item(i); + crc_table.Remove(fixup->address(), OperandSizeToValue(fixup->size())); + } + // skip loader CRC table + crc_table.Remove(loader_crc_address, loader_crc_size); + crc_table.Remove(loader_crc_size_address, sizeof(uint32_t)); + crc_table.Remove(loader_crc_hash_address, sizeof(uint32_t)); + // skip file CRC table + if (file_crc_address) + crc_table.Remove(file_crc_address, file_crc_size); + if (file_crc_size_address) + crc_table.Remove(file_crc_size_address, sizeof(uint32_t)); + + // write to file + AddressSeek(loader_crc_address); + uint32_t hash; + size = static_cast<uint32_t>(crc_table.WriteToFile(*this, false, &hash)); + AddressSeek(loader_crc_size_address); + WriteDWord(size); + AddressSeek(loader_crc_hash_address); + WriteDWord(hash); + } + + // write file CRC table + if (file_crc_address) { + CRCTable crc_table(function_list_->crc_cryptor(), file_crc_size - sizeof(uint32_t)); + + // add file range + crc_table.Add(1, static_cast<size_t>(this->size()) - 1); + // skip place for codesign's commands + crc_table.Remove(header_size_, max_header_size_ - header_size_); + // skip size of commands list + crc_table.Remove(offsetof(mach_header, ncmds), sizeof(uint32_t)); + crc_table.Remove(offsetof(mach_header, sizeofcmds), sizeof(uint32_t)); + // skip sizes of LINKEDIT + MacLoadCommand *linkedit_command = command_list()->GetCommandByObject(linkedit_segment_); + if (linkedit_command) { + if (cpu_address_size() == osDWord) { + crc_table.Remove(linkedit_command->address() + offsetof(segment_command, vmsize), sizeof(uint32_t)); + crc_table.Remove(linkedit_command->address() + offsetof(segment_command, filesize), sizeof(uint32_t)); + } else { + crc_table.Remove(linkedit_command->address() + offsetof(segment_command_64, vmsize), sizeof(uint64_t)); + crc_table.Remove(linkedit_command->address() + offsetof(segment_command_64, filesize), sizeof(uint64_t)); + } + } + + // skip file CRC table + if (AddressSeek(file_crc_address)) + crc_table.Remove(Tell(), file_crc_size); + if (AddressSeek(file_crc_size_address)) + crc_table.Remove(Tell(), sizeof(uint32_t)); + + // write to file + AddressSeek(file_crc_address); + size = static_cast<uint32_t>(this->size()); + Write(&size, sizeof(size)); + size = static_cast<uint32_t>(crc_table.WriteToFile(*this, true)); + AddressSeek(file_crc_size_address); + WriteDWord(size); + } + + EndProgress(); +} + +bool MacArchitecture::Prepare(CompileContext &ctx) +{ + if ((ctx.options.flags & cpStripFixups) == 0 && file_type_ == MH_EXECUTE && (flags_ & MH_PIE) == 0) + ctx.options.flags |= cpStripFixups; + else if (ctx.runtime) + ctx.options.flags |= cpLoader; + + if (ctx.options.flags & cpImportProtection) + ctx.options.flags &= ~cpImportProtection; + + if (ctx.options.flags & cpResourceProtection) + ctx.options.flags &= ~cpResourceProtection; + + if (ctx.runtime && ((runtime_functions_section_ && runtime_functions_section_->name() == SECT_EH_FRAME) || unwind_info_section_)) + ctx.options.flags |= cpLoader; + + if (!BaseArchitecture::Prepare(ctx)) + return false; + + size_t i; + MacSegment *segment; + std::vector<MacSegment *> optimized_segment_list; + + // optimize segments + if (linkedit_segment_) + optimized_segment_list.push_back(linkedit_segment_); + + optimized_segment_count_ = segment_list_->count(); + for (i = segment_list_->count(); i > 0; i--) { + segment = segment_list_->item(i - 1); + + std::vector<MacSegment *>::iterator it = std::find(optimized_segment_list.begin(), optimized_segment_list.end(), segment); + if (it != optimized_segment_list.end()) { + optimized_segment_list.erase(it); + optimized_segment_count_--; + } else { + break; + } + } + + // calc new header size + size_t new_header_size = (cpu_address_size_ == osDWord) ? sizeof(mach_header) : sizeof(mach_header_64); + for (i = 0; i < command_list_->count(); i++) { + MacLoadCommand *load_command = command_list_->item(i); + switch (load_command->type()) { + case LC_SEGMENT: + case LC_SEGMENT_64: + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_CODE_SIGNATURE: + continue; + case LC_FUNCTION_STARTS: + case LC_SOURCE_VERSION: + if (ctx.options.flags & cpStripDebugInfo) + continue; + break; + } + new_header_size += load_command->size(); + } + size_t segment_size = (cpu_address_size_ == osDWord) ? sizeof(segment_command) : sizeof(segment_command_64); + size_t section_size = (cpu_address_size_ == osDWord) ? sizeof(section) : sizeof(section_64); + new_header_size += (optimized_segment_count_ + 2) * segment_size; + new_header_size += section_list_->count() * section_size; + for (i = 0; i < import_list_->count(); i++) { + MacImport *import = import_list_->item(i); + if (import->is_sdk()) + continue; + new_header_size += AlignValue(sizeof(dylib_command) + import->name().size() + 1, OperandSizeToValue(cpu_address_size())); + } + + if (ctx.runtime) { + for (i = 0; i < section_list_->count(); i++) { + MacSection *section = section_list_->item(i); + if (section->type() == S_NON_LAZY_SYMBOL_POINTERS || section->type() == S_SYMBOL_STUBS || + section->type() == S_MOD_INIT_FUNC_POINTERS || section->type() == S_MOD_TERM_FUNC_POINTERS) + new_header_size -= section_size; + } + new_header_size += 2 * segment_size; + new_header_size += section_size * 4; // S_SYMBOL_STUBS / S_LAZY_SYMBOL_POINTERS + S_NON_LAZY_SYMBOL_POINTERS + S_MOD_INIT_FUNC_POINTERS + S_MOD_TERM_FUNC_POINTERS + new_header_size += 5; // jmp xxxx + if (entry_point_) + new_header_size += 5; // jmp xxxx + if ((ctx.options.flags & cpPack) && file_type_ == MH_DYLIB) + new_header_size += segment_size; + + for (i = 0; i < ctx.runtime->import_list()->count(); i++) { + IImport *import = ctx.runtime->import_list()->item(i); + if (import->is_sdk() || import_list_->GetImportByName(import->name())) + continue; + new_header_size += AlignValue(sizeof(dylib_command) + import->name().size() + 1, OperandSizeToValue(cpu_address_size())); + } + } + + for (i = 0; i < section_list_->count(); i++) { + MacSection *section = section_list_->item(i); + MacSegment *segment = section->parent(); + + if (segment->physical_size() && new_header_size > segment->physical_offset() && new_header_size > section->physical_offset()) { + Notify(mtError, NULL, language[lsCreateSegmentError]); + return false; + } + } + + for (i = 0; i < optimized_segment_list.size(); i++) { + segment = optimized_segment_list[i]; + ctx.manager->Add(segment->address(), static_cast<uint32_t>(segment->physical_size()), mtReadable | mtExecutable | mtWritable | mtNotPaged, NULL); + } + + if (optimized_segment_count_ > 0) { + segment = segment_list_->item(optimized_segment_count_ - 1); + if (ctx.runtime) { + MacArchitecture *runtime = reinterpret_cast<MacArchitecture *>(ctx.runtime); + if (runtime->segment_list()->count()) { + MemoryManager runtime_manager(runtime); + if (runtime->segment_list()->last() == runtime->linkedit_segment_) { + delete runtime->linkedit_segment_; + runtime->linkedit_segment_ = NULL; + } + runtime->Rebase(AlignValue(segment->address() + segment->size(), segment_alignment()) - runtime->segment_list()->item(0)->address(), dyld_info_.lazy_bind_size); + if (runtime->header_segment_) + runtime_manager.Add(runtime->header_segment_->address(), runtime->max_header_size_); + if (runtime->unwind_info_section_) + runtime_manager.Add(runtime->unwind_info_section_->address(), static_cast<size_t>(runtime->unwind_info_section_->size())); + if (runtime->runtime_functions_section_) + runtime_manager.Add(runtime->runtime_functions_section_->address(), static_cast<size_t>(runtime->runtime_functions_section_->size())); + runtime_manager.Pack(); + for (i = 0; i < runtime_manager.count(); i++) { + MemoryRegion *region = runtime_manager.item(i); + ctx.manager->Add(region->address(), region->size(), region->type()); + } + segment = runtime->segment_list()->last(); + } else { + runtime->Rebase(image_base() - runtime->image_base(), 0); + } + } + + // add new segment + assert(segment); + ctx.manager->Add(AlignValue(segment->address() + segment->size(), segment_alignment()), UINT32_MAX, mtReadable | mtExecutable | mtWritable | (runtime_function_list()->count() ? mtSolid : mtNone)); + } + + if (unwind_info_section_) + ctx.manager->Add(unwind_info_section_->address(), static_cast<size_t>(unwind_info_section_->size())); + if (runtime_functions_section_ && runtime_functions_section_->name() == SECT_EH_FRAME) + ctx.manager->Add(runtime_functions_section_->address(), static_cast<size_t>(runtime_functions_section_->size())); + + return true; +} + +uint64_t MacArchitecture::GetRelocBase() const +{ + if (cpu_address_size() == osQWord || (flags_ & MH_SPLIT_SEGS) != 0) { + for (size_t i = 0; i < segment_list_->count(); i++) { + MacSegment *segment = segment_list_->item(i); + if (segment->memory_type() & mtWritable) + return segment->address(); + } + } else { + if (segment_list_->count()) + return segment_list_->item(0)->address(); + } + throw std::runtime_error("Runtime error at GetRelocBase"); +} + +void MacArchitecture::Rebase(uint64_t delta_base, size_t delta_bind_info) +{ + BaseArchitecture::Rebase(delta_base); + + fixup_list_->Rebase(*this, delta_base); + export_list_->Rebase(delta_base); + indirect_symbol_list_->Rebase(delta_base); + command_list_->Rebase(delta_base); + segment_list_->Rebase(delta_base); + section_list_->Rebase(delta_base); + import_list_->Rebase(delta_base); + import_list_->RebaseBindInfo(*this, delta_bind_info); + function_list_->Rebase(delta_base); + runtime_function_list_->Rebase(delta_base); + + if (entry_point_) + entry_point_ += delta_base; + image_base_ += delta_base; +} + +bool MacArchitecture::is_executable() const +{ + return file_type() == MH_EXECUTE; +} + +/** + * MacFile + */ + +MacFile::MacFile(ILog *log) + : IFile(log), fat_magic_(0), runtime_(NULL) +{ + +} + +MacFile::~MacFile() +{ + delete runtime_; +} + +MacFile::MacFile(const MacFile &src, const char *file_name) + : IFile(src, file_name), runtime_(NULL) +{ + fat_magic_ = src.fat_magic_; + for (size_t i = 0; i < src.count(); i++) + AddObject(src.item(i)->Clone(this)); +} + +std::string MacFile::format_name() const +{ + return std::string("Mach-O"); +} + +MacArchitecture *MacFile::item(size_t index) const +{ + return reinterpret_cast<MacArchitecture *>(IFile::item(index)); +} + +MacArchitecture *MacFile::Add(uint64_t offset, uint64_t size) +{ + MacArchitecture *arch = new MacArchitecture(this, offset, size); + AddObject(arch); + return arch; +} + +OpenStatus MacFile::ReadHeader(uint32_t open_mode) +{ + Seek(0); + + fat_header fat; + if (size() < sizeof(fat)) + return osUnknownFormat; + + Read(&fat, sizeof(fat)); + if (fat.magic == FAT_MAGIC || fat.magic == FAT_CIGAM) { + fat_magic_ = fat.magic; + if (fat_magic_ == FAT_CIGAM) + fat.nfat_arch = __builtin_bswap32(fat.nfat_arch); + size_t i; + for (i = 0; i < fat.nfat_arch; i++) { + fat_arch header; + Read(&header, sizeof(header)); + if (fat_magic_ == FAT_CIGAM) { + header.offset = __builtin_bswap32(header.offset); + header.size = __builtin_bswap32(header.size); + } + Add(header.offset, header.size); + } + OpenStatus res = osSuccess; + for (i = 0; i < count(); i++) { + MacArchitecture *arch = item(i); + OpenStatus status = arch->ReadFromFile(open_mode); + if (status != osSuccess) { + res = status; + if (res == osUnknownFormat || res == osInvalidFormat) + break; + } + } + return res; + } else { + fat_magic_ = 0; + MacArchitecture *arch = Add(0, size()); + return arch->ReadFromFile(open_mode); + } +} + +MacFile *MacFile::Clone(const char *file_name) const +{ + MacFile *file = new MacFile(*this, file_name); + return file; +} + +bool MacFile::Compile(CompileOptions &options) +{ + MacArchitecture *arch; + size_t i; + + if (fat_magic_) { + fat_header fat; + fat.magic = fat_magic_; + fat.nfat_arch = static_cast<uint32_t>(count()); + if (fat_magic_ == FAT_CIGAM) + fat.nfat_arch = __builtin_bswap32(fat.nfat_arch); + Write(&fat, sizeof(fat)); + + fat_arch header = fat_arch(); + for (i = 0; i < count(); i++) { + Write(&header, sizeof(header)); + } + Resize(AlignValue(size(), 0x1000)); + } + + const ResourceInfo runtime_info[] = { + {mac_runtime32_dylib_file, sizeof(mac_runtime32_dylib_file), mac_runtime32_dylib_code}, + {mac_runtime64_dylib_file, sizeof(mac_runtime64_dylib_file), mac_runtime64_dylib_code} + }; + + for (size_t k = 0; k < count(); k++) { + arch = item(k); + if (!arch->visible()) + continue; + + ResourceInfo info = runtime_info[(arch->cpu_address_size() == osDWord) ? 0 : 1]; + if (info.size > 1) { + if (runtime_) { + uint32_t key = *reinterpret_cast<const uint32_t *>(info.file); + MemoryStreamEnc *stream = new MemoryStreamEnc(info.file + sizeof(key), info.size - sizeof(key), key); + arch = runtime_->Add(runtime_->size(), stream->Size()); + stream->Seek(0, soBeginning); + runtime_->stream_->Seek(0, soEnd); + runtime_->stream_->CopyFrom(*stream, static_cast<uint32_t>(stream->Size())); + delete stream; + + if (arch->ReadFromFile(foRead) != osSuccess) + throw std::runtime_error("Runtime error at OpenResource"); + } else { + runtime_ = new MacFile(NULL); + if (!runtime_->OpenResource(info.file, info.size, true)) + throw std::runtime_error("Runtime error at OpenResource"); + arch = runtime_->item(0); + } + + Buffer buffer(info.code); + arch->ReadFromBuffer(buffer); + for (i = 0; i < arch->function_list()->count(); i++) { + arch->function_list()->item(i)->set_from_runtime(true); + } + for (i = 0; i < arch->import_list()->count(); i++) { + MacImport *import = arch->import_list()->item(i); + for (size_t j = 0; j < import->count(); j++) { + import->item(j)->include_option(ioFromRuntime); + } + } + } + } + + if (!IFile::Compile(options)) + return false; + + if (fat_magic_) { + Seek(sizeof(fat_header)); + + fat_arch header; + + for (i = 0; i < count(); i++) { + MacArchitecture *arch = item(i); + header.cputype = arch->type(); + header.cpusubtype = arch->cpu_subtype(); + header.offset = static_cast<uint32_t>(arch->offset()); + header.size = static_cast<uint32_t>(arch->size()); + header.align = 0x0c; // 2 << 0xc = 0x1000 + + if (fat_magic_ == FAT_CIGAM) { + header.cputype = __builtin_bswap32(header.cputype); + header.cpusubtype = __builtin_bswap32(header.cpusubtype); + header.offset = __builtin_bswap32(header.offset); + header.size = __builtin_bswap32(header.size); + header.align = __builtin_bswap32(header.align); + } + Write(&header, sizeof(header)); + } + } + + return true; +} + +bool MacFile::is_executable() const +{ +#ifdef __unix__ + return false; +#elif __APPLE__ + for (size_t i = 0; i < count(); i++) { + if (item(i)->is_executable()) + return true; + } +#endif + return false; +} + +uint32_t MacFile::disable_options() const +{ + uint32_t res = cpResourceProtection | cpImportProtection | cpVirtualFiles; + for (size_t i = 0; i < count(); i++) { + MacArchitecture *arch = item(i); + if (arch->file_type() != MH_EXECUTE) + res |= cpStripFixups; + } + return res; +} + +bool MacFixupList::RebaseInfoHelper::operator()(const MacFixup *left, const MacFixup *right) const +{ + // sort by type, then address + if ( left->bind_type() != right->bind_type()) + return (left->bind_type() < right->bind_type()); + return (left->address() < right->address()); +}
\ No newline at end of file |