#include <unistd.h>
#include <vector>
+#include <unordered_map>
#include "Options.h"
#include "ld.hpp"
uint32_t currentOffset();
private:
- class CStringEquals
- {
- public:
- bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
- };
enum { kBufferSize = 0x01000000 };
- typedef __gnu_cxx::hash_map<const char*, int32_t, __gnu_cxx::hash<const char*>, CStringEquals> StringToOffset;
+ typedef std::unordered_map<const char*, int32_t, CStringHash, CStringEquals> StringToOffset;
const uint32_t _pointerSize;
std::vector<char*> _fullBuffers;
uint32_t stringOffsetForStab(const ld::relocatable::File::Stab& stab, StringPoolAtom* pool);
uint64_t valueForStab(const ld::relocatable::File::Stab& stab);
uint8_t sectionIndexForStab(const ld::relocatable::File::Stab& stab);
-
+ bool isAltEntry(const ld::Atom* atom);
mutable std::vector<macho_nlist<P> > _globals;
mutable std::vector<macho_nlist<P> > _locals;
int SymbolTableAtom<A>::_s_anonNameIndex = 1;
+template <typename A>
+bool SymbolTableAtom<A>::isAltEntry(const ld::Atom* atom)
+{
+ // alt entries have a group subordinate reference to the previous atom
+ for (ld::Fixup::iterator fit = atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) {
+ if ( fit->kind == ld::Fixup::kindNoneGroupSubordinate ) {
+ if ( fit->binding == Fixup::bindingDirectlyBound ) {
+ const Atom* prevAtom = fit->u.target;
+ assert(prevAtom != NULL);
+ for (ld::Fixup::iterator fit2 = prevAtom->fixupsBegin(); fit2 != prevAtom->fixupsEnd(); ++fit2) {
+ if ( fit2->kind == ld::Fixup::kindNoneFollowOn ) {
+ if ( fit2->binding == Fixup::bindingDirectlyBound ) {
+ if ( fit2->u.target == atom )
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
template <typename A>
bool SymbolTableAtom<A>::addLocal(const ld::Atom* atom, StringPoolAtom* pool)
{
desc |= N_WEAK_DEF;
if ( atom->isThumb() )
desc |= N_ARM_THUMB_DEF;
+ if ( (this->_options.outputKind() == Options::kObjectFile) && this->_state.allObjectFilesScatterable && isAltEntry(atom) )
+ desc |= N_ALT_ENTRY;
entry.set_n_desc(desc);
// set n_value ( address this symbol will be at if this executable is loaded at it preferred address )
desc |= N_SYMBOL_RESOLVER;
if ( atom->dontDeadStrip() && (this->_options.outputKind() == Options::kObjectFile) )
desc |= N_NO_DEAD_STRIP;
+ if ( (this->_options.outputKind() == Options::kObjectFile) && this->_state.allObjectFilesScatterable && isAltEntry(atom) )
+ desc |= N_ALT_ENTRY;
if ( (atom->definition() == ld::Atom::definitionRegular) && (atom->combine() == ld::Atom::combineByName) ) {
desc |= N_WEAK_DEF;
// <rdar://problem/6783167> support auto hidden weak symbols: .weak_def_can_be_hidden
// set n_type
if ( this->_options.outputKind() == Options::kObjectFile ) {
- if ( (atom->scope() == ld::Atom::scopeLinkageUnit)
+ if ( atom->section().type() == ld::Section::typeTempAlias ) {
+ if ( atom->scope() == ld::Atom::scopeLinkageUnit )
+ entry.set_n_type(N_INDR | N_EXT | N_PEXT);
+ else
+ entry.set_n_type(N_INDR | N_EXT);
+ }
+ else if ( (atom->scope() == ld::Atom::scopeLinkageUnit)
&& (atom->definition() == ld::Atom::definitionTentative) )
entry.set_n_type(N_UNDF | N_EXT | N_PEXT);
else
// set n_value, zero for import proxy and size for tentative definition
if ( atom->definition() == ld::Atom::definitionTentative )
entry.set_n_value(atom->size());
- else
+ else if ( atom->section().type() != ld::Section::typeTempAlias )
entry.set_n_value(0);
+ else {
+ assert(atom->fixupsBegin() != atom->fixupsEnd());
+ for (ld::Fixup::iterator fit = atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) {
+ assert(fit->kind == ld::Fixup::kindNoneFollowOn);
+ switch ( fit->binding ) {
+ case ld::Fixup::bindingByNameUnbound:
+ entry.set_n_value(pool->add(fit->u.name));
+ break;
+ case ld::Fixup::bindingsIndirectlyBound:
+ entry.set_n_value(pool->add((_state.indirectBindingTable[fit->u.bindingIndex])->name()));
+ break;
+ default:
+ assert(0 && "internal error: unexpected alias binding");
+ }
+ }
+ }
// add to array
_imports.push_back(entry);
template <typename A>
void SymbolTableAtom<A>::encode()
{
- uint32_t symbolIndex = 0;
+ // Note: We lay out the symbol table so that the strings for the stabs (local) symbols are at the
+ // end of the string pool. The stabs strings are not used when calculated the UUID for the image.
+ // If the stabs strings were not last, the string offsets for all other symbols may very which would alter the UUID.
- // make nlist entries for all local symbols
- std::vector<const ld::Atom*>& localAtoms = this->_writer._localAtoms;
- std::vector<const ld::Atom*>& globalAtoms = this->_writer._exportedAtoms;
- _locals.reserve(localAtoms.size()+this->_state.stabs.size());
- this->_writer._localSymbolsStartIndex = 0;
- // make nlist entries for all debug notes
- _stabsIndexStart = symbolIndex;
- _stabsStringsOffsetStart = this->_writer._stringPoolAtom->currentOffset();
- for (std::vector<ld::relocatable::File::Stab>::const_iterator sit=this->_state.stabs.begin(); sit != this->_state.stabs.end(); ++sit) {
- macho_nlist<P> entry;
- entry.set_n_type(sit->type);
- entry.set_n_sect(sectionIndexForStab(*sit));
- entry.set_n_desc(sit->desc);
- entry.set_n_value(valueForStab(*sit));
- entry.set_n_strx(stringOffsetForStab(*sit, this->_writer._stringPoolAtom));
- _locals.push_back(entry);
- ++symbolIndex;
- }
- _stabsIndexEnd = symbolIndex;
- _stabsStringsOffsetEnd = this->_writer._stringPoolAtom->currentOffset();
- for (std::vector<const ld::Atom*>::const_iterator it=localAtoms.begin(); it != localAtoms.end(); ++it) {
- const ld::Atom* atom = *it;
- if ( this->addLocal(atom, this->_writer._stringPoolAtom) )
- this->_writer._atomToSymbolIndex[atom] = symbolIndex++;
- }
- this->_writer._localSymbolsCount = symbolIndex;
-
+ // reserve space for local symbols
+ uint32_t localsCount = _state.stabs.size() + this->_writer._localAtoms.size();
// make nlist entries for all global symbols
+ std::vector<const ld::Atom*>& globalAtoms = this->_writer._exportedAtoms;
_globals.reserve(globalAtoms.size());
- this->_writer._globalSymbolsStartIndex = symbolIndex;
+ uint32_t symbolIndex = localsCount;
+ this->_writer._globalSymbolsStartIndex = localsCount;
for (std::vector<const ld::Atom*>::const_iterator it=globalAtoms.begin(); it != globalAtoms.end(); ++it) {
const ld::Atom* atom = *it;
this->addGlobal(atom, this->_writer._stringPoolAtom);
this->_writer._atomToSymbolIndex[*it] = symbolIndex++;
}
this->_writer._importSymbolsCount = symbolIndex - this->_writer._importSymbolsStartIndex;
+
+ // go back to start and make nlist entries for all local symbols
+ std::vector<const ld::Atom*>& localAtoms = this->_writer._localAtoms;
+ _locals.reserve(localsCount);
+ symbolIndex = 0;
+ this->_writer._localSymbolsStartIndex = 0;
+ _stabsIndexStart = 0;
+ _stabsStringsOffsetStart = this->_writer._stringPoolAtom->currentOffset();
+ for (const ld::relocatable::File::Stab& stab : _state.stabs) {
+ macho_nlist<P> entry;
+ entry.set_n_type(stab.type);
+ entry.set_n_sect(sectionIndexForStab(stab));
+ entry.set_n_desc(stab.desc);
+ entry.set_n_value(valueForStab(stab));
+ entry.set_n_strx(stringOffsetForStab(stab, this->_writer._stringPoolAtom));
+ _locals.push_back(entry);
+ ++symbolIndex;
+ }
+ _stabsIndexEnd = symbolIndex;
+ _stabsStringsOffsetEnd = this->_writer._stringPoolAtom->currentOffset();
+ for (const ld::Atom* atom : localAtoms) {
+ if ( this->addLocal(atom, this->_writer._stringPoolAtom) )
+ this->_writer._atomToSymbolIndex[atom] = symbolIndex++;
+ }
+ this->_writer._localSymbolsCount = symbolIndex;
}
template <typename A>
// for kext bundles the reloc base address starts at __TEXT segment
return _options.baseAddress();
}
- // for all other kinds, the x86_64 reloc base address starts at __DATA segment
+ // for all other kinds, the x86_64 reloc base address starts at first writable segment (usually __DATA)
for (std::vector<ld::Internal::FinalSection*>::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) {
ld::Internal::FinalSection* sect = *sit;
- if ( strcmp(sect->segmentName(), "__DATA") == 0 )
+ if ( !sect->isSectionHidden() && _options.initialSegProtection(sect->segmentName()) & VM_PROT_WRITE )
return sect->address;
}
- throw "__DATA segment not found";
+ throw "writable (__DATA) segment not found";
}
template <typename A>
// for x86_64 the reloc base address starts at __DATA segment
for (std::vector<ld::Internal::FinalSection*>::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) {
ld::Internal::FinalSection* sect = *sit;
- if ( strcmp(sect->segmentName(), "__DATA") == 0 )
+ if ( !sect->isSectionHidden() && _options.initialSegProtection(sect->segmentName()) & VM_PROT_WRITE )
return sect->address;
}
- throw "__DATA segment not found";
+ throw "writable (__DATA) segment not found";
}
template <typename A>
return (_pointerLocations.size() + _callSiteLocations.size()) * sizeof(macho_relocation_info<P>);
}
+#if SUPPORT_ARCH_arm64
+template <> uint32_t ExternalRelocationsAtom<arm64>::pointerReloc() { return ARM64_RELOC_UNSIGNED; }
+#endif
#if SUPPORT_ARCH_arm_any
template <> uint32_t ExternalRelocationsAtom<arm>::pointerReloc() { return ARM_RELOC_VANILLA; }
#endif
template <> uint32_t ExternalRelocationsAtom<x86_64>::callReloc() { return X86_64_RELOC_BRANCH; }
template <> uint32_t ExternalRelocationsAtom<x86>::callReloc() { return GENERIC_RELOC_VANILLA; }
+#if SUPPORT_ARCH_arm64
+template <> uint32_t ExternalRelocationsAtom<arm64>::callReloc() { return ARM64_RELOC_BRANCH26; }
+#endif
+
template <typename A>
uint32_t ExternalRelocationsAtom<A>::callReloc()
{
{
int len = 0;
uint32_t otherHalf = 0;
- uint32_t value = entry.toTarget->finalAddress()+entry.toAddend;
- if ( entry.fromTarget != NULL )
- value -= (entry.fromTarget->finalAddress()+entry.fromAddend);
+ uint32_t value;
+ if ( entry.fromTarget != NULL ) {
+ // this is a sect-diff
+ value = (entry.toTarget->finalAddress()+entry.toAddend) - (entry.fromTarget->finalAddress()+entry.fromAddend);
+ }
+ else {
+ // this is an absolute address
+ value = entry.toAddend;
+ if ( !external )
+ value += entry.toTarget->finalAddress();
+ }
switch ( entry.kind ) {
case ld::Fixup::kindStoreARMLow16:
len = 0;
}
#endif
+#if SUPPORT_ARCH_arm64
+template <>
+void SectionRelocationsAtom<arm64>::encodeSectionReloc(ld::Internal::FinalSection* sect,
+ const Entry& entry, std::vector<macho_relocation_info<P> >& relocs)
+{
+ macho_relocation_info<P> reloc1;
+ macho_relocation_info<P> reloc2;
+ uint64_t address = entry.inAtom->finalAddress()+entry.offsetInAtom - sect->address;
+ bool external = entry.toTargetUsesExternalReloc;
+ uint32_t symbolNum = sectSymNum(external, entry.toTarget);
+ bool fromExternal = false;
+ uint32_t fromSymbolNum = 0;
+ if ( entry.fromTarget != NULL ) {
+ fromExternal = entry.fromTargetUsesExternalReloc;
+ fromSymbolNum = sectSymNum(fromExternal, entry.fromTarget);
+ }
+
+
+ switch ( entry.kind ) {
+ case ld::Fixup::kindStoreARM64Branch26:
+ if ( entry.toAddend != 0 ) {
+ assert(entry.toAddend < 0x400000);
+ reloc2.set_r_address(address);
+ reloc2.set_r_symbolnum(entry.toAddend);
+ reloc2.set_r_pcrel(false);
+ reloc2.set_r_length(2);
+ reloc2.set_r_extern(false);
+ reloc2.set_r_type(ARM64_RELOC_ADDEND);
+ relocs.push_back(reloc2);
+ }
+ // fall into next case
+ case ld::Fixup::kindStoreTargetAddressARM64Branch26:
+ case ld::Fixup::kindStoreARM64DtraceCallSiteNop:
+ case ld::Fixup::kindStoreARM64DtraceIsEnableSiteClear:
+ reloc1.set_r_address(address);
+ reloc1.set_r_symbolnum(symbolNum);
+ reloc1.set_r_pcrel(true);
+ reloc1.set_r_length(2);
+ reloc1.set_r_extern(external);
+ reloc1.set_r_type(ARM64_RELOC_BRANCH26);
+ relocs.push_back(reloc1);
+ break;
+
+ case ld::Fixup::kindStoreARM64Page21:
+ if ( entry.toAddend != 0 ) {
+ assert(entry.toAddend < 0x400000);
+ reloc2.set_r_address(address);
+ reloc2.set_r_symbolnum(entry.toAddend);
+ reloc2.set_r_pcrel(false);
+ reloc2.set_r_length(2);
+ reloc2.set_r_extern(false);
+ reloc2.set_r_type(ARM64_RELOC_ADDEND);
+ relocs.push_back(reloc2);
+ }
+ // fall into next case
+ case ld::Fixup::kindStoreTargetAddressARM64Page21:
+ reloc1.set_r_address(address);
+ reloc1.set_r_symbolnum(symbolNum);
+ reloc1.set_r_pcrel(true);
+ reloc1.set_r_length(2);
+ reloc1.set_r_extern(external);
+ reloc1.set_r_type(ARM64_RELOC_PAGE21);
+ relocs.push_back(reloc1);
+ break;
+
+ case ld::Fixup::kindStoreARM64PageOff12:
+ if ( entry.toAddend != 0 ) {
+ assert(entry.toAddend < 0x400000);
+ reloc2.set_r_address(address);
+ reloc2.set_r_symbolnum(entry.toAddend);
+ reloc2.set_r_pcrel(false);
+ reloc2.set_r_length(2);
+ reloc2.set_r_extern(false);
+ reloc2.set_r_type(ARM64_RELOC_ADDEND);
+ relocs.push_back(reloc2);
+ }
+ // fall into next case
+ case ld::Fixup::kindStoreTargetAddressARM64PageOff12:
+ reloc1.set_r_address(address);
+ reloc1.set_r_symbolnum(symbolNum);
+ reloc1.set_r_pcrel(false);
+ reloc1.set_r_length(2);
+ reloc1.set_r_extern(external);
+ reloc1.set_r_type(ARM64_RELOC_PAGEOFF12);
+ relocs.push_back(reloc1);
+ break;
+
+ case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21:
+ case ld::Fixup::kindStoreARM64GOTLoadPage21:
+ reloc1.set_r_address(address);
+ reloc1.set_r_symbolnum(symbolNum);
+ reloc1.set_r_pcrel(true);
+ reloc1.set_r_length(2);
+ reloc1.set_r_extern(external);
+ reloc1.set_r_type(ARM64_RELOC_GOT_LOAD_PAGE21);
+ relocs.push_back(reloc1);
+ break;
+
+ case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12:
+ case ld::Fixup::kindStoreARM64GOTLoadPageOff12:
+ reloc1.set_r_address(address);
+ reloc1.set_r_symbolnum(symbolNum);
+ reloc1.set_r_pcrel(false);
+ reloc1.set_r_length(2);
+ reloc1.set_r_extern(external);
+ reloc1.set_r_type(ARM64_RELOC_GOT_LOAD_PAGEOFF12);
+ relocs.push_back(reloc1);
+ break;
+
+ case ld::Fixup::kindStoreARM64TLVPLoadPageOff12:
+ case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPageOff12:
+ reloc1.set_r_address(address);
+ reloc1.set_r_symbolnum(symbolNum);
+ reloc1.set_r_pcrel(false);
+ reloc1.set_r_length(2);
+ reloc1.set_r_extern(external);
+ reloc1.set_r_type(ARM64_RELOC_TLVP_LOAD_PAGEOFF12);
+ relocs.push_back(reloc1);
+ break;
+
+ case ld::Fixup::kindStoreARM64TLVPLoadPage21:
+ case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21:
+ reloc1.set_r_address(address);
+ reloc1.set_r_symbolnum(symbolNum);
+ reloc1.set_r_pcrel(true);
+ reloc1.set_r_length(2);
+ reloc1.set_r_extern(external);
+ reloc1.set_r_type(ARM64_RELOC_TLVP_LOAD_PAGE21);
+ relocs.push_back(reloc1);
+ break;
+
+ case ld::Fixup::kindStoreLittleEndian64:
+ case ld::Fixup::kindStoreTargetAddressLittleEndian64:
+ if ( entry.fromTarget != NULL ) {
+ // this is a pointer-diff
+ reloc1.set_r_address(address);
+ reloc1.set_r_symbolnum(symbolNum);
+ reloc1.set_r_pcrel(false);
+ reloc1.set_r_length(3);
+ reloc1.set_r_extern(external);
+ reloc1.set_r_type(ARM64_RELOC_UNSIGNED);
+ reloc2.set_r_address(address);
+ reloc2.set_r_symbolnum(fromSymbolNum);
+ reloc2.set_r_pcrel(false);
+ reloc2.set_r_length(3);
+ reloc2.set_r_extern(fromExternal);
+ reloc2.set_r_type(ARM64_RELOC_SUBTRACTOR);
+ relocs.push_back(reloc2);
+ relocs.push_back(reloc1);
+ }
+ else {
+ // regular pointer
+ reloc1.set_r_address(address);
+ reloc1.set_r_symbolnum(symbolNum);
+ reloc1.set_r_pcrel(false);
+ reloc1.set_r_length(3);
+ reloc1.set_r_extern(external);
+ reloc1.set_r_type(ARM64_RELOC_UNSIGNED);
+ relocs.push_back(reloc1);
+ }
+ break;
+
+ case ld::Fixup::kindStoreLittleEndian32:
+ case ld::Fixup::kindStoreTargetAddressLittleEndian32:
+ if ( entry.fromTarget != NULL ) {
+ // this is a pointer-diff
+ reloc1.set_r_address(address);
+ reloc1.set_r_symbolnum(symbolNum);
+ reloc1.set_r_pcrel(false);
+ reloc1.set_r_length(2);
+ reloc1.set_r_extern(external);
+ reloc1.set_r_type(ARM64_RELOC_UNSIGNED);
+ reloc2.set_r_address(address);
+ reloc2.set_r_symbolnum(fromSymbolNum);
+ reloc2.set_r_pcrel(false);
+ reloc2.set_r_length(2);
+ reloc2.set_r_extern(fromExternal);
+ reloc2.set_r_type(ARM64_RELOC_SUBTRACTOR);
+ relocs.push_back(reloc2);
+ relocs.push_back(reloc1);
+ }
+ else {
+ // regular pointer
+ reloc1.set_r_address(address);
+ reloc1.set_r_symbolnum(symbolNum);
+ reloc1.set_r_pcrel(false);
+ reloc1.set_r_length(2);
+ reloc1.set_r_extern(external);
+ reloc1.set_r_type(ARM64_RELOC_UNSIGNED);
+ relocs.push_back(reloc1);
+ }
+ break;
+
+ case ld::Fixup::kindStoreARM64PointerToGOT:
+ reloc1.set_r_address(address);
+ reloc1.set_r_symbolnum(symbolNum);
+ reloc1.set_r_pcrel(false);
+ reloc1.set_r_length(3);
+ reloc1.set_r_extern(external);
+ reloc1.set_r_type(ARM64_RELOC_POINTER_TO_GOT);
+ relocs.push_back(reloc1);
+ break;
+
+ case ld::Fixup::kindStoreARM64PCRelToGOT:
+ reloc1.set_r_address(address);
+ reloc1.set_r_symbolnum(symbolNum);
+ reloc1.set_r_pcrel(true);
+ reloc1.set_r_length(2);
+ reloc1.set_r_extern(external);
+ reloc1.set_r_type(ARM64_RELOC_POINTER_TO_GOT);
+ relocs.push_back(reloc1);
+ break;
+
+ default:
+ assert(0 && "need to handle arm64 -r reloc");
+
+ }
+
+}
+#endif // SUPPORT_ARCH_arm64
template <typename A>
uint32_t symIndexOfLazyPointerAtom(const ld::Atom*);
uint32_t symIndexOfNonLazyPointerAtom(const ld::Atom*);
uint32_t symbolIndex(const ld::Atom*);
- bool kextBundlesDontHaveIndirectSymbolTable();
std::vector<uint32_t> _entries;
{
for (ld::Fixup::iterator fit = stubAtom->fixupsBegin(); fit != stubAtom->fixupsEnd(); ++fit) {
if ( fit->binding == ld::Fixup::bindingDirectlyBound ) {
- assert((fit->u.target->contentType() == ld::Atom::typeLazyPointer)
- || (fit->u.target->contentType() == ld::Atom::typeLazyDylibPointer));
- return symIndexOfLazyPointerAtom(fit->u.target);
+ ld::Atom::ContentType type = fit->u.target->contentType();
+ if (( type == ld::Atom::typeLazyPointer) || (type == ld::Atom::typeLazyDylibPointer) )
+ return symIndexOfLazyPointerAtom(fit->u.target);
+ if ( type == ld::Atom::typeNonLazyPointer )
+ return symIndexOfNonLazyPointerAtom(fit->u.target);
}
}
throw "internal error: stub missing fixup to lazy pointer";
}
}
-template <typename A>
-bool IndirectSymbolTableAtom<A>::kextBundlesDontHaveIndirectSymbolTable()
-{
- return true;
-}
-
template <typename A>
void IndirectSymbolTableAtom<A>::encode()
{
if ( (this->_options.outputKind() == Options::kStaticExecutable) && !_options.positionIndependentExecutable() )
return;
- // x86_64 kext bundles should not have an indirect symbol table
- if ( (this->_options.outputKind() == Options::kKextBundle) && kextBundlesDontHaveIndirectSymbolTable() )
+ // x86_64 kext bundles should not have an indirect symbol table unless using stubs
+ if ( (this->_options.outputKind() == Options::kKextBundle) && !this->_options.kextsUseStubs() )
return;
// slidable static executables (-static -pie) should not have an indirect symbol table