+template <>
+bool Reader<x86_64>::addRelocReference(const macho_section<x86_64::P>* sect, const macho_relocation_info<x86_64::P>* reloc)
+{
+ uint64_t srcAddr;
+ uint64_t dstAddr = 0;
+ uint64_t addend;
+ uint32_t* fixUpPtr;
+ x86_64::ReferenceKinds kind;
+ bool result = false;
+ const macho_nlist<P>* targetSymbol = NULL;
+ const char* targetName = NULL;
+ srcAddr = sect->addr() + reloc->r_address();
+ fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address());
+ //fprintf(stderr, "addReloc type=%d\n", reloc->r_type());
+ if ( reloc->r_extern() ) {
+ targetSymbol = &fSymbols[reloc->r_symbolnum()];
+ targetName = &fStrings[targetSymbol->n_strx()];
+ }
+ switch ( reloc->r_type() ) {
+ case X86_64_RELOC_UNSIGNED:
+ if ( reloc->r_pcrel() )
+ throw "pcrel and X86_64_RELOC_UNSIGNED not supported";
+ if ( reloc->r_length() != 3 )
+ throw "length < 3 and X86_64_RELOC_UNSIGNED not supported";
+ dstAddr = E::get64(*((uint64_t*)fixUpPtr));
+ if ( reloc->r_extern() )
+ makeReferenceToSymbol(x86_64::kPointer, srcAddr, targetSymbol, dstAddr);
+ else
+ makeReference(x86_64::kPointer, srcAddr, dstAddr);
+ break;
+ case X86_64_RELOC_SIGNED:
+ if ( ! reloc->r_pcrel() )
+ throw "not pcrel and X86_64_RELOC_SIGNED not supported";
+ if ( reloc->r_length() != 2 )
+ throw "length != 2 and X86_64_RELOC_SIGNED not supported";
+ kind = x86_64::kPCRel32;
+ dstAddr = (int64_t)((int32_t)(E::get32(*fixUpPtr)));
+ if ( dstAddr == (uint64_t)(-1) ) {
+ dstAddr = 0;
+ kind = x86_64::kPCRel32_1;
+ }
+ else if ( dstAddr == (uint64_t)(-2) ) {
+ dstAddr = 0;
+ kind = x86_64::kPCRel32_2;
+ }
+ else if ( dstAddr == (uint64_t)(-4) ) {
+ dstAddr = 0;
+ kind = x86_64::kPCRel32_4;
+ }
+ if ( reloc->r_extern() )
+ makeReferenceToSymbol(kind, srcAddr, targetSymbol, dstAddr);
+ else {
+ makeReference(kind, srcAddr, srcAddr+4+dstAddr);
+ }
+ break;
+ case X86_64_RELOC_BRANCH:
+ if ( ! reloc->r_pcrel() )
+ throw "not pcrel and X86_64_RELOC_BRANCH not supported";
+ if ( reloc->r_length() != 2 )
+ throw "length != 2 and X86_64_RELOC_BRANCH not supported";
+ dstAddr = (int64_t)((int32_t)(E::get32(*fixUpPtr)));
+ if ( reloc->r_extern() ) {
+ if ( isWeakImportSymbol(targetSymbol) )
+ makeReferenceToSymbol(x86_64::kBranchPCRel32WeakImport, srcAddr, targetSymbol, dstAddr);
+ else
+ makeReferenceToSymbol(x86_64::kBranchPCRel32, srcAddr, targetSymbol, dstAddr);
+ }
+ else {
+ makeReference(x86_64::kBranchPCRel32, srcAddr, srcAddr+4+dstAddr);
+ }
+ break;
+ case X86_64_RELOC_GOT:
+ if ( ! reloc->r_extern() )
+ throw "not extern and X86_64_RELOC_GOT not supported";
+ if ( ! reloc->r_pcrel() )
+ throw "not pcrel and X86_64_RELOC_GOT not supported";
+ if ( reloc->r_length() != 2 )
+ throw "length != 2 and X86_64_RELOC_GOT not supported";
+ addend = (int64_t)((int32_t)(E::get32(*fixUpPtr)));
+ if ( isWeakImportSymbol(targetSymbol) )
+ makeReferenceToSymbol(x86_64::kPCRel32GOTWeakImport, srcAddr, targetSymbol, addend);
+ else
+ makeReferenceToSymbol(x86_64::kPCRel32GOT, srcAddr, targetSymbol, addend);
+ break;
+ case X86_64_RELOC_GOT_LOAD:
+ if ( ! reloc->r_extern() )
+ throw "not extern and X86_64_RELOC_GOT_LOAD not supported";
+ if ( ! reloc->r_pcrel() )
+ throw "not pcrel and X86_64_RELOC_GOT_LOAD not supported";
+ if ( reloc->r_length() != 2 )
+ throw "length != 2 and X86_64_RELOC_GOT_LOAD not supported";
+ addend = (int64_t)((int32_t)(E::get32(*fixUpPtr)));
+ if ( isWeakImportSymbol(targetSymbol) )
+ makeReferenceToSymbol(x86_64::kPCRel32GOTLoadWeakImport, srcAddr, targetSymbol, addend);
+ else
+ makeReferenceToSymbol(x86_64::kPCRel32GOTLoad, srcAddr, targetSymbol, addend);
+ break;
+ case X86_64_RELOC_SUBTRACTOR:
+ if ( reloc->r_pcrel() )
+ throw "X86_64_RELOC_SUBTRACTOR cannot be pc-relative";
+ if ( reloc->r_length() < 2 )
+ throw "X86_64_RELOC_SUBTRACTOR must have r_length of 2 or 3";
+ if ( !reloc->r_extern() )
+ throw "X86_64_RELOC_SUBTRACTOR must have r_extern=1";
+ const macho_relocation_info<x86_64::P>* nextReloc = &reloc[1];
+ if ( nextReloc->r_type() != X86_64_RELOC_UNSIGNED )
+ throw "X86_64_RELOC_SUBTRACTOR must be followed by X86_64_RELOC_UNSIGNED";
+ result = true;
+ if ( nextReloc->r_pcrel() )
+ throw "X86_64_RELOC_UNSIGNED following a X86_64_RELOC_SUBTRACTOR cannot be pc-relative";
+ if ( nextReloc->r_length() != reloc->r_length() )
+ throw "X86_64_RELOC_UNSIGNED following a X86_64_RELOC_SUBTRACTOR must have same r_length";
+ Reference<x86_64>* ref;
+ bool negativeAddend;
+ if ( reloc->r_length() == 2 ) {
+ kind = x86_64::kPointerDiff32;
+ dstAddr = E::get32(*fixUpPtr); // addend is in content
+ negativeAddend = ((dstAddr & 0x80000000) != 0);
+ }
+ else {
+ kind = x86_64::kPointerDiff;
+ dstAddr = E::get64(*((uint64_t*)fixUpPtr)); // addend is in content
+ negativeAddend = ((dstAddr & 0x8000000000000000ULL) != 0);
+ }
+ ObjectFile::Atom* inAtom = this->findAtomAndOffset(srcAddr).atom;
+ // create reference with "to" target
+ if ( nextReloc->r_extern() ) {
+ const macho_nlist<P>* targetSymbol = &fSymbols[nextReloc->r_symbolnum()];
+ const char* targetName = &fStrings[targetSymbol->n_strx()];
+ ref = makeReferenceToSymbol(kind, srcAddr, targetSymbol, 0);
+ // if "to" is in this atom, change by-name to a direct reference
+ if ( strcmp(targetName, inAtom->getName()) == 0 )
+ ref->setTarget(*inAtom, 0);
+ }
+ else {
+ ref = makeReference(kind, srcAddr, dstAddr);
+ }
+ // add in "from" target
+ if ( reloc->r_extern() ) {
+ const macho_nlist<P>* targetFromSymbol = &fSymbols[reloc->r_symbolnum()];
+ const char* fromTargetName = &fStrings[targetFromSymbol->n_strx()];
+ if ( (targetFromSymbol->n_type() & N_EXT) == 0 ) {
+ // from target is translation unit scoped, so use a direct reference
+ ref->setFromTarget(*(findAtomAndOffset(targetSymbol->n_value()).atom));
+ }
+ else if ( strcmp(fromTargetName, inAtom->getName()) == 0 ) {
+ // if "from" is in this atom, change by-name to a direct reference
+ ref->setFromTarget(*inAtom);
+ }
+ else {
+ // some non-static other atom
+ ref->setFromTargetName(fromTargetName);
+ }
+ }
+ // addend goes in from side iff negative
+ if ( negativeAddend )
+ ref->setFromTargetOffset(-dstAddr);
+ else
+ ref->setToTargetOffset(dstAddr);
+ break;
+ default:
+ fprintf(stderr, "unknown relocation type %d\n", reloc->r_type());
+ }
+ return result;
+}