diff --git a/.gitignore b/.gitignore index 2ecbbf8c2..5076104ad 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,9 @@ Thumbs.db .kdev_include_paths *~ +Makefile +CMakeFiles +cmake_install.cmake +CMakeCache.txt + diff --git a/CMakeLists.txt b/CMakeLists.txt index 7486a5396..203a83e6c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,5 +5,43 @@ if(COMMAND cmake_policy) cmake_policy(SET CMP0003 NEW) endif(COMMAND cmake_policy) -add_subdirectory(dyld) -add_subdirectory(libSystem) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/libmach-o) + +set(mach-o_SRCS + src/libmach-o/MachO.cpp + src/libmach-o/FatMachO.cpp + src/libmach-o/RebaseState.cpp + src/libmach-o/BindState.cpp + src/libmach-o/leb.cpp + src/libmach-o/MachOImpl.cpp + src/libmach-o/log.cc +) + +add_library(mach-o SHARED ${mach-o_SRCS}) +set_target_properties(mach-o PROPERTIES VERSION 1.0.0 SOVERSION 1.0) +set_target_properties(mach-o PROPERTIES COMPILE_FLAGS "-fvisibility=hidden") +target_link_libraries(mach-o -ldl -lpthread) + +set(dyld_SRCS + src/dyld/FileMap.cpp + src/dyld/ld-mac.cc + src/dyld/log.cc +) + +add_executable(dyld ${dyld_SRCS}) + +target_link_libraries(dyld -ldl -lpthread mach-o) + +set(fatmacho-exract_SRCS + src/dyld/extract.cpp +) + +add_executable(fatmacho-exract ${fatmacho-exract_SRCS}) + +target_link_libraries(fatmacho-exract -ldl -lpthread mach-o) + +install(TARGETS dyld fatmacho-exract DESTINATION bin) + + +add_subdirectory(src/libSystem) diff --git a/dyld/mach-o.cc b/dyld/mach-o.cc deleted file mode 100644 index 1431a8596..000000000 --- a/dyld/mach-o.cc +++ /dev/null @@ -1,840 +0,0 @@ -// Copyright 2011 Shinichiro Hamaji. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY Shinichiro Hamaji ``AS IS'' AND ANY -// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Shinichiro Hamaji OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -// SUCH DAMAGE. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "FatMachO.h" -#include "log.h" -#include "MachO.h" -#include - -DEFINE_bool(READ_SYMTAB, -#ifdef NDEBUG - false, -#else - true, -#endif - "Read symtab for better backtrace"); -DEFINE_bool(READ_DYSYMTAB, false, "Read dysymtab"); - -using namespace std; -typedef long long ll; -typedef unsigned long long ull; - -struct sym { - uint32_t name; - uint32_t addr; - uint32_t flag; -}; - -struct sym64 { - uint32_t name; - uint64_t addr; - uint32_t flag; -}; - -// See mach-o/nlist.h for this layout -struct nlist { - uint32_t n_strx; - uint8_t n_type; - uint8_t n_sect; - uint16_t n_desc; - uint64_t n_value; -}; - -#define N_WEAK_DEF 0x0080 - -static uint64_t uleb128(const uint8_t*& p) { - uint64_t r = 0; - int s = 0; - do { - r |= (uint64_t)(*p & 0x7f) << s; - s += 7; - } while (*p++ >= 0x80); - return r; -} - -static int64_t sleb128(const uint8_t*& p) { - int64_t r = 0; - int s = 0; - for (;;) { - uint8_t b = *p++; - if (b < 0x80) { - if (b & 0x40) { - r -= (0x80 - b) << s; - } - else { - r |= (b & 0x3f) << s; - } - break; - } - r |= (b & 0x7f) << s; - s += 7; - } - return r; -} - -class MachOImpl : public MachO { - public: - // Take ownership of fd. - // If len is 0, the size of file will be used as len. - MachOImpl(const char* filename, int fd, size_t offset, size_t len, - bool need_exports); - virtual ~MachOImpl(); - virtual void close(); - - private: - class RebaseState; - friend class MachOImpl::RebaseState; - class BindState; - friend class MachOImpl::BindState; - - template - void readSegment(char* cmds_ptr, - vector* segments, - vector* bind_sections); - void readRebase(const uint8_t* p, const uint8_t* end); - void readBind(const uint8_t* p, const uint8_t* end, bool is_weak); - void readExport(const uint8_t* start, const uint8_t* p, const uint8_t* end, - string* name_buf); - - template - void readClassicBind(const section& sec, - uint32_t* dysyms, - uint32_t* symtab, - const char* symstrtab) { - uint32_t indirect_offset = sec.reserved1; - int count = sec.size / m_ptrsize; - for (int i = 0; i < count; i++) { - uint32_t dysym = dysyms[indirect_offset + i]; - uint32_t index = dysym & 0x3fffffff; - nlist* sym = (nlist*)(symtab + index * (m_is64 ? 4 : 3)); - - MachO::Bind* bind = new MachO::Bind(); - bind->name = symstrtab + sym->n_strx; - bind->vmaddr = sec.addr + i * m_ptrsize; - bind->value = sym->n_value; - bind->type = BIND_TYPE_POINTER; - bind->ordinal = 1; - bind->is_weak = ((sym->n_desc & N_WEAK_DEF) != 0); - bind->is_classic = true; - LOGF("add classic bind! %s type=%d sect=%d desc=%d value=%lld " - "vmaddr=%p is_weak=%d\n", - bind->name.c_str(), sym->n_type, sym->n_sect, sym->n_desc, (ll)sym->n_value, - (void*)(bind->vmaddr), bind->is_weak); - m_binds.push_back(bind); - } - } - - char* mapped_; - size_t mapped_size_; - bool need_m_exports; -}; - -template -void MachOImpl::readSegment(char* cmds_ptr, - vector* segments, - vector* bind_sections) { - segment_command* segment = - reinterpret_cast(cmds_ptr); - segments->push_back(segment); - - LOGF("segment %s: vmaddr=%p vmsize=%llu " - "fileoff=%llu filesize=%llu " - "maxprot=%d initprot=%d nsects=%u flags=%u\n", - segment->segname, - (void*)(intptr_t)segment->vmaddr, (ull)segment->vmsize, - (ull)segment->fileoff, (ull)segment->filesize, - segment->maxprot, segment->initprot, - segment->nsects, segment->flags); - - section* sections = reinterpret_cast( - cmds_ptr + sizeof(segment_command)); - for (uint32_t j = 0; j < segment->nsects; j++) { - const section& sec = sections[j]; - LOGF("section %s in %s: " - "addr=%p size=%llu offset=%u align=%u " - "reloff=%u nreloc=%u flags=%u " - "reserved1=%u reserved2=%u\n", - sec.sectname, sec.segname, - (void*)(intptr_t)sec.addr, (ull)sec.size, - sec.offset, sec.align, - sec.reloff, sec.nreloc, sec.flags, - sec.reserved1, sec.reserved2); - - if (!strcmp(sec.sectname, "__dyld") && - !strcmp(sec.segname, "__DATA")) { - m_dyld_data = sec.addr; - } - - int section_type = sec.flags & SECTION_TYPE; - switch (section_type) { - case S_REGULAR: - /* Regular section: nothing to do */ - break; - - case S_MOD_INIT_FUNC_POINTERS: { - for (uint64_t p = sec.addr; p < sec.addr + sec.size; p += m_ptrsize) { - m_init_funcs.push_back(p); - } - break; - } - case S_MOD_TERM_FUNC_POINTERS: - for (uint64_t p = sec.addr; p < sec.addr + sec.size; p += m_ptrsize) { - m_exit_funcs.push_back(p); - } - break; - case S_NON_LAZY_SYMBOL_POINTERS: - case S_LAZY_SYMBOL_POINTERS: { - bind_sections->push_back(sections + j); - break; - } - case S_ZEROFILL: - case S_CSTRING_LITERALS: - case S_4BYTE_LITERALS: - case S_8BYTE_LITERALS: - case S_LITERAL_POINTERS: - case S_SYMBOL_STUBS: - case S_COALESCED: - case S_GB_ZEROFILL: - case S_INTERPOSING: - case S_16BYTE_LITERALS: - case S_DTRACE_DOF: - case S_LAZY_DYLIB_SYMBOL_POINTERS: - LOGF("FIXME: section type %d will not be handled for %s in %s\n", - section_type, sec.sectname, sec.segname); - break; - - default: - fprintf(stderr, "Unknown section type: %d\n", section_type); - abort(); - break; - } - } -} - -struct MachOImpl::RebaseState { - explicit RebaseState(MachOImpl* mach0) - : mach(mach0), type(0), seg_index(0), seg_offset(0) {} - - bool readRebaseOp(const uint8_t*& p) { - uint8_t op = *p & REBASE_OPCODE_MASK; - uint8_t imm = *p & REBASE_IMMEDIATE_MASK; - p++; - switch (op) { - case REBASE_OPCODE_DONE: - return false; - - case REBASE_OPCODE_SET_TYPE_IMM: - type = imm; - break; - - case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: - seg_index = imm; - seg_offset = uleb128(p); - break; - - case REBASE_OPCODE_ADD_ADDR_ULEB: - seg_offset += uleb128(p); - break; - - case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: - seg_offset += imm * mach->m_ptrsize; - break; - - case REBASE_OPCODE_DO_REBASE_IMM_TIMES: - for (int i = 0; i < imm; i++) { - addRebase(); - } - break; - - case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: { - int count = uleb128(p); - for (int i = 0; i < count; i++) { - addRebase(); - } - break; - } - - case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: - addRebase(); - seg_offset += uleb128(p); - break; - - case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: { - int count = uleb128(p); - uint64_t skip = uleb128(p); - for (int i = 0; i < count; i++) { - addRebase(); - seg_offset += skip; - } - break; - } - - default: - fprintf(stderr, "unknown op: %x\n", op); - } - - return true; - } - - void addRebase() { - MachO::Rebase* rebase = new MachO::Rebase(); - uint64_t vmaddr; - if (mach->m_is64) { - vmaddr = mach->m_segments64[seg_index]->vmaddr; - } else { - vmaddr = mach->m_segments[seg_index]->vmaddr; - } - LOGF("add rebase! seg_index=%d seg_offset=%llu type=%d vmaddr=%p\n", - seg_index, (ull)seg_offset, type, (void*)vmaddr); - rebase->vmaddr = vmaddr + seg_offset; - rebase->type = type; - mach->m_rebases.push_back(rebase); - - seg_offset += mach->m_ptrsize; - } - - MachOImpl* mach; - uint8_t type; - int seg_index; - uint64_t seg_offset; -}; - -void MachOImpl::readRebase(const uint8_t* p, const uint8_t* end) { - RebaseState state(this); - while (p < end) { - if (!state.readRebaseOp(p)) - break; - } -} - -struct MachOImpl::BindState { - BindState(MachOImpl* mach0, bool is_weak0) - : mach(mach0), ordinal(0), sym_name(NULL), type(BIND_TYPE_POINTER), - addend(0), seg_index(0), seg_offset(0), is_weak(is_weak0) {} - - void readBindOp(const uint8_t*& p) { - uint8_t op = *p & BIND_OPCODE_MASK; - uint8_t imm = *p & BIND_IMMEDIATE_MASK; - p++; - LOGF("bind: op=%x imm=%d\n", op, imm); - switch (op) { - case BIND_OPCODE_DONE: - break; - - case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: - ordinal = imm; - break; - - case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: - ordinal = uleb128(p); - break; - - case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: - if (imm == 0) { - ordinal = 0; - } else { - ordinal = BIND_OPCODE_MASK | imm; - } - break; - - case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: - sym_name = reinterpret_cast(p); - p += strlen(sym_name) + 1; - LOGF("sym_name=%s\n", sym_name); - break; - - case BIND_OPCODE_SET_TYPE_IMM: - type = imm; - break; - - case BIND_OPCODE_SET_ADDEND_SLEB: - addend = sleb128(p); - break; - - case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: - seg_index = imm; - seg_offset = uleb128(p); - break; - - case BIND_OPCODE_ADD_ADDR_ULEB: - seg_offset += uleb128(p); - break; - - case BIND_OPCODE_DO_BIND: - addBind(); - break; - - case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: - LOGF("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB\n"); - addBind(); - seg_offset += uleb128(p); - break; - - case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: - LOGF("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED %d\n", (int)imm); - addBind(); - seg_offset += imm * mach->m_ptrsize; - break; - - case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: { - uint64_t count = uleb128(p); - uint64_t skip = uleb128(p); - LOGF("BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB %u %u\n", - (unsigned)count, (unsigned)skip); - for (uint64_t i = 0; i < count; i++) { - addBind(); - seg_offset += skip; - } - break; - } - - default: - fprintf(stderr, "unknown op: %x\n", op); - } - } - - void addBind() { - MachO::Bind* bind = new MachO::Bind(); - uint64_t vmaddr; - if (mach->m_is64) { - vmaddr = mach->m_segments64[seg_index]->vmaddr; - } else { - vmaddr = mach->m_segments[seg_index]->vmaddr; - } - LOGF("add bind! %s seg_index=%d seg_offset=%llu " - "type=%d ordinal=%d addend=%lld vmaddr=%p is_weak=%d\n", - sym_name, seg_index, (ull)seg_offset, - type, ordinal, (ll)addend, (void*)(vmaddr + seg_offset), is_weak); - bind->name = sym_name; - bind->vmaddr = vmaddr + seg_offset; - bind->addend = addend; - bind->type = type; - bind->ordinal = ordinal; - bind->is_weak = is_weak; - mach->m_binds.push_back(bind); - - seg_offset += mach->m_ptrsize; - } - - MachOImpl* mach; - uint8_t ordinal; - const char* sym_name; - uint8_t type; - int64_t addend; - int seg_index; - uint64_t seg_offset; - bool is_weak; -}; - -void MachOImpl::readBind(const uint8_t* p, const uint8_t* end, bool is_weak) { - BindState state(this, is_weak); - while (p < end) { - state.readBindOp(p); - } -} - -void MachOImpl::readExport(const uint8_t* start, - const uint8_t* p, - const uint8_t* end, - string* name_buf) { - LOGF("readExport: %p-%p\n", p, end); -#if 0 - char buf[17]; - buf[16] = '\0'; - for (int i = 0; i < 16*8; i++) { - LOGF("%02x ", p[i]); - buf[i % 16] = p[i] < 32 ? '?' : p[i]; - if (i % 16 == 15) LOGF("%s\n", buf); - } -#endif - - if (p >= end) { - fprintf(stderr, "broken export trie\n"); - exit(1); - } - - if (uint8_t term_size = *p++) { - const uint8_t* expected_term_end = p + term_size; - Export* exp = new Export; - exp->name = *name_buf; - exp->flag = uleb128(p); - exp->addr = uleb128(p); - LOGF("export: %s %lu %p\n", - name_buf->c_str(), (long)exp->flag, (void*)exp->addr); - - m_exports.push_back(exp); - - CHECK(expected_term_end == p); - } - - const uint8_t num_children = *p++; - for (uint8_t i = 0; i < num_children; i++) { - size_t orig_name_size = name_buf->size(); - while (*p) { - name_buf->push_back(*p++); - } - p++; - - uint64_t off = uleb128(p); - CHECK(off != 0); - readExport(start, start + off, end, name_buf); - - name_buf->resize(orig_name_size); - } -} - -MachOImpl::MachOImpl(const char* filename, int fd, size_t offset, size_t len, - bool need_exports) - : mapped_(NULL), mapped_size_(len) { - m_filename = filename; - need_m_exports = need_exports; - m_dyld_data = 0; - CHECK(fd); - m_fd = fd; - m_offset = offset; - - if (!mapped_size_) { - mapped_size_ = lseek(m_fd, 0, SEEK_END); - } - lseek(fd, 0, SEEK_SET); - - char* bin = mapped_ = reinterpret_cast( - mmap(NULL, mapped_size_, - PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, m_fd, offset)); - m_base = bin; - - mach_header* header = reinterpret_cast(bin); - LOGF("magic=%x cpu=%d cpusub=%d file=%d ncmds=%d sizecmd=%d flags=%x\n", - header->magic, header->cputype, header->cpusubtype, - header->filetype, header->ncmds, header->sizeofcmds, - header->flags); - - m_is64 = false; - if (header->magic == MH_MAGIC_64) { - m_is64 = true; - } else if (header->magic != MH_MAGIC) { - fprintf(stderr, "Not mach-o: %s\n", filename); - exit(1); - } - m_ptrsize = m_is64 ? 8 : 4; - - if ((header->cputype & 0x00ffffff) != CPU_TYPE_X86) { - fprintf(stderr, "Unsupported CPU\n"); - exit(1); - } - - struct load_command* cmds_ptr = reinterpret_cast( - bin + (m_is64 ? sizeof(mach_header_64) - : sizeof(mach_header))); - - uint32_t* symtab = NULL; - uint32_t* dysyms = NULL; - const char* symstrtab = NULL; - dyld_info_command* dyinfo = NULL; - vector bind_sections_64; - vector bind_sections_32; - - for (uint32_t ii = 0; ii < header->ncmds; ii++) { - LOGF("cmd type:%x\n", cmds_ptr->cmd); - - switch (cmds_ptr->cmd) { - case LC_SEGMENT_64: { - readSegment( - (char*)cmds_ptr, &m_segments64, &bind_sections_64); - break; - } - - case LC_SEGMENT: { - readSegment( - (char*)cmds_ptr, &m_segments, &bind_sections_32); - break; - } - - case LC_DYLD_INFO: - case LC_DYLD_INFO_ONLY: { - dyinfo = reinterpret_cast(cmds_ptr); - LOGF("dyld info: rebase_off=%u rebase_size=%u " - "bind_off=%u bind_size=%u " - "weak_bind_off=%u weak_bind_size=%u " - "lazy_bind_off=%u lazy_bind_size=%u " - "export_off=%u export_size=%u\n", - dyinfo->rebase_off, dyinfo->rebase_size, - dyinfo->bind_off, dyinfo->bind_size, - dyinfo->weak_bind_off, dyinfo->weak_bind_size, - dyinfo->lazy_bind_off, dyinfo->lazy_bind_size, - dyinfo->export_off, dyinfo->export_size); - - { - const uint8_t* p = reinterpret_cast( - bin + dyinfo->rebase_off); - const uint8_t* end = p + dyinfo->rebase_size; - if (dyinfo->rebase_off && dyinfo->rebase_size) { - readRebase(p, end); - } - } - - { - const uint8_t* p = reinterpret_cast( - bin + dyinfo->bind_off); - const uint8_t* end = p + dyinfo->bind_size; - readBind(p, end, false); - } - - { - const uint8_t* p = reinterpret_cast( - bin + dyinfo->lazy_bind_off); - const uint8_t* end = p + dyinfo->lazy_bind_size; - readBind(p, end, false); - } - - { - const uint8_t* p = reinterpret_cast( - bin + dyinfo->weak_bind_off); - const uint8_t* end = p + dyinfo->weak_bind_size; - readBind(p, end, true); - } - - if (need_m_exports) { - const uint8_t* p = reinterpret_cast( - bin + dyinfo->export_off); - const uint8_t* end = p + dyinfo->export_size; - if (dyinfo->export_off && dyinfo->export_size) { - string buf; - readExport(p, p, end, &buf); - } - } - - break; - } - - case LC_SYMTAB: { - symtab_command* symtab_cmd = - reinterpret_cast(cmds_ptr); - - LOGF("symoff=%u nsysm=%u stroff=%u strsize=%u\n", - symtab_cmd->symoff, symtab_cmd->nsyms, - symtab_cmd->stroff, symtab_cmd->strsize); - - uint32_t* symtab_top = symtab = - reinterpret_cast(bin + symtab_cmd->symoff); - symstrtab = bin + symtab_cmd->stroff; - - if (FLAGS_READ_SYMTAB) { - for (uint32_t i = 0; i < symtab_cmd->nsyms; i++) { - Symbol sym; - nlist* nl = (nlist*)symtab; - sym.name = symstrtab + nl->n_strx; - if (m_is64) { - sym.addr = nl->n_value; - symtab += 4; - } else { - sym.addr = (uint32_t)nl->n_value; - symtab += 3; - } - - LOGF("%d %s(%d) %p\n", - i, sym.name.c_str(), nl->n_strx, (void*)sym.addr); - m_symbols.push_back(sym); - } - } - - // Will be used by other load commands. - symtab = symtab_top; - - break; - } - - case LC_DYSYMTAB: { - dysymtab_command* dysymtab_cmd = - reinterpret_cast(cmds_ptr); - - LOGF("dysym:\n" - " ilocalsym=%u nlocalsym=%u\n" - " iextdefsym=%u nextdefsym=%u\n" - " iundefsym=%u nundefsym=%u\n" - " tocoff=%u ntoc=%u\n" - " modtaboff=%u nmodtab=%u\n" - " extrefsymoff=%u nextrefsyms=%u\n" - " indirectsymoff=%u nindirectsyms=%u\n" - " extreloff=%u nextrel=%u\n" - " locreloff=%u nlocrel=%u\n" - , - dysymtab_cmd->ilocalsym, dysymtab_cmd->nlocalsym, - dysymtab_cmd->iextdefsym, dysymtab_cmd->nextdefsym, - dysymtab_cmd->iundefsym, dysymtab_cmd->nundefsym, - dysymtab_cmd->tocoff, dysymtab_cmd->ntoc, - dysymtab_cmd->modtaboff, dysymtab_cmd->nmodtab, - dysymtab_cmd->extrefsymoff, dysymtab_cmd->nextrefsyms, - dysymtab_cmd->indirectsymoff, dysymtab_cmd->nindirectsyms, - dysymtab_cmd->extreloff, dysymtab_cmd->nextrel, - dysymtab_cmd->locreloff, dysymtab_cmd->nlocrel); - - if (dysymtab_cmd->nindirectsyms) { - dysyms = reinterpret_cast( - bin + dysymtab_cmd->indirectsymoff); - } - if (FLAGS_READ_DYSYMTAB) { - for (uint32_t j = 0; j < dysymtab_cmd->nindirectsyms; j++) { - uint32_t dysym = dysyms[j]; - uint32_t index = dysym & 0x3fffffff; - const char* local = - (dysym & INDIRECT_SYMBOL_LOCAL) ? " local" : ""; - const char* abs = - (dysym & INDIRECT_SYMBOL_ABS) ? " abs" : ""; - - uint32_t* sym = symtab; - sym += index * (m_is64 ? 4 : 3); - - LOGF("dysym %d %s(%u)%s%s\n", - j, symstrtab + sym[0], index, local, abs); - } - - uint32_t* dymods = reinterpret_cast( - bin + dysymtab_cmd->modtaboff); - for (uint32_t j = 0; j < dysymtab_cmd->nmodtab; j++) { - LOGF("dymods: %u\n", dymods[j]); - } - } - - break; - } - - case LC_LOAD_DYLINKER: { - lc_str name = reinterpret_cast(cmds_ptr)->name; - LOGF("dylinker: %s\n", (char*)cmds_ptr + name.offset); - break; - } - - case LC_UUID: - break; - - case LC_UNIXTHREAD: { - uint32_t* p = reinterpret_cast(cmds_ptr); - LOGF("UNIXTHREAD"); - for (uint32_t i = 2; i < p[1]; i++) { - LOGF(" %d:%x", i, p[i]); - } - LOGF("\n"); - if (m_is64) { - m_entry = reinterpret_cast(cmds_ptr)[18]; - } else { - m_entry = reinterpret_cast(cmds_ptr)[14]; - } - LOGF("entry=%llx\n", (ull)m_entry); - break; - } - - case LC_LOAD_DYLIB: { - dylib* lib = &reinterpret_cast(cmds_ptr)->dylib; - LOGF("dylib: '%s'\n", (char*)cmds_ptr + lib->name.offset); - m_dylibs.push_back((char*)cmds_ptr + lib->name.offset); - break; - } - - } - - cmds_ptr = reinterpret_cast( - reinterpret_cast(cmds_ptr) + cmds_ptr->cmdsize); - } - - LOGF("%p vs %p\n", cmds_ptr, bin + mapped_size_); - - // No LC_DYLD_INFO_ONLY, we will read classic binding info. - if (!dyinfo && dysyms && symtab && symstrtab) { - for (size_t i = 0; i < bind_sections_64.size(); i++) { - readClassicBind( - *bind_sections_64[i], dysyms, symtab, symstrtab); - } - for (size_t i = 0; i < bind_sections_32.size(); i++) { - readClassicBind
( - *bind_sections_32[i], dysyms, symtab, symstrtab); - } - } -} - -MachOImpl::~MachOImpl() { - close(); -} - -void MachOImpl::close() { - for (size_t i = 0; i < m_binds.size(); i++) { - delete m_binds[i]; - } - m_binds.clear(); - for (size_t i = 0; i < m_rebases.size(); i++) { - delete m_rebases[i]; - } - m_rebases.clear(); - for (size_t i = 0; i < m_exports.size(); i++) { - delete m_exports[i]; - } - m_exports.clear(); - - if (mapped_) { - munmap(mapped_, mapped_size_); - ::close(m_fd); - mapped_ = NULL; - m_fd = -1; - } -} - -MachO* MachO::readFile(std::string path, const char* arch, bool need_exports) { - int fd = open(path.c_str(), O_RDONLY); - if (fd < 0) { - fprintf(stderr, "%s: %s\n", path.c_str(), strerror(errno)); - exit(1); - } - - size_t offset = 0, len = 0; - map archs; - if (FatMachO::readFatInfo(fd, &archs)) { - map::const_iterator found = archs.find(arch); - if (found == archs.end()) { - fprintf(stderr, - "%s is a fat binary, but doesn't contain %s binary\n", - path.c_str(), arch); - exit(1); - } - offset = found->second.offset; - len = found->second.size; - LOGF("fat offset=%lu, len=%lu\n", - (unsigned long)offset, (unsigned long)len); - } - - return new MachOImpl(path.c_str(), fd, offset, len, need_exports); -} diff --git a/dyld/mach-o.h b/dyld/mach-o.h deleted file mode 100644 index f4709b553..000000000 --- a/dyld/mach-o.h +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2011 Shinichiro Hamaji. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY Shinichiro Hamaji ``AS IS'' AND ANY -// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Shinichiro Hamaji OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -// SUCH DAMAGE. - -#ifndef MACH_O_H_ -#define MACH_O_H_ - -#include - -#include -#include - -#include "mach-o/loader.h" - -using namespace std; - -class MachO { - public: - struct Rebase { - uint64_t vmaddr; - uint8_t type; - }; - - struct Bind { - uint64_t vmaddr; - const char* name; - union { - int64_t addend; - uint64_t value; - }; - uint8_t type; - uint8_t ordinal; - bool is_weak; - bool is_classic; - }; - - struct Export { - string name; - uint64_t addr; - uint32_t flag; - }; - - struct Symbol { - string name; - uint64_t addr; - }; - - static MachO* read(const char* path, const char* arch, - bool need_exports = true); - - virtual ~MachO() {} - virtual void close() = 0; - - const string& filename() const { return filename_; } - - const vector& segments64() const { - return segments64_; - } - - const vector& segments() const { - return segments_; - } - - const vector& dylibs() const { return dylibs_; } - - const vector& rebases() const { return rebases_; } - - const vector& binds() const { return binds_; } - - const vector& exports() const { return exports_; } - - const vector& symbols() const { return symbols_; } - - const char* base() const { return base_; } - - uint64_t entry() const { return entry_; } - - const vector& init_funcs() const { return init_funcs_; } - - uint64_t dyld_data() const { return dyld_data_; } - - bool is64() const { return is64_; } - - int fd() const { return fd_; } - size_t offset() const { return offset_; } - - protected: - string filename_; - vector segments64_; - vector segments_; - vector dylibs_; - vector rebases_; - vector binds_; - vector exports_; - vector symbols_; - const char* base_; - uint64_t entry_; - vector init_funcs_; - uint64_t dyld_data_; - bool is64_; - int ptrsize_; - int fd_; - size_t offset_; -}; - -#endif // MACH_O_H_ diff --git a/libSystem/kernel-mach/.kdev_include_paths b/libSystem/kernel-mach/.kdev_include_paths deleted file mode 100644 index e972af5e7..000000000 --- a/libSystem/kernel-mach/.kdev_include_paths +++ /dev/null @@ -1 +0,0 @@ -/home/lubos/Projects/darling/libmac/xnu diff --git a/libSystem/kernel-mach/vm.cpp~ b/libSystem/kernel-mach/vm.cpp~ deleted file mode 100644 index 491bbaa68..000000000 --- a/libSystem/kernel-mach/vm.cpp~ +++ /dev/null @@ -1,216 +0,0 @@ - -#include "vm.h" -#include "task.h" -#include "mach-stub.h" -#include -#include -#include -#include -#include -#include -#include - -static bool memory_readable(const void* ptr, size_t bytes) -{ - int fd = ::open("/dev/null", O_WRONLY); - if (fd == -1) - return false; - - int r = ::write(fd, ptr, bytes); - ::close(fd); - - if (r != bytes) - { - if (errno == EFAULT) - return false; - } - return true; -} - -static bool memory_writable(void* ptr, size_t bytes) -{ - int fd = ::open("/dev/zero", O_RDONLY); - if (fd == -1) - return false; - - int r = ::read(fd, ptr, bytes); - ::close(fd); - - if (r != bytes) - { - if (errno == EFAULT) - return false; - } - return true; -} - -kern_return_t vm_msync(vm_task_t target_task, void* addr, vm_size_t size, vm_sync_t in_flags) -{ - CHECK_TASK_SELF(target_task); - - int flags = 0; - if (in_flags & VM_SYNC_SYNCHRONOUS) - flags |= MS_SYNC; - if (in_flags & VM_SYNC_ASYNCHRONOUS) - flags |= MS_ASYNC; - if (in_flags & VM_SYNC_INVALIDATE) - flags |= MS_INVALIDATE; - - if (::msync(addr, size, flags) == -1) - return KERN_INVALID_ADDRESS; - else - return KERN_SUCCESS; -} - -kern_return_t vm_allocate(vm_task_t target_task, void** addr, vm_size_t size, boolean_t anywhere) -{ - CHECK_TASK_SELF(target_task); - - int flags = MAP_ANONYMOUS|MAP_PRIVATE; - - if (!anywhere) - flags |= MAP_FIXED; - - // Give the memory all permissions, as per vm_allocate definition - *addr = ::mmap(*addr, size, PROT_EXEC|PROT_READ|PROT_WRITE, flags, 0, 0); - - if (!*addr) - { - if (errno == EINVAL) - return KERN_INVALID_ADDRESS; - else - return KERN_NO_SPACE; // No other return values are permitted - } - else - return KERN_SUCCESS; -} - -kern_return_t vm_deallocate(vm_task_t target_task, void* addr, vm_size_t size) -{ - CHECK_TASK_SELF(target_task); - - if (::munmap(addr, size) == -1) - return KERN_INVALID_ADDRESS; - else - return KERN_SUCCESS; -} - -kern_return_t vm_copy(vm_task_t target_task, const void* source_address, vm_size_t count, void* dest_address) -{ - CHECK_TASK_SELF(target_task); - - if (!memory_readable(source_address, count)) - return KERN_INVALID_ADDRESS; - if (!memory_writable(dest_address, count)) - return KERN_INVALID_ADDRESS; - - ::memcpy(dest_address, source_address, count); - return KERN_SUCCESS; -} - -kern_return_t vm_write(vm_task_t target_task, void* address, const void* data, vm_size_t data_count) -{ - CHECK_TASK_SELF(target_task); - - if (!memory_writable(address, data_count)) - return KERN_INVALID_ADDRESS; - if (!memory_readable(data, data_count)) - return KERN_INVALID_ADDRESS; - - ::memcpy(address, data, data_count); - return KERN_SUCCESS; -} - -kern_return_t vm_behavior_set (vm_task_t target_task, void* address, vm_size_t size, vm_behavior_t behavior) -{ - CHECK_TASK_SELF(target_task); - MACH_STUB(); -} - -kern_return_t vm_inherit (vm_task_t target_task, void* address, vm_size_t size, vm_inherit_t new_inheritance) -{ - CHECK_TASK_SELF(target_task); - MACH_STUB(); -} - -kern_return_t vm_machine_attribute(vm_task_t target_task, void* address, vm_size_t size, vm_machine_attribute_t attribute, vm_machine_attribute_val_t value) -{ - CHECK_TASK_SELF(target_task); - MACH_STUB(); -} - -kern_return_t vm_map(vm_task_t target_task, void* address, vm_size_t size, void* mask, boolean_t anywhere, memory_object_t memory_object, vm_offset_t offset, - boolean_t copy, vm_prot_t cur_protection, vm_prot_t max_protection, vm_inherit_t inheritance) -{ - CHECK_TASK_SELF(target_task); - MACH_STUB(); -} - -kern_return_t vm_protect(vm_task_t target_task, void* address, vm_size_t size, boolean_t set_maximum, vm_prot_t new_protection) -{ - CHECK_TASK_SELF(target_task); - - // FIXME: Here we ignore set_maximum, not sure what to do with that - int flags = 0; - - if (new_protection & VM_PROT_READ) - flags |= PROT_READ; - if (new_protection & VM_PROT_WRITE) - flags |= PROT_WRITE; - if (new_protection & VM_PROT_EXECUTE) - flags |= PROT_EXEC; - - if (::mprotect(address, size, flags) == -1) - { - if (errno == EACCES) - return KERN_PROTECTION_FAILURE; - else if (errno == EINVAL || errno == ENOMEM) - return KERN_INVALID_ADDRESS; - else - return KERN_FAILURE; - } - else - return KERN_SUCCESS; -} - -kern_return_t vm_read(vm_task_t target_task, const void* address, vm_size_t size, void** data_out, natural_t* data_count) -{ - CHECK_TASK_SELF(target_task); - - if (!memory_readable(address, size)) - return KERN_INVALID_ADDRESS; - - kern_return_t ret = vm_allocate(mach_task_self_, data_out, size, true); - if (ret != KERN_SUCCESS) - return ret; - - ::memcpy(*data_out, address, size); - *data_count = size; // Is this correct? - - return KERN_SUCCESS; -} - -kern_return_t vm_read_overwrite (vm_task_t target_task, void* address, vm_size_t size, void* data_in, natural_t* data_count) -{ - CHECK_TASK_SELF(target_task); - MACH_STUB(); -} - -kern_return_t vm_region(vm_task_t target_task, void* address, vm_size_t size, vm_region_flavor_t flavor, vm_region_info_t info, mach_msg_type_number_t info_count, memory_object_name_t object_name) -{ - CHECK_TASK_SELF(target_task); - MACH_STUB(); -} - -kern_return_t vm_remap(vm_task_t target_task, void* target_address, vm_size_t size, void* mask, boolean_t anywhere, mach_port_t source_task, void* source_address, boolean_t copy, - vm_prot_t cur_protection, vm_prot_t max_protection, vm_inherit_t inheritance) -{ - CHECK_TASK_SELF(target_task); - MACH_STUB(); -} - -kern_return_t vm_wire(host_priv_t host, vm_task_t target_task, void* address, vm_size_t size, vm_prot_t wired_access) -{ - CHECK_TASK_SELF(target_task); - MACH_STUB(); -} diff --git a/libmach-o/MachO.cpp b/libmach-o/MachO.cpp deleted file mode 100644 index c817be984..000000000 --- a/libmach-o/MachO.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "MachO.h" -#include "FatMachO.h" -#include -#include -#include -#include -#include -#include -#include -#include - -MachO* MachO::read(std::string path, const char* arch, bool need_exports) -{ - int fd = ::open(path.c_str(), O_RDONLY); - if (fd < 0) - { - std::cerr << path << ": " << strerror(errno) << std::endl; - return 0; - } - - size_t offset = 0, len = 0; - std::map archs; - if (FatMachO::readFatInfo(fd, &archs)) - { - std::map::const_iterator found = archs.find(arch); - if (found == archs.end()) - { - std::cerr << path << " is a fat binary, but doesn't support the following architecture: " << arch << std::endl; - return 0; - } - - offset = found->second.offset; - len = found->second.size; - LOGF("fat offset=%lu, len=%lu\n", (unsigned long)offset, (unsigned long)len); - } - - return new MachOImpl(path, fd, offset, len, need_exports); -} diff --git a/dyld/CMakeLists.txt b/src/dyld/CMakeLists.txt similarity index 85% rename from dyld/CMakeLists.txt rename to src/dyld/CMakeLists.txt index 13e0c073c..9bd6447d4 100644 --- a/dyld/CMakeLists.txt +++ b/src/dyld/CMakeLists.txt @@ -10,10 +10,9 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../libmach-o) set(dyld_SRCS - FatMachO.cpp + FileMap.cpp ld-mac.cc log.cc - mach-o.cc ) add_executable(dyld ${dyld_SRCS}) @@ -22,11 +21,10 @@ target_link_libraries(dyld -ldl -lpthread) set(fatmacho-exract_SRCS extract.cpp - FatMachO.cpp ) add_executable(fatmacho-exract ${fatmacho-exract_SRCS}) -target_link_libraries(fatmacho-exract -ldl -lpthread) +target_link_libraries(fatmacho-exract -ldl -lpthread mach-o) install(TARGETS dyld fatmacho-exract DESTINATION bin) diff --git a/src/dyld/FileMap.cpp b/src/dyld/FileMap.cpp new file mode 100644 index 000000000..93ab988c6 --- /dev/null +++ b/src/dyld/FileMap.cpp @@ -0,0 +1,97 @@ +// Copyright 2011 Shinichiro Hamaji. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY Shinichiro Hamaji ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Shinichiro Hamaji OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. + +#include "FileMap.h" +#include +#include +#include +#include +#include + +void FileMap::add(const MachO& mach, uintptr_t slide, uintptr_t base) +{ + SymbolMap* symbol_map = new SymbolMap(); + symbol_map->filename = mach.filename(); + symbol_map->base = base; + if (!m_maps.insert(std::make_pair(base, symbol_map)).second) + { + err(1, "dupicated base addr: %p in %s", + (void*)base, mach.filename().c_str()); + } + + for (size_t i = 0; i < mach.symbols().size(); i++) + { + MachO::Symbol sym = mach.symbols()[i]; + if (sym.name.empty() || sym.name[0] != '_') + continue; + sym.addr += slide; + if (sym.addr < base) + continue; + symbol_map->symbols.insert(std::make_pair(sym.addr, sym.name.substr(1))); + } +} + +void FileMap::addWatchDog(uintptr_t addr) +{ + bool r = m_maps.insert(std::make_pair(addr, (SymbolMap*)NULL)).second; + assert(r); +} + +const char* FileMap::dumpSymbol(void* p) +{ + uintptr_t addr = reinterpret_cast(p); + std::map::const_iterator found = m_maps.upper_bound(addr); + if (found == m_maps.begin() || found == m_maps.end()) + { + return 0; + } + + --found; + return dumpSymbolFromMap(*found->second, addr); +} + +const char* FileMap::dumpSymbolFromMap(const SymbolMap& symbol_map, uintptr_t addr) +{ + uintptr_t file_offset = addr - symbol_map.base; + + // Use lower_bound as PC may be in just after call. + std::map::const_iterator found = symbol_map.symbols.lower_bound(addr); + if (found == symbol_map.symbols.begin()) + { + snprintf(m_dumped_stack_frame_buf, 4095, "%s [%p(%lx)]", + symbol_map.filename.c_str(), (void*)addr, (long)file_offset); + return m_dumped_stack_frame_buf; + } + + --found; + const char* name = found->second.c_str(); + uintptr_t func_offset = addr - found->first; + snprintf(m_dumped_stack_frame_buf, 4095, "%s(%s+%lx) [%p(%lx)]", + symbol_map.filename.c_str(), name, (long)func_offset, + (void*)addr, (long)file_offset); + return m_dumped_stack_frame_buf; +} diff --git a/src/dyld/FileMap.h b/src/dyld/FileMap.h new file mode 100644 index 000000000..8a2da8dfa --- /dev/null +++ b/src/dyld/FileMap.h @@ -0,0 +1,59 @@ +// Copyright 2011 Shinichiro Hamaji. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY Shinichiro Hamaji ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Shinichiro Hamaji OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. + +#ifndef FILEMAP_H +#define FILEMAP_H +#include +#include +#include +#include "MachO.h" + +class FileMap +{ +public: + void add(const MachO& mach, uintptr_t slide, uintptr_t base); + + void addWatchDog(uintptr_t addr); + + const char* dumpSymbol(void* p); + +private: + struct SymbolMap + { + std::string filename; + std::map symbols; + uintptr_t base; + }; + + const char* dumpSymbolFromMap(const SymbolMap& symbol_map, uintptr_t addr); + + std::map m_maps; + char m_dumped_stack_frame_buf[4096]; +}; + + +#endif diff --git a/dyld/env_flags.h b/src/dyld/env_flags.h similarity index 100% rename from dyld/env_flags.h rename to src/dyld/env_flags.h diff --git a/dyld/extract.cpp b/src/dyld/extract.cpp similarity index 100% rename from dyld/extract.cpp rename to src/dyld/extract.cpp diff --git a/dyld/ld-mac.cc b/src/dyld/ld-mac.cc similarity index 92% rename from dyld/ld-mac.cc rename to src/dyld/ld-mac.cc index 86ab7f102..a6ac3b29c 100644 --- a/dyld/ld-mac.cc +++ b/src/dyld/ld-mac.cc @@ -55,6 +55,7 @@ #include "FatMachO.h" #include "log.h" #include "MachO.h" +#include "FileMap.h" using namespace std; using namespace std::tr1; @@ -87,75 +88,6 @@ struct Timer { clock_t start_time; }; -class FileMap { - public: - void add(const MachO& mach, uintptr_t slide, uintptr_t base) { - SymbolMap* symbol_map = new SymbolMap(); - symbol_map->filename = mach.filename(); - symbol_map->base = base; - if (!maps_.insert(make_pair(base, symbol_map)).second) { - err(1, "dupicated base addr: %p in %s", - (void*)base, mach.filename().c_str()); - } - - for (size_t i = 0; i < mach.symbols().size(); i++) { - MachO::Symbol sym = mach.symbols()[i]; - if (sym.name.empty() || sym.name[0] != '_') - continue; - sym.addr += slide; - if (sym.addr < base) - continue; - symbol_map->symbols.insert(make_pair(sym.addr, sym.name.substr(1))); - } - } - - void addWatchDog(uintptr_t addr) { - bool r = maps_.insert(make_pair(addr, (SymbolMap*)NULL)).second; - CHECK(r); - } - - const char* dumpSymbol(void* p) { - uintptr_t addr = reinterpret_cast(p); - map::const_iterator found = maps_.upper_bound(addr); - if (found == maps_.begin() || found == maps_.end()) { - return NULL; - } - - --found; - return dumpSymbolFromMap(*found->second, addr); - } - - private: - struct SymbolMap { - string filename; - map symbols; - uintptr_t base; - }; - - const char* dumpSymbolFromMap(const SymbolMap& symbol_map, uintptr_t addr) { - uintptr_t file_offset = addr - symbol_map.base; - - // Use lower_bound as PC may be in just after call. - map::const_iterator found = - symbol_map.symbols.lower_bound(addr); - if (found == symbol_map.symbols.begin()) { - snprintf(dumped_stack_frame_buf_, 4095, "%s [%p(%lx)]", - symbol_map.filename.c_str(), (void*)addr, (long)file_offset); - return dumped_stack_frame_buf_; - } - - --found; - const char* name = found->second.c_str(); - uintptr_t func_offset = addr - found->first; - snprintf(dumped_stack_frame_buf_, 4095, "%s(%s+%lx) [%p(%lx)]", - symbol_map.filename.c_str(), name, (long)func_offset, - (void*)addr, (long)file_offset); - return dumped_stack_frame_buf_; - } - - map maps_; - char dumped_stack_frame_buf_[4096]; -}; static FileMap g_file_map; diff --git a/dyld/log.cc b/src/dyld/log.cc similarity index 100% rename from dyld/log.cc rename to src/dyld/log.cc diff --git a/dyld/log.h b/src/dyld/log.h similarity index 100% rename from dyld/log.h rename to src/dyld/log.h diff --git a/dyld/macho2elf.cc b/src/dyld/macho2elf.cc similarity index 100% rename from dyld/macho2elf.cc rename to src/dyld/macho2elf.cc diff --git a/dyld/no_trampoline.tab b/src/dyld/no_trampoline.tab similarity index 100% rename from dyld/no_trampoline.tab rename to src/dyld/no_trampoline.tab diff --git a/dyld/rename.tab b/src/dyld/rename.tab similarity index 100% rename from dyld/rename.tab rename to src/dyld/rename.tab diff --git a/libSystem/CMakeLists.txt b/src/libSystem/CMakeLists.txt similarity index 100% rename from libSystem/CMakeLists.txt rename to src/libSystem/CMakeLists.txt diff --git a/libSystem/kernel-mach/host.cpp b/src/libSystem/kernel-mach/host.cpp similarity index 100% rename from libSystem/kernel-mach/host.cpp rename to src/libSystem/kernel-mach/host.cpp diff --git a/libSystem/kernel-mach/host.h b/src/libSystem/kernel-mach/host.h similarity index 100% rename from libSystem/kernel-mach/host.h rename to src/libSystem/kernel-mach/host.h diff --git a/libSystem/kernel-mach/lockset.cpp b/src/libSystem/kernel-mach/lockset.cpp similarity index 100% rename from libSystem/kernel-mach/lockset.cpp rename to src/libSystem/kernel-mach/lockset.cpp diff --git a/libSystem/kernel-mach/lockset.h b/src/libSystem/kernel-mach/lockset.h similarity index 100% rename from libSystem/kernel-mach/lockset.h rename to src/libSystem/kernel-mach/lockset.h diff --git a/libSystem/kernel-mach/mach-stub.h b/src/libSystem/kernel-mach/mach-stub.h similarity index 100% rename from libSystem/kernel-mach/mach-stub.h rename to src/libSystem/kernel-mach/mach-stub.h diff --git a/libSystem/kernel-mach/semaphore.cpp b/src/libSystem/kernel-mach/semaphore.cpp similarity index 100% rename from libSystem/kernel-mach/semaphore.cpp rename to src/libSystem/kernel-mach/semaphore.cpp diff --git a/libSystem/kernel-mach/semaphore.h b/src/libSystem/kernel-mach/semaphore.h similarity index 100% rename from libSystem/kernel-mach/semaphore.h rename to src/libSystem/kernel-mach/semaphore.h diff --git a/libSystem/kernel-mach/task.cpp b/src/libSystem/kernel-mach/task.cpp similarity index 100% rename from libSystem/kernel-mach/task.cpp rename to src/libSystem/kernel-mach/task.cpp diff --git a/libSystem/kernel-mach/task.h b/src/libSystem/kernel-mach/task.h similarity index 100% rename from libSystem/kernel-mach/task.h rename to src/libSystem/kernel-mach/task.h diff --git a/libSystem/kernel-mach/vm.cpp b/src/libSystem/kernel-mach/vm.cpp similarity index 100% rename from libSystem/kernel-mach/vm.cpp rename to src/libSystem/kernel-mach/vm.cpp diff --git a/libSystem/kernel-mach/vm.h b/src/libSystem/kernel-mach/vm.h similarity index 100% rename from libSystem/kernel-mach/vm.h rename to src/libSystem/kernel-mach/vm.h diff --git a/libSystem/libc/OSAtomic.cpp b/src/libSystem/libc/OSAtomic.cpp similarity index 100% rename from libSystem/libc/OSAtomic.cpp rename to src/libSystem/libc/OSAtomic.cpp diff --git a/libSystem/libc/OSAtomic.h b/src/libSystem/libc/OSAtomic.h similarity index 100% rename from libSystem/libc/OSAtomic.h rename to src/libSystem/libc/OSAtomic.h diff --git a/libSystem/libc/darwin_errno_codes.h b/src/libSystem/libc/darwin_errno_codes.h similarity index 100% rename from libSystem/libc/darwin_errno_codes.h rename to src/libSystem/libc/darwin_errno_codes.h diff --git a/libSystem/libc/darwin_to_linux.map b/src/libSystem/libc/darwin_to_linux.map similarity index 100% rename from libSystem/libc/darwin_to_linux.map rename to src/libSystem/libc/darwin_to_linux.map diff --git a/libSystem/libc/directmap.asm b/src/libSystem/libc/directmap.asm similarity index 100% rename from libSystem/libc/directmap.asm rename to src/libSystem/libc/directmap.asm diff --git a/libSystem/libc/directmap.lst b/src/libSystem/libc/directmap.lst similarity index 100% rename from libSystem/libc/directmap.lst rename to src/libSystem/libc/directmap.lst diff --git a/libSystem/libc/err.c b/src/libSystem/libc/err.c similarity index 100% rename from libSystem/libc/err.c rename to src/libSystem/libc/err.c diff --git a/libSystem/libc/err.h b/src/libSystem/libc/err.h similarity index 100% rename from libSystem/libc/err.h rename to src/libSystem/libc/err.h diff --git a/libSystem/libc/errno.cpp b/src/libSystem/libc/errno.cpp similarity index 87% rename from libSystem/libc/errno.cpp rename to src/libSystem/libc/errno.cpp index 5300d9177..4f03ef8eb 100644 --- a/libSystem/libc/errno.cpp +++ b/src/libSystem/libc/errno.cpp @@ -1,5 +1,5 @@ -#define _POSIX_C_SOURCE 200112L -#undef _GNU_SOURCE // for proper strerror_r + +#include #include #include #include @@ -15,6 +15,10 @@ static int linux_to_darwin[130]; extern "C" const char * const __darwin_sys_errlist[0]; extern "C" const int __darwin_sys_nerr = 0; +// For a non-gnu version of strerror_r +extern "C" int __xpg_strerror_r (int __errnum, char *__buf, size_t __buflen) + __THROW __nonnull ((2)); + int cthread_errno() { return errno; @@ -58,7 +62,7 @@ char* __darwin_strerror(int errnum) int __darwin_strerror_r(int errnum, char *strerrbuf, size_t buflen) { errnum = errnoDarwinToLinux(errnum); - return ::strerror_r(errnum, strerrbuf, buflen); + return ::__xpg_strerror_r(errnum, strerrbuf, buflen); } void __darwin_perror(const char *s) diff --git a/libSystem/libc/errno.h b/src/libSystem/libc/errno.h similarity index 100% rename from libSystem/libc/errno.h rename to src/libSystem/libc/errno.h diff --git a/libSystem/libc/libc-stub.h b/src/libSystem/libc/libc-stub.h similarity index 100% rename from libSystem/libc/libc-stub.h rename to src/libSystem/libc/libc-stub.h diff --git a/libSystem/libc/linux_to_darwin.map b/src/libSystem/libc/linux_to_darwin.map similarity index 100% rename from libSystem/libc/linux_to_darwin.map rename to src/libSystem/libc/linux_to_darwin.map diff --git a/libSystem/libc/mac.c b/src/libSystem/libc/mac.c similarity index 100% rename from libSystem/libc/mac.c rename to src/libSystem/libc/mac.c diff --git a/libSystem/libc/none.c b/src/libSystem/libc/none.c similarity index 100% rename from libSystem/libc/none.c rename to src/libSystem/libc/none.c diff --git a/libSystem/libc/popcountdi2.c b/src/libSystem/libc/popcountdi2.c similarity index 100% rename from libSystem/libc/popcountdi2.c rename to src/libSystem/libc/popcountdi2.c diff --git a/libSystem/libc/rune.h b/src/libSystem/libc/rune.h similarity index 100% rename from libSystem/libc/rune.h rename to src/libSystem/libc/rune.h diff --git a/libSystem/libc/runetable.c b/src/libSystem/libc/runetable.c similarity index 100% rename from libSystem/libc/runetable.c rename to src/libSystem/libc/runetable.c diff --git a/libSystem/libc/stack_protector-obsd.c b/src/libSystem/libc/stack_protector-obsd.c similarity index 100% rename from libSystem/libc/stack_protector-obsd.c rename to src/libSystem/libc/stack_protector-obsd.c diff --git a/libSystem/libc/strmode.c b/src/libSystem/libc/strmode.c similarity index 100% rename from libSystem/libc/strmode.c rename to src/libSystem/libc/strmode.c diff --git a/libSystem/xnu/bsd/i386/_types.h b/src/libSystem/xnu/bsd/i386/_types.h similarity index 100% rename from libSystem/xnu/bsd/i386/_types.h rename to src/libSystem/xnu/bsd/i386/_types.h diff --git a/libSystem/xnu/bsd/sys/appleapiopts.h b/src/libSystem/xnu/bsd/sys/appleapiopts.h similarity index 100% rename from libSystem/xnu/bsd/sys/appleapiopts.h rename to src/libSystem/xnu/bsd/sys/appleapiopts.h diff --git a/libSystem/xnu/mach/boolean.h b/src/libSystem/xnu/mach/boolean.h similarity index 100% rename from libSystem/xnu/mach/boolean.h rename to src/libSystem/xnu/mach/boolean.h diff --git a/libSystem/xnu/mach/i386/boolean.h b/src/libSystem/xnu/mach/i386/boolean.h similarity index 100% rename from libSystem/xnu/mach/i386/boolean.h rename to src/libSystem/xnu/mach/i386/boolean.h diff --git a/libSystem/xnu/mach/i386/kern_return.h b/src/libSystem/xnu/mach/i386/kern_return.h similarity index 100% rename from libSystem/xnu/mach/i386/kern_return.h rename to src/libSystem/xnu/mach/i386/kern_return.h diff --git a/libSystem/xnu/mach/i386/vm_param.h b/src/libSystem/xnu/mach/i386/vm_param.h similarity index 100% rename from libSystem/xnu/mach/i386/vm_param.h rename to src/libSystem/xnu/mach/i386/vm_param.h diff --git a/libSystem/xnu/mach/i386/vm_types.h b/src/libSystem/xnu/mach/i386/vm_types.h similarity index 100% rename from libSystem/xnu/mach/i386/vm_types.h rename to src/libSystem/xnu/mach/i386/vm_types.h diff --git a/libSystem/xnu/mach/kern_return.h b/src/libSystem/xnu/mach/kern_return.h similarity index 100% rename from libSystem/xnu/mach/kern_return.h rename to src/libSystem/xnu/mach/kern_return.h diff --git a/libSystem/xnu/mach/machine/boolean.h b/src/libSystem/xnu/mach/machine/boolean.h similarity index 100% rename from libSystem/xnu/mach/machine/boolean.h rename to src/libSystem/xnu/mach/machine/boolean.h diff --git a/libSystem/xnu/mach/machine/kern_return.h b/src/libSystem/xnu/mach/machine/kern_return.h similarity index 100% rename from libSystem/xnu/mach/machine/kern_return.h rename to src/libSystem/xnu/mach/machine/kern_return.h diff --git a/libSystem/xnu/mach/machine/vm_param.h b/src/libSystem/xnu/mach/machine/vm_param.h similarity index 100% rename from libSystem/xnu/mach/machine/vm_param.h rename to src/libSystem/xnu/mach/machine/vm_param.h diff --git a/libSystem/xnu/mach/machine/vm_types.h b/src/libSystem/xnu/mach/machine/vm_types.h similarity index 100% rename from libSystem/xnu/mach/machine/vm_types.h rename to src/libSystem/xnu/mach/machine/vm_types.h diff --git a/libSystem/xnu/mach/memory_object.h b/src/libSystem/xnu/mach/memory_object.h similarity index 100% rename from libSystem/xnu/mach/memory_object.h rename to src/libSystem/xnu/mach/memory_object.h diff --git a/libSystem/xnu/mach/memory_object_types.h b/src/libSystem/xnu/mach/memory_object_types.h similarity index 100% rename from libSystem/xnu/mach/memory_object_types.h rename to src/libSystem/xnu/mach/memory_object_types.h diff --git a/libSystem/xnu/mach/message.h b/src/libSystem/xnu/mach/message.h similarity index 100% rename from libSystem/xnu/mach/message.h rename to src/libSystem/xnu/mach/message.h diff --git a/libSystem/xnu/mach/port.h b/src/libSystem/xnu/mach/port.h similarity index 100% rename from libSystem/xnu/mach/port.h rename to src/libSystem/xnu/mach/port.h diff --git a/libSystem/xnu/mach/vm_attributes.h b/src/libSystem/xnu/mach/vm_attributes.h similarity index 100% rename from libSystem/xnu/mach/vm_attributes.h rename to src/libSystem/xnu/mach/vm_attributes.h diff --git a/libSystem/xnu/mach/vm_behavior.h b/src/libSystem/xnu/mach/vm_behavior.h similarity index 100% rename from libSystem/xnu/mach/vm_behavior.h rename to src/libSystem/xnu/mach/vm_behavior.h diff --git a/libSystem/xnu/mach/vm_inherit.h b/src/libSystem/xnu/mach/vm_inherit.h similarity index 100% rename from libSystem/xnu/mach/vm_inherit.h rename to src/libSystem/xnu/mach/vm_inherit.h diff --git a/libSystem/xnu/mach/vm_prot.h b/src/libSystem/xnu/mach/vm_prot.h similarity index 100% rename from libSystem/xnu/mach/vm_prot.h rename to src/libSystem/xnu/mach/vm_prot.h diff --git a/libSystem/xnu/mach/vm_region.h b/src/libSystem/xnu/mach/vm_region.h similarity index 100% rename from libSystem/xnu/mach/vm_region.h rename to src/libSystem/xnu/mach/vm_region.h diff --git a/libSystem/xnu/mach/vm_sync.h b/src/libSystem/xnu/mach/vm_sync.h similarity index 100% rename from libSystem/xnu/mach/vm_sync.h rename to src/libSystem/xnu/mach/vm_sync.h diff --git a/libSystem/xnu/mach/vm_types.h b/src/libSystem/xnu/mach/vm_types.h similarity index 100% rename from libSystem/xnu/mach/vm_types.h rename to src/libSystem/xnu/mach/vm_types.h diff --git a/src/libmach-o/BindState.cpp b/src/libmach-o/BindState.cpp new file mode 100644 index 000000000..dd2a15255 --- /dev/null +++ b/src/libmach-o/BindState.cpp @@ -0,0 +1,153 @@ +// Copyright 2011 Shinichiro Hamaji. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY Shinichiro Hamaji ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Shinichiro Hamaji OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. + + +#include "BindState.h" +#include "MachO.h" +#include "MachOImpl.h" +#include "log.h" +#include "leb.h" +#include + +typedef long long ll; +typedef unsigned long long ull; + +BindState::BindState(MachOImpl* mach0, bool is_weak0) + : mach(mach0), ordinal(0), sym_name(NULL), type(BIND_TYPE_POINTER), + addend(0), seg_index(0), seg_offset(0), is_weak(is_weak0) +{ +} + +void BindState::readBindOp(const uint8_t*& p) +{ + uint8_t op = *p & BIND_OPCODE_MASK; + uint8_t imm = *p & BIND_IMMEDIATE_MASK; + p++; + + LOGF("bind: op=%x imm=%d\n", op, imm); + + switch (op) + { + case BIND_OPCODE_DONE: + break; + + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + ordinal = imm; + break; + + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + ordinal = uleb128(p); + break; + + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + if (imm == 0) + ordinal = 0; + else + ordinal = BIND_OPCODE_MASK | imm; + + break; + + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + sym_name = reinterpret_cast(p); + p += strlen(sym_name) + 1; + LOGF("sym_name=%s\n", sym_name); + break; + + case BIND_OPCODE_SET_TYPE_IMM: + type = imm; + break; + + case BIND_OPCODE_SET_ADDEND_SLEB: + addend = sleb128(p); + break; + + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + seg_index = imm; + seg_offset = uleb128(p); + break; + + case BIND_OPCODE_ADD_ADDR_ULEB: + seg_offset += uleb128(p); + break; + + case BIND_OPCODE_DO_BIND: + addBind(); + break; + + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + LOGF("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB\n"); + addBind(); + seg_offset += uleb128(p); + break; + + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + LOGF("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED %d\n", (int)imm); + addBind(); + seg_offset += imm * mach->m_ptrsize; + break; + + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: { + uint64_t count = uleb128(p); + uint64_t skip = uleb128(p); + LOGF("BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB %u %u\n", + (unsigned)count, (unsigned)skip); + for (uint64_t i = 0; i < count; i++) + { + addBind(); + seg_offset += skip; + } + break; + } + + default: + fprintf(stderr, "unknown op: %x\n", op); + } + } + +void BindState::addBind() +{ + MachO::Bind* bind = new MachO::Bind(); + uint64_t vmaddr; + if (mach->m_is64) + vmaddr = mach->m_segments64[seg_index]->vmaddr; + else + vmaddr = mach->m_segments[seg_index]->vmaddr; + + LOGF("add bind! %s seg_index=%d seg_offset=%llu " + "type=%d ordinal=%d addend=%lld vmaddr=%p is_weak=%d\n", + sym_name, seg_index, (ull)seg_offset, + type, ordinal, (ll)addend, (void*)(vmaddr + seg_offset), is_weak); + bind->name = sym_name; + bind->vmaddr = vmaddr + seg_offset; + bind->addend = addend; + bind->type = type; + bind->ordinal = ordinal; + bind->is_weak = is_weak; + mach->m_binds.push_back(bind); + + seg_offset += mach->m_ptrsize; +} diff --git a/src/libmach-o/BindState.h b/src/libmach-o/BindState.h new file mode 100644 index 000000000..d88b4f7f1 --- /dev/null +++ b/src/libmach-o/BindState.h @@ -0,0 +1,53 @@ +// Copyright 2011 Shinichiro Hamaji. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY Shinichiro Hamaji ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Shinichiro Hamaji OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. + + +#ifndef BINDSTATE_H +#define BINDSTATE_H +#include "MachOImpl.h" + +struct BindState +{ + BindState(MachOImpl* mach0, bool is_weak0); + + void readBindOp(const uint8_t*& p); + + void addBind(); + + MachOImpl* mach; + uint8_t ordinal; + const char* sym_name; + uint8_t type; + int64_t addend; + int seg_index; + uint64_t seg_offset; + bool is_weak; +}; + + +#endif + diff --git a/src/libmach-o/CMakeLists.txt b/src/libmach-o/CMakeLists.txt new file mode 100644 index 000000000..8f7eb1a78 --- /dev/null +++ b/src/libmach-o/CMakeLists.txt @@ -0,0 +1,25 @@ +project(mach-o) + +cmake_minimum_required(VERSION 2.4.0) +if(COMMAND cmake_policy) + cmake_policy(SET CMP0003 NEW) +endif(COMMAND cmake_policy) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../include) +set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden") + + +set(mach-o_SRCS + MachO.cpp + FatMachO.cpp + RebaseState.cpp + BindState.cpp + leb.cpp + MachOImpl.cpp +) + +add_library(mach-o SHARED ${mach-o_SRCS}) +set_target_properties(mach-o PROPERTIES VERSION 1.0.0 SOVERSION 1.0) +target_link_libraries(mach-o -ldl -lpthread) + +install(TARGETS mach-o DESTINATION lib) diff --git a/dyld/FatMachO.cpp b/src/libmach-o/FatMachO.cpp similarity index 100% rename from dyld/FatMachO.cpp rename to src/libmach-o/FatMachO.cpp diff --git a/dyld/FatMachO.h b/src/libmach-o/FatMachO.h similarity index 97% rename from dyld/FatMachO.h rename to src/libmach-o/FatMachO.h index 729ded1f2..035fe8050 100644 --- a/dyld/FatMachO.h +++ b/src/libmach-o/FatMachO.h @@ -37,6 +37,7 @@ namespace FatMachO { // Reads fd and fills fat info. Returns true if fd is a valid fat binary. + __attribute__ ((visibility ("default"))) bool readFatInfo(int fd, std::map* fat); } diff --git a/src/libmach-o/MachO.cpp b/src/libmach-o/MachO.cpp new file mode 100644 index 000000000..1c3ed8f1e --- /dev/null +++ b/src/libmach-o/MachO.cpp @@ -0,0 +1,68 @@ +// Copyright 2011 Shinichiro Hamaji. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY Shinichiro Hamaji ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Shinichiro Hamaji OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. + + +#include "MachO.h" +#include "FatMachO.h" +#include "MachOImpl.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MachO* MachO::readFile(std::string path, const char* arch, bool need_exports) +{ + int fd = ::open(path.c_str(), O_RDONLY); + if (fd < 0) + { + std::cerr << path << ": " << strerror(errno) << std::endl; + return 0; + } + + size_t offset = 0, len = 0; + std::map archs; + if (FatMachO::readFatInfo(fd, &archs)) + { + std::map::const_iterator found = archs.find(arch); + if (found == archs.end()) + { + std::cerr << path << " is a fat binary, but doesn't support the following architecture: " << arch << std::endl; + return 0; + } + + offset = found->second.offset; + len = found->second.size; + // LOGF("fat offset=%lu, len=%lu\n", (unsigned long)offset, (unsigned long)len); + } + + return new MachOImpl(path.c_str(), fd, offset, len, need_exports); +} diff --git a/libmach-o/MachO.h b/src/libmach-o/MachO.h similarity index 94% rename from libmach-o/MachO.h rename to src/libmach-o/MachO.h index c22c78c41..42ac1a6da 100644 --- a/libmach-o/MachO.h +++ b/src/libmach-o/MachO.h @@ -39,6 +39,7 @@ class MachO { public: + __attribute__ ((visibility ("default"))) static MachO* readFile(std::string path, const char* arch, bool need_exports = true); virtual ~MachO() {} @@ -80,13 +81,9 @@ public: uint64_t addr; }; - const std::vector& segments64() const { - return m_segments64; - } + const std::vector& segments64() const { return m_segments64; } - const std::vector& segments() const { - return m_segments; - } + const std::vector& segments() const { return m_segments; } const std::vector& dylibs() const { return m_dylibs; } diff --git a/src/libmach-o/MachOImpl.cpp b/src/libmach-o/MachOImpl.cpp new file mode 100644 index 000000000..d3e6d4bfb --- /dev/null +++ b/src/libmach-o/MachOImpl.cpp @@ -0,0 +1,535 @@ +// Copyright 2011 Shinichiro Hamaji. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY Shinichiro Hamaji ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Shinichiro Hamaji OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. + + +#include "MachOImpl.h" +#include "log.h" +#include "RebaseState.h" +#include "BindState.h" +#include "leb.h" + +#include +#include +#include + +#define N_WEAK_DEF 0x0080 +#define FLAGS_READ_SYMTAB 1 +#define FLAGS_READ_DYSYMTAB 1 + +template +void MachOImpl::readClassicBind(const section& sec, uint32_t* dysyms, uint32_t* symtab, const char* symstrtab) +{ + uint32_t indirect_offset = sec.reserved1; + int count = sec.size / m_ptrsize; + + for (int i = 0; i < count; i++) + { + uint32_t dysym = dysyms[indirect_offset + i]; + uint32_t index = dysym & 0x3fffffff; + nlist* sym = (nlist*)(symtab + index * (m_is64 ? 4 : 3)); + + MachO::Bind* bind = new MachO::Bind(); + bind->name = symstrtab + sym->n_strx; + bind->vmaddr = sec.addr + i * m_ptrsize; + bind->value = sym->n_value; + bind->type = BIND_TYPE_POINTER; + bind->ordinal = 1; + bind->is_weak = ((sym->n_desc & N_WEAK_DEF) != 0); + bind->is_classic = true; + LOGF("add classic bind! %s type=%d sect=%d desc=%d value=%lld " + "vmaddr=%p is_weak=%d\n", + bind->name.c_str(), sym->n_type, sym->n_sect, sym->n_desc, (ll)sym->n_value, + (void*)(bind->vmaddr), bind->is_weak); + m_binds.push_back(bind); + } +} + + +template +void MachOImpl::readSegment(char* cmds_ptr, std::vector* segments, std::vector* bind_sections) +{ + segment_command* segment = reinterpret_cast(cmds_ptr); + segments->push_back(segment); + + LOGF("segment %s: vmaddr=%p vmsize=%llu " + "fileoff=%llu filesize=%llu " + "maxprot=%d initprot=%d nsects=%u flags=%u\n", + segment->segname, + (void*)(intptr_t)segment->vmaddr, (ull)segment->vmsize, + (ull)segment->fileoff, (ull)segment->filesize, + segment->maxprot, segment->initprot, + segment->nsects, segment->flags); + + section* sections = reinterpret_cast(cmds_ptr + sizeof(segment_command)); + + for (uint32_t j = 0; j < segment->nsects; j++) + { + const section& sec = sections[j]; + LOGF("section %s in %s: " + "addr=%p size=%llu offset=%u align=%u " + "reloff=%u nreloc=%u flags=%u " + "reserved1=%u reserved2=%u\n", + sec.sectname, sec.segname, + (void*)(intptr_t)sec.addr, (ull)sec.size, + sec.offset, sec.align, + sec.reloff, sec.nreloc, sec.flags, + sec.reserved1, sec.reserved2); + + if (!strcmp(sec.sectname, "__dyld") && !strcmp(sec.segname, "__DATA")) + m_dyld_data = sec.addr; + + int section_type = sec.flags & SECTION_TYPE; + switch (section_type) + { + case S_REGULAR: + /* Regular section: nothing to do */ + break; + + case S_MOD_INIT_FUNC_POINTERS: + { + for (uint64_t p = sec.addr; p < sec.addr + sec.size; p += m_ptrsize) + m_init_funcs.push_back(p); + + break; + } + case S_MOD_TERM_FUNC_POINTERS: + for (uint64_t p = sec.addr; p < sec.addr + sec.size; p += m_ptrsize) + m_exit_funcs.push_back(p); + + break; + case S_NON_LAZY_SYMBOL_POINTERS: + case S_LAZY_SYMBOL_POINTERS: + bind_sections->push_back(sections + j); + break; + + case S_ZEROFILL: + case S_CSTRING_LITERALS: + case S_4BYTE_LITERALS: + case S_8BYTE_LITERALS: + case S_LITERAL_POINTERS: + case S_SYMBOL_STUBS: + case S_COALESCED: + case S_GB_ZEROFILL: + case S_INTERPOSING: + case S_16BYTE_LITERALS: + case S_DTRACE_DOF: + case S_LAZY_DYLIB_SYMBOL_POINTERS: + LOGF("FIXME: section type %d will not be handled for %s in %s\n", + section_type, sec.sectname, sec.segname); + break; + + default: + fprintf(stderr, "Unknown section type: %d\n", section_type); + abort(); + break; + } + } +} + +void MachOImpl::readRebase(const uint8_t* p, const uint8_t* end) +{ + RebaseState state(this); + while (p < end) + { + if (!state.readRebaseOp(p)) + break; + } +} + + +void MachOImpl::readBind(const uint8_t* p, const uint8_t* end, bool is_weak) +{ + BindState state(this, is_weak); + while (p < end) + { + state.readBindOp(p); + } +} + +void MachOImpl::readExport(const uint8_t* start, const uint8_t* p, const uint8_t* end, std::string* name_buf) +{ + LOGF("readExport: %p-%p\n", p, end); +#if 0 + char buf[17]; + buf[16] = '\0'; + for (int i = 0; i < 16*8; i++) { + LOGF("%02x ", p[i]); + buf[i % 16] = p[i] < 32 ? '?' : p[i]; + if (i % 16 == 15) LOGF("%s\n", buf); + } +#endif + + if (p >= end) + { + fprintf(stderr, "broken export trie\n"); // TODO: throw an exception instead + exit(1); + } + + if (uint8_t term_size = *p++) + { + const uint8_t* expected_term_end = p + term_size; + Export* exp = new Export; + exp->name = *name_buf; + exp->flag = uleb128(p); + exp->addr = uleb128(p); + LOGF("export: %s %lu %p\n", + name_buf->c_str(), (long)exp->flag, (void*)exp->addr); + + m_exports.push_back(exp); + + CHECK(expected_term_end == p); + } + + const uint8_t num_children = *p++; + for (uint8_t i = 0; i < num_children; i++) + { + size_t orig_name_size = name_buf->size(); + while (*p) + name_buf->push_back(*p++); + p++; + + uint64_t off = uleb128(p); + CHECK(off != 0); + readExport(start, start + off, end, name_buf); + + name_buf->resize(orig_name_size); + } +} + +MachOImpl::MachOImpl(const char* filename, int fd, size_t offset, size_t len, bool need_exports) + : m_mapped(0), m_mapped_size(len) +{ + m_filename = filename; + m_need_exports = need_exports; + m_dyld_data = 0; + CHECK(fd); + m_fd = fd; + m_offset = offset; + + if (!m_mapped_size) + m_mapped_size = ::lseek(m_fd, 0, SEEK_END); + + ::lseek(fd, 0, SEEK_SET); + + char* bin = m_mapped = reinterpret_cast( + ::mmap(NULL, m_mapped_size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, m_fd, offset) + ); + m_base = bin; + + mach_header* header = reinterpret_cast(bin); + + LOGF("magic=%x cpu=%d cpusub=%d file=%d ncmds=%d sizecmd=%d flags=%x\n", + header->magic, header->cputype, header->cpusubtype, + header->filetype, header->ncmds, header->sizeofcmds, + header->flags); + + m_is64 = false; + if (header->magic == MH_MAGIC_64) + m_is64 = true; + else if (header->magic != MH_MAGIC) + { + fprintf(stderr, "Not mach-o: %s\n", filename); + exit(1); // TODO: throw instead + } + m_ptrsize = m_is64 ? 8 : 4; + + if ((header->cputype & 0x00ffffff) != CPU_TYPE_X86) + { + fprintf(stderr, "Unsupported CPU\n"); + exit(1); // TODO: throw instead + } + + // TODO: split into a method + + struct load_command* cmds_ptr = reinterpret_cast( + bin + (m_is64 ? sizeof(mach_header_64) : sizeof(mach_header)) + ); + + uint32_t* symtab = 0; + uint32_t* dysyms = 0; + const char* symstrtab = 0; + dyld_info_command* dyinfo = 0; + std::vector bind_sections_64; + std::vector bind_sections_32; + + for (uint32_t ii = 0; ii < header->ncmds; ii++) + { + LOGF("cmd type:%x\n", cmds_ptr->cmd); + + switch (cmds_ptr->cmd) + { + case LC_SEGMENT_64: + readSegment((char*)cmds_ptr, &m_segments64, &bind_sections_64); + break; + + + case LC_SEGMENT: + readSegment((char*)cmds_ptr, &m_segments, &bind_sections_32); + break; + + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + { + dyinfo = reinterpret_cast(cmds_ptr); + LOGF("dyld info: rebase_off=%u rebase_size=%u " + "bind_off=%u bind_size=%u " + "weak_bind_off=%u weak_bind_size=%u " + "lazy_bind_off=%u lazy_bind_size=%u " + "export_off=%u export_size=%u\n", + dyinfo->rebase_off, dyinfo->rebase_size, + dyinfo->bind_off, dyinfo->bind_size, + dyinfo->weak_bind_off, dyinfo->weak_bind_size, + dyinfo->lazy_bind_off, dyinfo->lazy_bind_size, + dyinfo->export_off, dyinfo->export_size); + + { + const uint8_t* p = reinterpret_cast( + bin + dyinfo->rebase_off); + const uint8_t* end = p + dyinfo->rebase_size; + if (dyinfo->rebase_off && dyinfo->rebase_size) + readRebase(p, end); + } + + { + const uint8_t* p = reinterpret_cast( + bin + dyinfo->bind_off); + const uint8_t* end = p + dyinfo->bind_size; + readBind(p, end, false); + } + + { + const uint8_t* p = reinterpret_cast( + bin + dyinfo->lazy_bind_off); + const uint8_t* end = p + dyinfo->lazy_bind_size; + readBind(p, end, false); + } + + { + const uint8_t* p = reinterpret_cast( + bin + dyinfo->weak_bind_off); + const uint8_t* end = p + dyinfo->weak_bind_size; + readBind(p, end, true); + } + + if (m_need_exports) + { + const uint8_t* p = reinterpret_cast( + bin + dyinfo->export_off); + const uint8_t* end = p + dyinfo->export_size; + if (dyinfo->export_off && dyinfo->export_size) + { + std::string buf; + readExport(p, p, end, &buf); + } + } + + break; + } + + case LC_SYMTAB: + { + symtab_command* symtab_cmd = reinterpret_cast(cmds_ptr); + + LOGF("symoff=%u nsysm=%u stroff=%u strsize=%u\n", + symtab_cmd->symoff, symtab_cmd->nsyms, + symtab_cmd->stroff, symtab_cmd->strsize); + + uint32_t* symtab_top = symtab = reinterpret_cast(bin + symtab_cmd->symoff); + symstrtab = bin + symtab_cmd->stroff; + + if (FLAGS_READ_SYMTAB) + { + for (uint32_t i = 0; i < symtab_cmd->nsyms; i++) + { + Symbol sym; + nlist* nl = (nlist*)symtab; + sym.name = symstrtab + nl->n_strx; + if (m_is64) + { + sym.addr = nl->n_value; + symtab += 4; + } + else + { + sym.addr = (uint32_t)nl->n_value; + symtab += 3; + } + + LOGF("%d %s(%d) %p\n", + i, sym.name.c_str(), nl->n_strx, (void*)sym.addr); + m_symbols.push_back(sym); + } + } + + // Will be used by other load commands. + symtab = symtab_top; + + break; + } + + case LC_DYSYMTAB: + { + dysymtab_command* dysymtab_cmd = reinterpret_cast(cmds_ptr); + + LOGF("dysym:\n" + " ilocalsym=%u nlocalsym=%u\n" + " iextdefsym=%u nextdefsym=%u\n" + " iundefsym=%u nundefsym=%u\n" + " tocoff=%u ntoc=%u\n" + " modtaboff=%u nmodtab=%u\n" + " extrefsymoff=%u nextrefsyms=%u\n" + " indirectsymoff=%u nindirectsyms=%u\n" + " extreloff=%u nextrel=%u\n" + " locreloff=%u nlocrel=%u\n" + , + dysymtab_cmd->ilocalsym, dysymtab_cmd->nlocalsym, + dysymtab_cmd->iextdefsym, dysymtab_cmd->nextdefsym, + dysymtab_cmd->iundefsym, dysymtab_cmd->nundefsym, + dysymtab_cmd->tocoff, dysymtab_cmd->ntoc, + dysymtab_cmd->modtaboff, dysymtab_cmd->nmodtab, + dysymtab_cmd->extrefsymoff, dysymtab_cmd->nextrefsyms, + dysymtab_cmd->indirectsymoff, dysymtab_cmd->nindirectsyms, + dysymtab_cmd->extreloff, dysymtab_cmd->nextrel, + dysymtab_cmd->locreloff, dysymtab_cmd->nlocrel); + + if (dysymtab_cmd->nindirectsyms) + { + dysyms = reinterpret_cast( + bin + dysymtab_cmd->indirectsymoff); + } + if (FLAGS_READ_DYSYMTAB) + { + for (uint32_t j = 0; j < dysymtab_cmd->nindirectsyms; j++) + { + uint32_t dysym = dysyms[j]; + uint32_t index = dysym & 0x3fffffff; + const char* local = + (dysym & INDIRECT_SYMBOL_LOCAL) ? " local" : ""; + const char* abs = + (dysym & INDIRECT_SYMBOL_ABS) ? " abs" : ""; + + uint32_t* sym = symtab; + sym += index * (m_is64 ? 4 : 3); + + LOGF("dysym %d %s(%u)%s%s\n", j, symstrtab + sym[0], index, local, abs); + } + + uint32_t* dymods = reinterpret_cast( bin + dysymtab_cmd->modtaboff); + for (uint32_t j = 0; j < dysymtab_cmd->nmodtab; j++) + LOGF("dymods: %u\n", dymods[j]); + } + + break; + } + + case LC_LOAD_DYLINKER: + { + lc_str name = reinterpret_cast(cmds_ptr)->name; + LOGF("dylinker: %s\n", (char*)cmds_ptr + name.offset); + break; + } + + case LC_UUID: + break; + + case LC_UNIXTHREAD: + { + uint32_t* p = reinterpret_cast(cmds_ptr); + LOGF("UNIXTHREAD"); + + for (uint32_t i = 2; i < p[1]; i++) + LOGF(" %d:%x", i, p[i]); + + LOGF("\n"); + + if (m_is64) + m_entry = reinterpret_cast(cmds_ptr)[18]; + else + m_entry = reinterpret_cast(cmds_ptr)[14]; + + LOGF("entry=%llx\n", (ull)m_entry); + break; + } + + case LC_LOAD_DYLIB: + { + dylib* lib = &reinterpret_cast(cmds_ptr)->dylib; + LOGF("dylib: '%s'\n", (char*)cmds_ptr + lib->name.offset); + m_dylibs.push_back((char*)cmds_ptr + lib->name.offset); + break; + } + + } + + cmds_ptr = reinterpret_cast( + reinterpret_cast(cmds_ptr) + cmds_ptr->cmdsize); + } + + LOGF("%p vs %p\n", cmds_ptr, bin + m_mapped_size); + + // No LC_DYLD_INFO_ONLY, we will read classic binding info. + if (!dyinfo && dysyms && symtab && symstrtab) + { + for (size_t i = 0; i < bind_sections_64.size(); i++) + { + readClassicBind(*bind_sections_64[i], dysyms, symtab, symstrtab); + } + for (size_t i = 0; i < bind_sections_32.size(); i++) + { + readClassicBind
(*bind_sections_32[i], dysyms, symtab, symstrtab); + } + } +} + +MachOImpl::~MachOImpl() +{ + close(); +} + +void MachOImpl::close() +{ + for (size_t i = 0; i < m_binds.size(); i++) + delete m_binds[i]; + + m_binds.clear(); + for (size_t i = 0; i < m_rebases.size(); i++) + delete m_rebases[i]; + + m_rebases.clear(); + for (size_t i = 0; i < m_exports.size(); i++) + delete m_exports[i]; + + m_exports.clear(); + + if (m_mapped) + { + ::munmap(m_mapped, m_mapped_size); + ::close(m_fd); + m_mapped = 0; + m_fd = -1; + } +} diff --git a/src/libmach-o/MachOImpl.h b/src/libmach-o/MachOImpl.h new file mode 100644 index 000000000..d7edf51d6 --- /dev/null +++ b/src/libmach-o/MachOImpl.h @@ -0,0 +1,93 @@ +// Copyright 2011 Shinichiro Hamaji. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY Shinichiro Hamaji ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Shinichiro Hamaji OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. + + +#ifndef MACHOIMPL_H +#define MACHOIMPL_H +#include "MachO.h" +#include +#include +#include +#include + +class MachOImpl : public MachO +{ +public: + // Takes ownership of fd. + // If len is 0, the size of file will be used as len. + MachOImpl(const char* filename, int fd, size_t offset, size_t len, bool need_exports); + + virtual ~MachOImpl(); + virtual void close(); + + typedef long long ll; + typedef unsigned long long ull; + +private: + friend class RebaseState; + friend class BindState; + + template + void readSegment(char* cmds_ptr, std::vector* segments, std::vector* bind_sections); + + void readRebase(const uint8_t* p, const uint8_t* end); + void readBind(const uint8_t* p, const uint8_t* end, bool is_weak); + void readExport(const uint8_t* start, const uint8_t* p, const uint8_t* end, std::string* name_buf); + + template + void readClassicBind(const section& sec, uint32_t* dysyms, uint32_t* symtab, const char* symstrtab); + + char* m_mapped; + size_t m_mapped_size; + bool m_need_exports; + + struct sym + { + uint32_t name; + uint32_t addr; + uint32_t flag; + }; + + struct sym64 + { + uint32_t name; + uint64_t addr; + uint32_t flag; + }; + + // See mach-o/nlist.h for this layout + struct nlist + { + uint32_t n_strx; + uint8_t n_type; + uint8_t n_sect; + uint16_t n_desc; + uint64_t n_value; + }; +}; + +#endif diff --git a/src/libmach-o/RebaseState.cpp b/src/libmach-o/RebaseState.cpp new file mode 100644 index 000000000..e0d309e16 --- /dev/null +++ b/src/libmach-o/RebaseState.cpp @@ -0,0 +1,123 @@ +// Copyright 2011 Shinichiro Hamaji. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY Shinichiro Hamaji ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Shinichiro Hamaji OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. + +#include "RebaseState.h" +#include "leb.h" +#include "log.h" +#include "MachOImpl.h" +#include + +typedef unsigned long long ull; + +RebaseState::RebaseState(MachOImpl* mach0) + : mach(mach0), type(0), seg_index(0), seg_offset(0) +{ +} + +bool RebaseState::readRebaseOp(const uint8_t*& p) +{ + uint8_t op = *p & REBASE_OPCODE_MASK; + uint8_t imm = *p & REBASE_IMMEDIATE_MASK; + p++; + + switch (op) + { + case REBASE_OPCODE_DONE: + return false; + + case REBASE_OPCODE_SET_TYPE_IMM: + type = imm; + break; + + case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + seg_index = imm; + seg_offset = uleb128(p); + break; + + case REBASE_OPCODE_ADD_ADDR_ULEB: + seg_offset += uleb128(p); + break; + + case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: + seg_offset += imm * mach->m_ptrsize; + break; + + case REBASE_OPCODE_DO_REBASE_IMM_TIMES: + for (int i = 0; i < imm; i++) + addRebase(); + break; + + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: + { + int count = uleb128(p); + for (int i = 0; i < count; i++) + addRebase(); + break; + } + + case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: + addRebase(); + seg_offset += uleb128(p); + break; + + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: + { + int count = uleb128(p); + uint64_t skip = uleb128(p); + for (int i = 0; i < count; i++) + { + addRebase(); + seg_offset += skip; + } + break; + } + + default: + fprintf(stderr, "unknown op: %x\n", op); + } + + return true; +} + +void RebaseState::addRebase() +{ + MachO::Rebase* rebase = new MachO::Rebase(); + uint64_t vmaddr; + if (mach->m_is64) + vmaddr = mach->m_segments64[seg_index]->vmaddr; + else + vmaddr = mach->m_segments[seg_index]->vmaddr; + + LOGF("add rebase! seg_index=%d seg_offset=%llu type=%d vmaddr=%p\n", + seg_index, (ull)seg_offset, type, (void*)vmaddr); + rebase->vmaddr = vmaddr + seg_offset; + rebase->type = type; + mach->m_rebases.push_back(rebase); + + seg_offset += mach->m_ptrsize; +} + diff --git a/src/libmach-o/RebaseState.h b/src/libmach-o/RebaseState.h new file mode 100644 index 000000000..6bfcb36bd --- /dev/null +++ b/src/libmach-o/RebaseState.h @@ -0,0 +1,47 @@ +// Copyright 2011 Shinichiro Hamaji. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY Shinichiro Hamaji ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Shinichiro Hamaji OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. + + +#ifndef REBASESTATE_H +#define REBASESTATE_H +#include "MachOImpl.h" +#include + +struct RebaseState +{ + explicit RebaseState(MachOImpl* mach0); + bool readRebaseOp(const uint8_t*& p); + void addRebase(); + + MachOImpl* mach; + uint8_t type; + int seg_index; + uint64_t seg_offset; +}; + + +#endif diff --git a/src/libmach-o/env_flags.h b/src/libmach-o/env_flags.h new file mode 100644 index 000000000..a85e6fc29 --- /dev/null +++ b/src/libmach-o/env_flags.h @@ -0,0 +1,56 @@ +// Copyright 2011 Shinichiro Hamaji. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY Shinichiro Hamaji ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Shinichiro Hamaji OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. + +#ifndef ENV_FLAGS_H_ +#define ENV_FLAGS_H_ + +#include +#include + +#define LD_MAC_EnvToBool(envname, dflt) \ + (!getenv(envname) ? (dflt) : memchr("tTyY1\0", getenv(envname)[0], 6) != NULL) + +#define LD_MAC_DECLARE_VARIABLE(type, name, tn) \ + namespace FLAG__namespace_do_not_use_directly_use_LD_MAC_DECLARE_##tn##_instead { \ + extern type FLAGS_##name; \ + } \ + using FLAG__namespace_do_not_use_directly_use_LD_MAC_DECLARE_##tn##_instead::FLAGS_##name + +#define LD_MAC_DEFINE_VARIABLE(type, name, value, meaning, tn) \ + namespace FLAG__namespace_do_not_use_directly_use_LD_MAC_DECLARE_##tn##_instead { \ + type FLAGS_##name(value); \ + } \ + using FLAG__namespace_do_not_use_directly_use_LD_MAC_DECLARE_##tn##_instead::FLAGS_##name; + +#define DECLARE_bool(name) \ + LD_MAC_DECLARE_VARIABLE(bool, name, bool) + +#define DEFINE_bool(name, value, meaning) \ + LD_MAC_DEFINE_VARIABLE(bool, name, LD_MAC_EnvToBool("LD_MAC_" #name, value), \ + meaning, bool) + +#endif // ENV_FLAGS_H_ diff --git a/src/libmach-o/leb.cpp b/src/libmach-o/leb.cpp new file mode 100644 index 000000000..a36e33c0f --- /dev/null +++ b/src/libmach-o/leb.cpp @@ -0,0 +1,63 @@ +// Copyright 2011 Shinichiro Hamaji. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY Shinichiro Hamaji ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Shinichiro Hamaji OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. + + +#include "leb.h" +#include + +uint64_t uleb128(const uint8_t*& p) +{ + uint64_t r = 0; + int s = 0; + do + { + r |= (uint64_t)(*p & 0x7f) << s; + s += 7; + } + while (*p++ >= 0x80); + return r; +} + +int64_t sleb128(const uint8_t*& p) +{ + int64_t r = 0; + int s = 0; + for (;;) { + uint8_t b = *p++; + if (b < 0x80) + { + if (b & 0x40) + r -= (0x80 - b) << s; + else + r |= (b & 0x3f) << s; + break; + } + r |= (b & 0x7f) << s; + s += 7; + } + return r; +} diff --git a/src/libmach-o/leb.h b/src/libmach-o/leb.h new file mode 100644 index 000000000..c51e77181 --- /dev/null +++ b/src/libmach-o/leb.h @@ -0,0 +1,36 @@ +// Copyright 2011 Shinichiro Hamaji. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY Shinichiro Hamaji ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Shinichiro Hamaji OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. + + +#ifndef LEB_H +#define LEB_H +#include + +uint64_t uleb128(const uint8_t*& p); +int64_t sleb128(const uint8_t*& p); + +#endif diff --git a/src/libmach-o/libmach-o.so.1.0 b/src/libmach-o/libmach-o.so.1.0 new file mode 120000 index 000000000..d6f61a4eb --- /dev/null +++ b/src/libmach-o/libmach-o.so.1.0 @@ -0,0 +1 @@ +libmach-o.so.1.0.0 \ No newline at end of file diff --git a/src/libmach-o/libmach-o.so.1.0.0 b/src/libmach-o/libmach-o.so.1.0.0 new file mode 100755 index 0000000000000000000000000000000000000000..d7be1d1290087b993c1f47693bccd3f3c36828bb GIT binary patch literal 208956 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%0DOW_Wxc>KZE56`&vm zH@FaMl|a=Y*3?R^GQ}9Q_7yq>2wCc=-wI-=G8;mcoVP zHZ6Kv9FJR|K?coENX=8@c!r$f)YNR$t`D+P5pxm9W7x>`JbLF2)iTroBdSvcYlT5t z1lT%NxutpNS3Sm;rev0-WE!LS1b3iRqRkP5&U23nE-^{XOUc2uMh4p{UeFmWGmPm< z)W)KBGR~zQ=rtma1_5$w06e*hv_1fl(m*Xx=+s}LCqfuHT8}O4!PX<~T7``h;%S0_ zYf$j=PONn(s1^a6id4;`qzLe;Ta*SGqAdsQet~7dEgx_@zQoXil*OH(K?-OpK$<=- zMykAFqtgg!+{py$c33%%v`heYd=XOc!3thzU5{By;c6Tp+7O^IQIHQnK1XV|gPKav zA_!#|3!xEQ3&PEZY?E0|y#Su$mvKV8fS3;M!rqfcLZ$Xo|zzG=f)^&{P7cWME;78r7(yX`o^P?-Z4HGWKQ@wOYpU!SP5- z=HaG-rwYO6y)(q8rX-dmg7PAG;W)@Qm_-Z5QZS@+kMkG-(B(pq6ZbLNr?A+CH*k^j z4B>QzWkd>*2auB%q?;X|UQrQWkXlrnpO=`ESyCBaW`Nl(LLY(0T*;0({|Zmeu#AJ6 zGN6SVB*%f00*)CRXq^C=w1HO&$i)-NlpHk45-gk0%RST*3hp>VW3bbZDl-(@u=ZZ@ z=LmF{A*v5Z_`s$d36BQj+DU;@WFY(nsUWa6Fra~kP>Y=6Fw!S1J~4&}aVB<9cb2qD z+!)vOaCo~+2Ck&;dWIHSu<9MlHa>z06lw${_Q0(hNLq!CKZWL%XXd5E2c;I5=7N@a zIy=W3>luKHloCo$ID;i(SWgdm3J^JmK%EGm1%(ARarFJOeFU}#B%R6!zl4Pdr| z%6ia>26(1HZk)obhSs^@b#&-mNT@ZS(iPmeC2;B<<|tfoNl|hx=GE_@QVV4z63oR| z3M+W&PuXD*X7R!C*{PNIH(_B^7${{5G&4dQVmRmiU;&G0jX*mI&~yjBs2WR!i}jX1 za2`iJNgvrwXyFC!0;FYv>WeB+lJ@a*a*j9BgIs)t+Vz3D5}G-%jjLm}8)3=82z)IE z_4cKq#0@;Yk!pL?i{Qa|6nDn}>!k=-#>S9Lz}gOkrAh2{G-MVS+^ItiSghqZ)(Z%O zixY!EE8#G&H$ZY4dM6&{Dy;n-Xr#glP2y`Ts77dNfHx432X2rnFVI>Vq>&P&krYFs z*Tpy+;yacFw8RG9%z{*qP&blLxnapXu;4?@I0Ypcpd;1elR@jBF)lQq)}arGF*ii~ zf(Fj8tT7;Cl*}SN*a@^R2;XcKS`fnG4a?8~q$mgX_rXIB&}tDaN^o~f;0jS2Ebz=t zX<_aQz9I>HfHxw4;%xumIf@n0w1T8Etl1Y@@`FbLQ4VZ_o?%Aq{xaMn&^iRxj6fY| zr&3^oCwM{gF`%ghP{|IuehFONV{0zJTm%}@f;AVY<`Cq&>=8HC!x}v(t5eY0Hb|)+ zl%+7cL5Ng>+|wc>pTYMwBX{`FO6f|p<3tE|8L%chL^+JK0t>wy#?q^V7zF@cbOhf3 literal 0 HcmV?d00001 diff --git a/src/libmach-o/log.cc b/src/libmach-o/log.cc new file mode 100644 index 000000000..43d38c053 --- /dev/null +++ b/src/libmach-o/log.cc @@ -0,0 +1,30 @@ +// Copyright 2011 Shinichiro Hamaji. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY Shinichiro Hamaji ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Shinichiro Hamaji OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. + +#include "env_flags.h" + +DEFINE_bool(LOG, false, "Output bunch of logs"); diff --git a/src/libmach-o/log.h b/src/libmach-o/log.h new file mode 100644 index 000000000..d02186e1b --- /dev/null +++ b/src/libmach-o/log.h @@ -0,0 +1,54 @@ +// Copyright 2011 Shinichiro Hamaji. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY Shinichiro Hamaji ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Shinichiro Hamaji OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. + +#ifndef LOG_H_ +#define LOG_H_ + +#include + +#include "env_flags.h" + +DECLARE_bool(LOG); + +#ifdef NOLOG +# define LOG if (0) cout +# define LOGF(...) if (0) fprintf(stderr, __VA_ARGS__) +#else +# define LOG if (FLAGS_LOG) cerr +# define LOGF(...) if (FLAGS_LOG) fprintf(stderr, __VA_ARGS__) +#endif + +#define ERR cerr + +#ifdef NDEBUG +// Do an extra check to avoid warning around unused local variables. +# define CHECK(r) do { if (!(r)) assert(r); } while (0) +#else +# define CHECK(r) assert(r); +#endif + +#endif