void MachObject::doBindSymbols()
{
    /*
     * Parsing the compressed dyld info section.
     */
    if (fIsClassic)
    {
        this->classicBindIndirect();
        this->classicBindExterns();
    }
    else
    {
        if (!fDyldInfo) {
            lnk::halt("no dyld info section");
        }

        /* Bind opcodes */
        this->readBind(addUintPtr2(fLinkEditBase, fDyldInfo->bind_off),
                       addUintPtr3(fLinkEditBase, fDyldInfo->bind_off, fDyldInfo->bind_size));

        this->readWeakBind(addUintPtr2(fLinkEditBase, fDyldInfo->weak_bind_off),
                           addUintPtr3(fLinkEditBase, fDyldInfo->weak_bind_off, fDyldInfo->weak_bind_size));

        this->readLazyBind(addUintPtr2(fLinkEditBase, fDyldInfo->lazy_bind_off),
                           addUintPtr3(fLinkEditBase, fDyldInfo->lazy_bind_off, fDyldInfo->lazy_bind_size));
    }
}
uintptr_t MachObject::exportedSymbolAddressCompressed(Symbol *sym)
{
    const uint8_t* exportNode = (uint8_t*)(sym->addr);
    const uint8_t* start = addUintPtr2(fLinkEditBase, fDyldInfo->export_off);
    const uint8_t* end = addUintPtr3(fLinkEditBase, fDyldInfo->export_off, fDyldInfo->export_size);
    bool runResolver = true;
    uintptr_t result = 0;

    if ((exportNode < start) || (exportNode > end))
        lnk::halt("symbol not in a trie");

    uint32_t flags = read_uleb128(exportNode, end);

    if ((flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_REGULAR) {
        if ( runResolver && (flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) ) {
            lnk::halt("XXX: resolvers not implemented, fix macho loader on line %d", __LINE__);
            return result;
        }

        return read_uleb128(exportNode, end) + (uintptr_t)fHeader;
    }
    else if ((flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL) {
        if (flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER)
            lnk::halt("unsupported local exported symbol kind. flags=%d at node=%p", flags, sym);

        return read_uleb128(exportNode, end) + (uintptr_t)fHeader;
    }
    else {
        lnk::halt("unsupported exported symbol kind. flags=%d at node=%p", flags, sym);
    }
}
void MachObject::doRebase()
{
    if (fIsClassic)
    {
        if (!fIsSplitSeg) {
            /*
             * Split segs do not need rebasing.
             */
            classicRebase();
        }
    }
    else
    {
        this->readRebase(addUintPtr2(fLinkEditBase, fDyldInfo->rebase_off),
                         addUintPtr3(fLinkEditBase, fDyldInfo->rebase_off, fDyldInfo->rebase_size));
    }
}
bool MachObject::findExportedSymbolCompressed(const char* symbol, Symbol* sym)
{
    /*
     This is a slightly tidier version of 'findExportedSymbol'
     from dyld. Still no f*****g idea what the semantics of it
     are since I suck at CS (lol, wtf is a trie?!).
     */

    /* export table sanity */
    if (fDyldInfo->export_size == 0)
        return false;

    const uint8_t* start = addUintPtr2(fLinkEditBase, fDyldInfo->export_off);
    const uint8_t* end = addUintPtr3(fLinkEditBase, fDyldInfo->export_off, fDyldInfo->export_size);

    const uint8_t* foundNodeStart = trie_walk(start, end, symbol);

    if (foundNodeStart != NULL) {
        const uint8_t* p = foundNodeStart;
        const uint32_t flags = read_uleb128(p, end);

        if (flags & EXPORT_SYMBOL_FLAGS_REEXPORT) {
            lnk::halt("no f*****g idea, honestly");
            return false;
        }
        else {
            sym->addr = (void*)foundNodeStart;
            sym->inImage = (void*)this;

            return true;
        }
    }
    else {
        return false;
    }
}
		/*
		 * This is used by the sliding function to read and execute
		 * the compressed reloc table.
		 */
		static void rebaseCompressed(struct dyld_info_command* dyldInfo,
									 macho_segment_command* linkEdit,
									 uintptr_t slide,
									 macho_segment_command** segments,
									 int segCount)
		{
			/*
			 * HARD MODE!!!
			 */

			const uint8_t* base = (uint8_t*)((linkEdit->vmaddr + slide) - linkEdit->fileoff);
			const uint8_t* start = addUintPtr2(dyldInfo->rebase_off, base);
			const uint8_t* end = addUintPtr3(dyldInfo->rebase_size, dyldInfo->rebase_off, base);
			const uint8_t* p = start;

			/*
			 * If you want a better documented version of this code, see 'MachObject.cpp'
			 */

			uint8_t type = 0;
			int segmentIndex = 0;
			uintptr_t address = (segments[0]->vmaddr + slide);
			uintptr_t segmentEndAddress = (segments[0]->vmaddr + slide + segments[0]->vmsize);
			uint32_t count;
			uint32_t skip;

			bool done = false;
			while ( !done && (p < end) ) {
				uint8_t immediate = *p & REBASE_IMMEDIATE_MASK;
				uint8_t opcode = *p & REBASE_OPCODE_MASK;

				++p;

				switch (opcode) {
					case REBASE_OPCODE_DONE:
						done = true;
						break;
					case REBASE_OPCODE_SET_TYPE_IMM:
						type = immediate;
						break;
					case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
						segmentIndex = immediate;
						if (segmentIndex > segCount)
							lnk::ldbg::printText("LNK_RELOC_ERROR: baaaad whatever \n");

						address = (segments[segmentIndex]->vmaddr + slide) + read_uleb128(p, end);
						segmentEndAddress = (segments[segmentIndex]->vmaddr + segments[segmentIndex]->vmsize + slide);
						break;
					case REBASE_OPCODE_ADD_ADDR_ULEB:
						address += read_uleb128(p, end);
						break;
					case REBASE_OPCODE_ADD_ADDR_IMM_SCALED:
						address += immediate*sizeof(uintptr_t);
						break;
					case REBASE_OPCODE_DO_REBASE_IMM_TIMES:
						for (int i=0; i < immediate; ++i) {
							if ( address >= segmentEndAddress )
								lnk::ldbg::printText("LNK_RELOC_ERROR: baaaad REBASE_OPCODE_DO_REBASE_IMM_TIMES \n");
							rebaseAt(address, slide, type);
							address += sizeof(uintptr_t);
						}
						break;
					case REBASE_OPCODE_DO_REBASE_ULEB_TIMES:
						count = read_uleb128(p, end);
						for (uint32_t i=0; i < count; ++i) {
							if ( address >= segmentEndAddress )
								lnk::ldbg::printText("LNK_RELOC_ERROR: baaaad REBASE_OPCODE_DO_REBASE_ULEB_TIMES \n");
							rebaseAt(address, slide, type);
							address += sizeof(uintptr_t);
						}
						break;
					case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
						if ( address >= segmentEndAddress )
							lnk::ldbg::printText("LNK_RELOC_ERROR: baaaad REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB \n");
						rebaseAt(address, slide, type);
						address += read_uleb128(p, end) + sizeof(uintptr_t);
						break;
					case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB:
						count = read_uleb128(p, end);
						skip = read_uleb128(p, end);
						for (uint32_t i=0; i < count; ++i) {
							if ( address >= segmentEndAddress )
								lnk::ldbg::printText("LNK_RELOC_ERROR: baaaad REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB \n");
							rebaseAt(address, slide, type);
							address += skip + sizeof(uintptr_t);
						}
						break;
					default:
						_printAbrt();
				}
			}
		}
MachObject* MachObject::instantiateFromFile(const char* path, const char* origPath, int fd, const struct stat* info)
{
    macho_header header;
    uint32_t ncmds = 0;
    void* cmd_base = NULL;
    uint32_t loader_bias = 0;
    size_t offset = 0;
    uint32_t map_size = 0;
    macho_segment_command* segments[64];
    uint32_t segment_count = 0;
    void* actual_map_address = NULL;
    uint32_t file_size = 0;

    if (gVerboseLog) {
        lnk::log("institating a MachObject from file\n"
                 "            path  : %s\n"
                 "            fd    : %p"
                 , path, fd);
    }

    /* Read mach-o header */
    memset(&header, 0, sizeof(header));
    read_at(fd, &header, sizeof(header), 0);

    /* Check sanity */
    if (header.magic != MH_MAGIC) {
        lnk::halt("instantiateFromFile(%s): invalid magic (%p instead of %p)",
                  path,
                  header.magic,
                  MH_MAGIC);
    }

    /* Read load commands into a buffer */
    cmd_base = malloc(header.sizeofcmds);
    read_at(fd, cmd_base, header.sizeofcmds, sizeof(macho_header));

    /* Set stuff */
    ncmds = header.ncmds;

    /* Build array of segment load commands  */
    for (int i = 0; i < ncmds; i++)
    {
        struct load_command	*lcp =
            (struct load_command *)((size_t)cmd_base + offset);

        offset += lcp->cmdsize;

        if (lcp->cmd == LC_SEGMENT)
        {
            segments[segment_count] = (macho_segment_command*)lcp;
            map_size += segments[segment_count]->vmsize;
            segment_count++;
        }
    }

    /* Check segment sanity */
    if (segment_count < 1) {
        lnk::halt("instantiateFromFile(%s): no segments", path);
    }

    /* Is this a fixed-load dylib? */
    if (segments[0]->vmaddr != 0) {
        /* Fixed, so not reserving */
        loader_bias = 0;
    }
    else
    {
        /* Reserve a continous range */
        loader_bias = (uint32_t)lnk::mm::reserve(map_size);
    }

    /* Map the segments */
    for (int i = 0; i < segment_count; i++)
    {
        macho_segment_command* seg = segments[i];
        uint32_t delta = 0;
        vm_size_t mm_filesize = round_page(seg->filesize);
        vm_size_t mm_vmsize = round_page(seg->vmsize);

        actual_map_address =
            lnk::mm::wire(fd,
                          pageAlignPtr((void*)addUintPtr2(seg->vmaddr, loader_bias)),
                          mm_filesize,
                          seg->fileoff);

        if (gVerboseLog) {
            lnk::log("(%s:%s): wired at %p (sz: %p)", path, seg->segname, actual_map_address, mm_filesize);
        }

        delta = mm_vmsize - mm_filesize;

        if (delta && !loader_bias)
        {
            /*
             * We need to zero-fill the memory as this space
             * isn't backed up be reserved memory.
             */

            void* anon_load =
                (void*)(addUintPtr3(seg->vmaddr, loader_bias, mm_filesize));

            anon_load = pageAlignPtr(anon_load);

            /* map anonymous memory to fill the rest */
            actual_map_address =
                lnk::mm::wire_anon(anon_load,
                                   delta);

            if (gVerboseLog) {
                lnk::log("(%s:%s): wired anon at %p (sz: %p)", path, seg->segname, actual_map_address, delta);
            }
        }
    }

    /* Add __TEXT to the linker table */
    linker_image_table_add((uintptr_t)addUintPtr2(segments[0]->vmaddr, loader_bias),
                           (size_t)segments[0]->vmsize,
                           path);

    /* Instiate a new object */
    MachObject* obj = new MachObject();

    /* Set the object properties */
    obj->fSlide = (uintptr_t)loader_bias;
    obj->fHeader = (macho_header*)addUintPtr2(segments[0]->vmaddr, loader_bias);
    obj->fModuleName = NULL;
    obj->fFilePath = path;
    obj->fOrigFilePath = origPath;
    obj->fIsSplitSeg = (loader_bias == 0);

    return obj;
}