--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-*
+ *
+ * Copyright (c) 2009-2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifndef __LINKEDIT_CLASSIC_HPP__
+#define __LINKEDIT_CLASSIC_HPP__
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <limits.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include "Options.h"
+#include "ld.hpp"
+#include "Architectures.hpp"
+#include "MachOFileAbstraction.hpp"
+
+namespace ld {
+namespace tool {
+
+
+
+class ClassicLinkEditAtom : public ld::Atom
+{
+public:
+
+ // overrides of ld::Atom
+ virtual ld::File* file() const { return NULL; }
+ virtual bool translationUnitSource(const char** dir, const char** nm) const
+ { return false; }
+ virtual uint64_t objectAddress() const { return 0; }
+
+ virtual void encode() = 0;
+ virtual bool hasStabs(uint32_t& ssos, uint32_t& ssoe, uint32_t& sos, uint32_t& soe) { return false; }
+
+ ClassicLinkEditAtom(const Options& opts, ld::Internal& state,
+ OutputFile& writer, const ld::Section& sect,
+ unsigned int pointerSize)
+ : ld::Atom(sect, ld::Atom::definitionRegular,
+ ld::Atom::combineNever, ld::Atom::scopeTranslationUnit,
+ ld::Atom::typeUnclassified, ld::Atom::symbolTableNotIn,
+ false, false, false, ld::Atom::Alignment(log2(pointerSize))),
+ _options(opts), _state(state), _writer(writer) { }
+protected:
+ const Options& _options;
+ ld::Internal& _state;
+ OutputFile& _writer;
+};
+
+
+
+class StringPoolAtom : public ClassicLinkEditAtom
+{
+public:
+ StringPoolAtom(const Options& opts, ld::Internal& state,
+ OutputFile& writer, int pointerSize);
+
+ // overrides of ld::Atom
+ virtual const char* name() const { return "string pool"; }
+ virtual uint64_t size() const;
+ virtual void copyRawContent(uint8_t buffer[]) const;
+ // overrides of ClassicLinkEditAtom
+ virtual void encode() { }
+
+ int32_t add(const char* name);
+ int32_t addUnique(const char* name);
+ int32_t emptyString() { return 1; }
+ const char* stringForIndex(int32_t) const;
+ 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;
+
+ const uint32_t _pointerSize;
+ std::vector<char*> _fullBuffers;
+ char* _currentBuffer;
+ uint32_t _currentBufferUsed;
+ StringToOffset _uniqueStrings;
+
+ static ld::Section _s_section;
+};
+
+ld::Section StringPoolAtom::_s_section("__LINKEDIT", "__string_pool", ld::Section::typeLinkEdit, true);
+
+
+StringPoolAtom::StringPoolAtom(const Options& opts, ld::Internal& state, OutputFile& writer, int pointerSize)
+ : ClassicLinkEditAtom(opts, state, writer, _s_section, pointerSize),
+ _pointerSize(pointerSize), _currentBuffer(NULL), _currentBufferUsed(0)
+{
+ _currentBuffer = new char[kBufferSize];
+ // burn first byte of string pool (so zero is never a valid string offset)
+ _currentBuffer[_currentBufferUsed++] = ' ';
+ // make offset 1 always point to an empty string
+ _currentBuffer[_currentBufferUsed++] = '\0';
+}
+
+uint64_t StringPoolAtom::size() const
+{
+ // pointer size align size
+ return (kBufferSize * _fullBuffers.size() + _currentBufferUsed + _pointerSize-1) & (-_pointerSize);
+}
+
+void StringPoolAtom::copyRawContent(uint8_t buffer[]) const
+{
+ uint64_t offset = 0;
+ for (unsigned int i=0; i < _fullBuffers.size(); ++i) {
+ memcpy(&buffer[offset], _fullBuffers[i], kBufferSize);
+ offset += kBufferSize;
+ }
+ memcpy(&buffer[offset], _currentBuffer, _currentBufferUsed);
+ // zero fill end to align
+ offset += _currentBufferUsed;
+ while ( (offset % _pointerSize) != 0 )
+ buffer[offset++] = 0;
+}
+
+int32_t StringPoolAtom::add(const char* str)
+{
+ int32_t offset = kBufferSize * _fullBuffers.size() + _currentBufferUsed;
+ int lenNeeded = strlcpy(&_currentBuffer[_currentBufferUsed], str, kBufferSize-_currentBufferUsed)+1;
+ if ( (_currentBufferUsed+lenNeeded) < kBufferSize ) {
+ _currentBufferUsed += lenNeeded;
+ }
+ else {
+ int copied = kBufferSize-_currentBufferUsed-1;
+ // change trailing '\0' that strlcpy added to real char
+ _currentBuffer[kBufferSize-1] = str[copied];
+ // alloc next buffer
+ _fullBuffers.push_back(_currentBuffer);
+ _currentBuffer = new char[kBufferSize];
+ _currentBufferUsed = 0;
+ // append rest of string
+ this->add(&str[copied+1]);
+ }
+ return offset;
+}
+
+uint32_t StringPoolAtom::currentOffset()
+{
+ return kBufferSize * _fullBuffers.size() + _currentBufferUsed;
+}
+
+
+int32_t StringPoolAtom::addUnique(const char* str)
+{
+ StringToOffset::iterator pos = _uniqueStrings.find(str);
+ if ( pos != _uniqueStrings.end() ) {
+ return pos->second;
+ }
+ else {
+ int32_t offset = this->add(str);
+ _uniqueStrings[str] = offset;
+ return offset;
+ }
+}
+
+
+const char* StringPoolAtom::stringForIndex(int32_t index) const
+{
+ int32_t currentBufferStartIndex = kBufferSize * _fullBuffers.size();
+ int32_t maxIndex = currentBufferStartIndex + _currentBufferUsed;
+ // check for out of bounds
+ if ( index > maxIndex )
+ return "";
+ // check for index in _currentBuffer
+ if ( index > currentBufferStartIndex )
+ return &_currentBuffer[index-currentBufferStartIndex];
+ // otherwise index is in a full buffer
+ uint32_t fullBufferIndex = index/kBufferSize;
+ return &_fullBuffers[fullBufferIndex][index-(kBufferSize*fullBufferIndex)];
+}
+
+
+
+template <typename A>
+class SymbolTableAtom : public ClassicLinkEditAtom
+{
+public:
+ SymbolTableAtom(const Options& opts, ld::Internal& state, OutputFile& writer)
+ : ClassicLinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)),
+ _stabsStringsOffsetStart(0), _stabsStringsOffsetEnd(0),
+ _stabsIndexStart(0), _stabsIndexEnd(0) { }
+
+ // overrides of ld::Atom
+ virtual const char* name() const { return "symbol table"; }
+ virtual uint64_t size() const;
+ virtual void copyRawContent(uint8_t buffer[]) const;
+ // overrides of ClassicLinkEditAtom
+ virtual void encode();
+ virtual bool hasStabs(uint32_t& ssos, uint32_t& ssoe, uint32_t& sos, uint32_t& soe);
+
+private:
+ typedef typename A::P P;
+ typedef typename A::P::E E;
+ typedef typename A::P::uint_t pint_t;
+
+ bool addLocal(const ld::Atom* atom, StringPoolAtom* pool);
+ void addGlobal(const ld::Atom* atom, StringPoolAtom* pool);
+ void addImport(const ld::Atom* atom, StringPoolAtom* pool);
+ uint8_t classicOrdinalForProxy(const ld::Atom* atom);
+ 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);
+
+
+ mutable std::vector<macho_nlist<P> > _globals;
+ mutable std::vector<macho_nlist<P> > _locals;
+ mutable std::vector<macho_nlist<P> > _imports;
+
+ uint32_t _stabsStringsOffsetStart;
+ uint32_t _stabsStringsOffsetEnd;
+ uint32_t _stabsIndexStart;
+ uint32_t _stabsIndexEnd;
+
+ static ld::Section _s_section;
+};
+
+template <typename A>
+ld::Section SymbolTableAtom<A>::_s_section("__LINKEDIT", "__symbol_table", ld::Section::typeLinkEdit, true);
+
+
+
+template <typename A>
+bool SymbolTableAtom<A>::addLocal(const ld::Atom* atom, StringPoolAtom* pool)
+{
+ macho_nlist<P> entry;
+ static int s_anonNameIndex = 1;
+ assert(atom->symbolTableInclusion() != ld::Atom::symbolTableNotIn);
+
+ // set n_strx
+ const char* symbolName = atom->name();
+ char anonName[32];
+ if ( this->_options.outputKind() == Options::kObjectFile ) {
+ if ( atom->contentType() == ld::Atom::typeCString ) {
+ if ( atom->combine() == ld::Atom::combineByNameAndContent ) {
+ // don't use 'l' labels for x86_64 strings
+ // <rdar://problem/6605499> x86_64 obj-c runtime confused when static lib is stripped
+ sprintf(anonName, "LC%u", s_anonNameIndex++);
+ symbolName = anonName;
+ }
+ }
+ else if ( atom->contentType() == ld::Atom::typeCFI ) {
+ if ( _options.removeEHLabels() )
+ return false;
+ // synthesize .eh name
+ if ( strcmp(atom->name(), "CIE") == 0 )
+ symbolName = "EH_Frame1";
+ else
+ symbolName = "func.eh";
+ }
+ else if ( atom->symbolTableInclusion() == ld::Atom::symbolTableInWithRandomAutoStripLabel ) {
+ // make auto-strip anonymous name for symbol
+ sprintf(anonName, "l%03u", s_anonNameIndex++);
+ symbolName = anonName;
+ }
+ }
+ entry.set_n_strx(pool->add(symbolName));
+
+ // set n_type
+ uint8_t type = N_SECT;
+ if ( atom->definition() == ld::Atom::definitionAbsolute ) {
+ type = N_ABS;
+ }
+ else if ( (atom->section().type() == ld::Section::typeObjC1Classes)
+ && (this->_options.outputKind() == Options::kObjectFile) ) {
+ // __OBJC __class has floating abs symbols for each class data structure
+ type = N_ABS;
+ }
+ if ( atom->scope() == ld::Atom::scopeLinkageUnit )
+ type |= N_PEXT;
+ entry.set_n_type(type);
+
+ // set n_sect (section number of implementation )
+ if ( atom->definition() == ld::Atom::definitionAbsolute )
+ entry.set_n_sect(0);
+ else
+ entry.set_n_sect(atom->machoSection());
+
+ // set n_desc
+ uint16_t desc = 0;
+ if ( atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip )
+ desc |= REFERENCED_DYNAMICALLY;
+ if ( atom->dontDeadStrip() && (this->_options.outputKind() == Options::kObjectFile) )
+ desc |= N_NO_DEAD_STRIP;
+ if ( (atom->definition() == ld::Atom::definitionRegular) && (atom->combine() == ld::Atom::combineByName) )
+ desc |= N_WEAK_DEF;
+ if ( atom->isThumb() )
+ desc |= N_ARM_THUMB_DEF;
+ entry.set_n_desc(desc);
+
+ // set n_value ( address this symbol will be at if this executable is loaded at it preferred address )
+ if ( atom->definition() == ld::Atom::definitionAbsolute )
+ entry.set_n_value(atom->objectAddress());
+ else
+ entry.set_n_value(atom->finalAddress());
+
+ // add to array
+ _locals.push_back(entry);
+ return true;
+}
+
+
+template <typename A>
+void SymbolTableAtom<A>::addGlobal(const ld::Atom* atom, StringPoolAtom* pool)
+{
+ macho_nlist<P> entry;
+
+ // set n_strx
+ entry.set_n_strx(pool->add(atom->name()));
+
+ // set n_type
+ if ( atom->definition() == ld::Atom::definitionAbsolute ) {
+ entry.set_n_type(N_EXT | N_ABS);
+ }
+ else if ( (atom->section().type() == ld::Section::typeObjC1Classes)
+ && (this->_options.outputKind() == Options::kObjectFile) ) {
+ // __OBJC __class has floating abs symbols for each class data structure
+ entry.set_n_type(N_EXT | N_ABS);
+ }
+ else if ( (atom->definition() == ld::Atom::definitionProxy) && (atom->scope() == ld::Atom::scopeGlobal) ) {
+ entry.set_n_type(N_EXT | N_INDR);
+ }
+ else {
+ entry.set_n_type(N_EXT | N_SECT);
+ if ( (atom->scope() == ld::Atom::scopeLinkageUnit) && (this->_options.outputKind() == Options::kObjectFile) ) {
+ if ( this->_options.keepPrivateExterns() )
+ entry.set_n_type(N_EXT | N_SECT | N_PEXT);
+ }
+ else if ( (atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip)
+ && (atom->section().type() == ld::Section::typeMachHeader) ) {
+ // the __mh_execute_header is historical magic and must be an absolute symbol
+ entry.set_n_type(N_EXT | N_ABS);
+ }
+ }
+
+ // set n_sect (section number of implementation)
+ if ( atom->definition() == ld::Atom::definitionAbsolute )
+ entry.set_n_sect(0);
+ else if ( (atom->definition() == ld::Atom::definitionProxy) && (atom->scope() == ld::Atom::scopeGlobal) )
+ entry.set_n_sect(0);
+ else
+ entry.set_n_sect(atom->machoSection());
+
+ // set n_desc
+ uint16_t desc = 0;
+ if ( atom->isThumb() )
+ desc |= N_ARM_THUMB_DEF;
+ if ( atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip )
+ desc |= REFERENCED_DYNAMICALLY;
+ if ( (atom->contentType() == ld::Atom::typeResolver) && (this->_options.outputKind() == Options::kObjectFile) )
+ desc |= N_SYMBOL_RESOLVER;
+ if ( atom->dontDeadStrip() && (this->_options.outputKind() == Options::kObjectFile) )
+ desc |= N_NO_DEAD_STRIP;
+ 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
+ if ( (atom->scope() == ld::Atom::scopeGlobal) && atom->autoHide() && (this->_options.outputKind() == Options::kObjectFile) )
+ desc |= N_WEAK_REF;
+ }
+ entry.set_n_desc(desc);
+
+ // set n_value ( address this symbol will be at if this executable is loaded at it preferred address )
+ if ( atom->definition() == ld::Atom::definitionAbsolute )
+ entry.set_n_value(atom->objectAddress());
+ else if ( (atom->definition() == ld::Atom::definitionProxy) && (atom->scope() == ld::Atom::scopeGlobal) ) {
+ if ( atom->isAlias() ) {
+ // this re-export also renames
+ for (ld::Fixup::iterator fit = atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) {
+ if ( fit->kind == ld::Fixup::kindNoneFollowOn ) {
+ assert(fit->binding == ld::Fixup::bindingDirectlyBound);
+ entry.set_n_value(pool->add(fit->u.target->name()));
+ }
+ }
+ }
+ else
+ entry.set_n_value(entry.n_strx());
+ }
+ else
+ entry.set_n_value(atom->finalAddress());
+
+ // add to array
+ _globals.push_back(entry);
+}
+
+template <typename A>
+uint8_t SymbolTableAtom<A>::classicOrdinalForProxy(const ld::Atom* atom)
+{
+ assert(atom->definition() == ld::Atom::definitionProxy);
+ // when linking for flat-namespace ordinals are always zero
+ if ( _options.nameSpace() != Options::kTwoLevelNameSpace )
+ return 0;
+ const ld::dylib::File* dylib = dynamic_cast<const ld::dylib::File*>(atom->file());
+ // when linking -undefined dynamic_lookup, unbound symbols use DYNAMIC_LOOKUP_ORDINAL
+ if ( dylib == NULL ) {
+ if (_options.undefinedTreatment() == Options::kUndefinedDynamicLookup )
+ return DYNAMIC_LOOKUP_ORDINAL;
+ if (_options.allowedUndefined(atom->name()) )
+ return DYNAMIC_LOOKUP_ORDINAL;
+ }
+ assert(dylib != NULL);
+ int ord = this->_writer.dylibToOrdinal(dylib);
+ if ( ord == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE )
+ return EXECUTABLE_ORDINAL;
+ return ord;
+}
+
+
+template <typename A>
+void SymbolTableAtom<A>::addImport(const ld::Atom* atom, StringPoolAtom* pool)
+{
+ macho_nlist<P> entry;
+
+ // set n_strx
+ entry.set_n_strx(pool->add(atom->name()));
+
+ // set n_type
+ if ( this->_options.outputKind() == Options::kObjectFile ) {
+ if ( (atom->scope() == ld::Atom::scopeLinkageUnit)
+ && (atom->definition() == ld::Atom::definitionTentative) )
+ entry.set_n_type(N_UNDF | N_EXT | N_PEXT);
+ else
+ entry.set_n_type(N_UNDF | N_EXT);
+ }
+ else {
+ if ( this->_options.prebind() )
+ entry.set_n_type(N_PBUD | N_EXT);
+ else
+ entry.set_n_type(N_UNDF | N_EXT);
+ }
+
+ // set n_sect
+ entry.set_n_sect(0);
+
+ uint16_t desc = 0;
+ if ( this->_options.outputKind() != Options::kObjectFile ) {
+ uint8_t ordinal = this->classicOrdinalForProxy(atom);
+ //fprintf(stderr, "ordinal=%u from reader=%p for symbol=%s\n", ordinal, atom->getFile(), atom->getName());
+ SET_LIBRARY_ORDINAL(desc, ordinal);
+
+#if 0
+ // set n_desc ( high byte is library ordinal, low byte is reference type )
+ std::map<const ObjectFile::Atom*,ObjectFile::Atom*>::iterator pos = fStubsMap.find(atom);
+ if ( pos != fStubsMap.end() || ( strncmp(atom->getName(), ".objc_class_name_", 17) == 0) )
+ desc |= REFERENCE_FLAG_UNDEFINED_LAZY;
+ else
+ desc |= REFERENCE_FLAG_UNDEFINED_NON_LAZY;
+#endif
+ }
+ else if ( atom->definition() == ld::Atom::definitionTentative ) {
+ uint8_t align = atom->alignment().powerOf2;
+ // always record custom alignment of common symbols to match what compiler does
+ SET_COMM_ALIGN(desc, align);
+ }
+ if ( (this->_options.outputKind() != Options::kObjectFile)
+ && (atom->definition() == ld::Atom::definitionProxy)
+ && (atom->combine() == ld::Atom::combineByName) ) {
+ desc |= N_REF_TO_WEAK;
+ }
+ if ( atom->weakImported() )
+ desc |= N_WEAK_REF;
+ entry.set_n_desc(desc);
+
+ // 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
+ entry.set_n_value(0);
+
+ // add to array
+ _imports.push_back(entry);
+}
+
+template <typename A>
+uint8_t SymbolTableAtom<A>::sectionIndexForStab(const ld::relocatable::File::Stab& stab)
+{
+ // in FUN stabs, n_sect field is 0 for start FUN and 1 for end FUN
+ if ( stab.type == N_FUN )
+ return stab.other;
+ else if ( stab.type == N_GSYM )
+ return 0;
+ else if ( stab.atom != NULL )
+ return stab.atom->machoSection();
+ else
+ return stab.other;
+}
+
+
+template <typename A>
+uint64_t SymbolTableAtom<A>::valueForStab(const ld::relocatable::File::Stab& stab)
+{
+ switch ( stab.type ) {
+ case N_FUN:
+ if ( stab.atom == NULL ) {
+ // <rdar://problem/5591394> Add support to ld64 for N_FUN stabs when used for symbolic constants
+ return stab.value;
+ }
+ if ( (stab.string == NULL) || (strlen(stab.string) == 0) ) {
+ // end of function N_FUN has size
+ return stab.atom->size();
+ }
+ else {
+ // start of function N_FUN has address
+ return stab.atom->finalAddress();
+ }
+ case N_LBRAC:
+ case N_RBRAC:
+ case N_SLINE:
+ if ( stab.atom == NULL )
+ // some weird assembly files have slines not associated with a function
+ return stab.value;
+ else
+ // all these stab types need their value changed from an offset in the atom to an address
+ return stab.atom->finalAddress() + stab.value;
+ case N_STSYM:
+ case N_LCSYM:
+ case N_BNSYM:
+ // all these need address of atom
+ if ( stab.atom != NULL )
+ return stab.atom->finalAddress();
+ else
+ return 0; // <rdar://problem/7811357> work around for mismatch N_BNSYM
+ case N_ENSYM:
+ return stab.atom->size();
+ case N_SO:
+ if ( stab.atom == NULL ) {
+ return 0;
+ }
+ else {
+ if ( (stab.string == NULL) || (strlen(stab.string) == 0) ) {
+ // end of translation unit N_SO has address of end of last atom
+ return stab.atom->finalAddress() + stab.atom->size();
+ }
+ else {
+ // start of translation unit N_SO has address of end of first atom
+ return stab.atom->finalAddress();
+ }
+ }
+ break;
+ default:
+ return stab.value;
+ }
+}
+
+template <typename A>
+uint32_t SymbolTableAtom<A>::stringOffsetForStab(const ld::relocatable::File::Stab& stab, StringPoolAtom* pool)
+{
+ switch (stab.type) {
+ case N_SO:
+ if ( (stab.string == NULL) || stab.string[0] == '\0' ) {
+ return pool->emptyString();
+ break;
+ }
+ // fall into uniquing case
+ case N_SOL:
+ case N_BINCL:
+ case N_EXCL:
+ return pool->addUnique(stab.string);
+ break;
+ default:
+ if ( stab.string == NULL )
+ return 0;
+ else if ( stab.string[0] == '\0' )
+ return pool->emptyString();
+ else
+ return pool->add(stab.string);
+ }
+ return 0;
+}
+
+
+
+template <typename A>
+bool SymbolTableAtom<A>::hasStabs(uint32_t& ssos, uint32_t& ssoe, uint32_t& sos, uint32_t& soe)
+{
+ ssos = _stabsStringsOffsetStart;
+ ssoe = _stabsStringsOffsetEnd;
+ sos = _stabsIndexStart * sizeof(macho_nlist<P>);
+ soe = _stabsIndexEnd * sizeof(macho_nlist<P>);
+ return ( (_stabsIndexStart != _stabsIndexEnd) || (_stabsStringsOffsetStart != _stabsStringsOffsetEnd) );
+}
+
+template <typename A>
+void SymbolTableAtom<A>::encode()
+{
+ uint32_t symbolIndex = 0;
+
+ // make nlist entries for all local symbols
+ std::vector<const ld::Atom*>& localAtoms = this->_writer._localAtoms;
+ _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;
+
+
+ // make nlist entries for all global symbols
+ std::vector<const ld::Atom*>& globalAtoms = this->_writer._exportedAtoms;
+ _globals.reserve(globalAtoms.size());
+ this->_writer._globalSymbolsStartIndex = symbolIndex;
+ 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[atom] = symbolIndex++;
+ }
+ this->_writer._globalSymbolsCount = symbolIndex - this->_writer._globalSymbolsStartIndex;
+
+ // make nlist entries for all undefined (imported) symbols
+ std::vector<const ld::Atom*>& importAtoms = this->_writer._importedAtoms;
+ _imports.reserve(importAtoms.size());
+ this->_writer._importSymbolsStartIndex = symbolIndex;
+ for (std::vector<const ld::Atom*>::const_iterator it=importAtoms.begin(); it != importAtoms.end(); ++it) {
+ this->addImport(*it, this->_writer._stringPoolAtom);
+ this->_writer._atomToSymbolIndex[*it] = symbolIndex++;
+ }
+ this->_writer._importSymbolsCount = symbolIndex - this->_writer._importSymbolsStartIndex;
+}
+
+template <typename A>
+uint64_t SymbolTableAtom<A>::size() const
+{
+ return sizeof(macho_nlist<P>) * (_locals.size() + _globals.size() + _imports.size());
+}
+
+template <typename A>
+void SymbolTableAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ memcpy(&buffer[this->_writer._localSymbolsStartIndex*sizeof(macho_nlist<P>)], &_locals[0],
+ this->_writer._localSymbolsCount*sizeof(macho_nlist<P>));
+ memcpy(&buffer[this->_writer._globalSymbolsStartIndex*sizeof(macho_nlist<P>)], &_globals[0],
+ this->_writer._globalSymbolsCount*sizeof(macho_nlist<P>));
+ memcpy(&buffer[this->_writer._importSymbolsStartIndex *sizeof(macho_nlist<P>)], &_imports[0],
+ this->_writer._importSymbolsCount*sizeof(macho_nlist<P>));
+}
+
+
+
+
+class RelocationsAtomAbstract : public ClassicLinkEditAtom
+{
+public:
+ RelocationsAtomAbstract(const Options& opts, ld::Internal& state,
+ OutputFile& writer, const ld::Section& sect,
+ unsigned int pointerSize)
+ : ClassicLinkEditAtom(opts, state, writer, sect, pointerSize) { }
+
+ virtual void addPointerReloc(uint64_t addr, uint32_t symNum) = 0;
+ virtual void addTextReloc(uint64_t addr, ld::Fixup::Kind k, uint64_t targetAddr, uint32_t symNum) = 0;
+ virtual void addExternalPointerReloc(uint64_t addr, const ld::Atom*) = 0;
+ virtual void addExternalCallSiteReloc(uint64_t addr, const ld::Atom*) = 0;
+ virtual uint64_t relocBaseAddress(ld::Internal& state) = 0;
+ virtual void addSectionReloc(ld::Internal::FinalSection* sect, ld::Fixup::Kind,
+ const ld::Atom* inAtom, uint32_t offsetInAtom,
+ bool toTargetUsesExternalReloc ,bool fromTargetExternalReloc,
+ const ld::Atom* toTarget, uint64_t toAddend,
+ const ld::Atom* fromTarget, uint64_t fromAddend) = 0;
+protected:
+ uint32_t symbolIndex(const ld::Atom* atom) const;
+
+};
+
+
+
+uint32_t RelocationsAtomAbstract::symbolIndex(const ld::Atom* atom) const
+{
+ std::map<const ld::Atom*, uint32_t>::iterator pos = this->_writer._atomToSymbolIndex.find(atom);
+ if ( pos != this->_writer._atomToSymbolIndex.end() )
+ return pos->second;
+ fprintf(stderr, "_atomToSymbolIndex content:\n");
+ for(std::map<const ld::Atom*, uint32_t>::iterator it = this->_writer._atomToSymbolIndex.begin(); it != this->_writer._atomToSymbolIndex.end(); ++it) {
+ fprintf(stderr, "%p(%s) => %d\n", it->first, it->first->name(), it->second);
+ }
+ throwf("internal error: atom not found in symbolIndex(%s)", atom->name());
+}
+
+
+template <typename A>
+class LocalRelocationsAtom : public RelocationsAtomAbstract
+{
+public:
+ LocalRelocationsAtom(const Options& opts, ld::Internal& state, OutputFile& writer)
+ : RelocationsAtomAbstract(opts, state, writer, _s_section, sizeof(pint_t)) { }
+
+ // overrides of ld::Atom
+ virtual const char* name() const { return "local relocations"; }
+ virtual uint64_t size() const;
+ virtual void copyRawContent(uint8_t buffer[]) const;
+ // overrides of ClassicLinkEditAtom
+ virtual void encode() {}
+ // overrides of RelocationsAtomAbstract
+ virtual void addPointerReloc(uint64_t addr, uint32_t symNum);
+ virtual void addExternalPointerReloc(uint64_t addr, const ld::Atom*) {}
+ virtual void addExternalCallSiteReloc(uint64_t addr, const ld::Atom*) {}
+ virtual uint64_t relocBaseAddress(ld::Internal& state);
+ virtual void addTextReloc(uint64_t addr, ld::Fixup::Kind k, uint64_t targetAddr, uint32_t symNum);
+ virtual void addSectionReloc(ld::Internal::FinalSection* sect, ld::Fixup::Kind,
+ const ld::Atom* inAtom, uint32_t offsetInAtom,
+ bool toTargetUsesExternalReloc ,bool fromTargetExternalReloc,
+ const ld::Atom* toTarget, uint64_t toAddend,
+ const ld::Atom* fromTarget, uint64_t fromAddend) { }
+
+private:
+ typedef typename A::P P;
+ typedef typename A::P::E E;
+ typedef typename A::P::uint_t pint_t;
+
+ std::vector<macho_relocation_info<P> > _relocs;
+
+ static ld::Section _s_section;
+};
+
+template <typename A>
+ld::Section LocalRelocationsAtom<A>::_s_section("__LINKEDIT", "__local_relocs", ld::Section::typeLinkEdit, true);
+
+
+template <>
+uint64_t LocalRelocationsAtom<x86_64>::relocBaseAddress(ld::Internal& state)
+{
+ if ( _options.outputKind() == Options::kKextBundle ) {
+ // 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 (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 )
+ return sect->address;
+ }
+ throw "__DATA segment not found";
+}
+
+template <typename A>
+uint64_t LocalRelocationsAtom<A>::relocBaseAddress(ld::Internal& state)
+{
+ return _options.baseAddress();
+}
+
+template <typename A>
+void LocalRelocationsAtom<A>::addPointerReloc(uint64_t addr, uint32_t symNum)
+{
+ macho_relocation_info<P> reloc;
+ reloc.set_r_address(addr);
+ reloc.set_r_symbolnum(symNum);
+ reloc.set_r_pcrel(false);
+ reloc.set_r_length();
+ reloc.set_r_extern(false);
+ reloc.set_r_type(GENERIC_RELOC_VANILLA);
+ _relocs.push_back(reloc);
+}
+
+template <typename A>
+void LocalRelocationsAtom<A>::addTextReloc(uint64_t addr, ld::Fixup::Kind kind, uint64_t targetAddr, uint32_t symNum)
+{
+ macho_relocation_info<P> reloc1;
+ macho_relocation_info<P> reloc2;
+ switch ( kind ) {
+ case ld::Fixup::kindStorePPCAbsLow14:
+ case ld::Fixup::kindStorePPCAbsLow16:
+ // a reference to the absolute address of something in this same linkage unit can be
+ // encoded as a local text reloc in a dylib or bundle
+ if ( _options.outputSlidable() ) {
+ reloc1.set_r_address(addr);
+ reloc1.set_r_symbolnum(symNum);
+ reloc1.set_r_pcrel(false);
+ reloc1.set_r_length(2);
+ reloc1.set_r_extern(false);
+ reloc1.set_r_type(kind==ld::Fixup::kindStorePPCAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14);
+ reloc2.set_r_address(targetAddr >> 16);
+ reloc2.set_r_symbolnum(0);
+ reloc2.set_r_pcrel(false);
+ reloc2.set_r_length(2);
+ reloc2.set_r_extern(false);
+ reloc2.set_r_type(PPC_RELOC_PAIR);
+ _relocs.push_back(reloc1);
+ _relocs.push_back(reloc2);
+ }
+ break;
+ case ld::Fixup::kindStorePPCAbsHigh16AddLow:
+ case ld::Fixup::kindStorePPCAbsHigh16:
+ if ( _options.outputSlidable() ) {
+ reloc1.set_r_address(addr);
+ reloc1.set_r_symbolnum(symNum);
+ reloc1.set_r_pcrel(false);
+ reloc1.set_r_length(2);
+ reloc1.set_r_extern(false);
+ reloc1.set_r_type(kind==ld::Fixup::kindStorePPCAbsHigh16AddLow ? PPC_RELOC_HA16 : PPC_RELOC_HI16);
+ reloc2.set_r_address(targetAddr & 0xFFFF);
+ reloc2.set_r_symbolnum(0);
+ reloc2.set_r_pcrel(false);
+ reloc2.set_r_length(2);
+ reloc2.set_r_extern(false);
+ reloc2.set_r_type(PPC_RELOC_PAIR);
+ _relocs.push_back(reloc1);
+ _relocs.push_back(reloc2);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+
+template <typename A>
+uint64_t LocalRelocationsAtom<A>::size() const
+{
+ return _relocs.size() * sizeof(macho_relocation_info<P>);
+}
+
+template <typename A>
+void LocalRelocationsAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ memcpy(buffer, &_relocs[0], _relocs.size()*sizeof(macho_relocation_info<P>));
+}
+
+
+
+
+
+
+template <typename A>
+class ExternalRelocationsAtom : public RelocationsAtomAbstract
+{
+public:
+ ExternalRelocationsAtom(const Options& opts, ld::Internal& state, OutputFile& writer)
+ : RelocationsAtomAbstract(opts, state, writer, _s_section, sizeof(pint_t)) { }
+
+ // overrides of ld::Atom
+ virtual const char* name() const { return "external relocations"; }
+ virtual uint64_t size() const;
+ virtual void copyRawContent(uint8_t buffer[]) const;
+ // overrides of ClassicLinkEditAtom
+ virtual void encode() {}
+ // overrides of RelocationsAtomAbstract
+ virtual void addPointerReloc(uint64_t addr, uint32_t symNum) {}
+ virtual void addTextReloc(uint64_t addr, ld::Fixup::Kind k, uint64_t targetAddr, uint32_t symNum) {}
+ virtual void addExternalPointerReloc(uint64_t addr, const ld::Atom*);
+ virtual void addExternalCallSiteReloc(uint64_t addr, const ld::Atom*);
+ virtual uint64_t relocBaseAddress(ld::Internal& state);
+ virtual void addSectionReloc(ld::Internal::FinalSection* sect, ld::Fixup::Kind,
+ const ld::Atom* inAtom, uint32_t offsetInAtom,
+ bool toTargetUsesExternalReloc ,bool fromTargetExternalReloc,
+ const ld::Atom* toTarget, uint64_t toAddend,
+ const ld::Atom* fromTarget, uint64_t fromAddend) { }
+
+
+private:
+ typedef typename A::P P;
+ typedef typename A::P::E E;
+ typedef typename A::P::uint_t pint_t;
+
+ struct LocAndAtom {
+ LocAndAtom(uint64_t l, const ld::Atom* a) : loc(l), atom(a), symbolIndex(0) {}
+
+ uint64_t loc;
+ const ld::Atom* atom;
+ uint32_t symbolIndex;
+
+ bool operator<(const LocAndAtom& rhs) const {
+ // sort first by symbol number
+ if ( this->symbolIndex != rhs.symbolIndex )
+ return (this->symbolIndex < rhs.symbolIndex);
+ // then sort all uses of the same symbol by address
+ return (this->loc < rhs.loc);
+ }
+
+ };
+
+ static uint32_t pointerReloc();
+ static uint32_t callReloc();
+
+ mutable std::vector<LocAndAtom> _pointerLocations;
+ mutable std::vector<LocAndAtom> _callSiteLocations;
+
+ static ld::Section _s_section;
+};
+
+template <typename A>
+ld::Section ExternalRelocationsAtom<A>::_s_section("__LINKEDIT", "__extrn_relocs", ld::Section::typeLinkEdit, true);
+
+template <>
+uint64_t ExternalRelocationsAtom<x86_64>::relocBaseAddress(ld::Internal& state)
+{
+ // 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 )
+ return sect->address;
+ }
+ throw "__DATA segment not found";
+}
+
+template <typename A>
+uint64_t ExternalRelocationsAtom<A>::relocBaseAddress(ld::Internal& state)
+{
+ return 0;
+}
+
+template <typename A>
+void ExternalRelocationsAtom<A>::addExternalPointerReloc(uint64_t addr, const ld::Atom* target)
+{
+ _pointerLocations.push_back(LocAndAtom(addr, target));
+}
+
+template <typename A>
+void ExternalRelocationsAtom<A>::addExternalCallSiteReloc(uint64_t addr, const ld::Atom* target)
+{
+ _callSiteLocations.push_back(LocAndAtom(addr, target));
+}
+
+
+template <typename A>
+uint64_t ExternalRelocationsAtom<A>::size() const
+{
+ if ( _options.outputKind() == Options::kStaticExecutable ) {
+ assert(_pointerLocations.size() == 0);
+ assert(_callSiteLocations.size() == 0);
+ }
+ return (_pointerLocations.size() + _callSiteLocations.size()) * sizeof(macho_relocation_info<P>);
+}
+
+template <> uint32_t ExternalRelocationsAtom<arm>::pointerReloc() { return ARM_RELOC_VANILLA; }
+template <> uint32_t ExternalRelocationsAtom<x86>::pointerReloc() { return GENERIC_RELOC_VANILLA; }
+template <> uint32_t ExternalRelocationsAtom<ppc>::pointerReloc() { return PPC_RELOC_VANILLA; }
+template <> uint32_t ExternalRelocationsAtom<x86_64>::pointerReloc() { return X86_64_RELOC_UNSIGNED; }
+template <> uint32_t ExternalRelocationsAtom<ppc64>::pointerReloc() { return PPC_RELOC_VANILLA; }
+
+
+template <> uint32_t ExternalRelocationsAtom<x86_64>::callReloc() { return X86_64_RELOC_BRANCH; }
+template <> uint32_t ExternalRelocationsAtom<x86>::callReloc() { return GENERIC_RELOC_VANILLA; }
+template <typename A>
+uint32_t ExternalRelocationsAtom<A>::callReloc()
+{
+ assert(0 && "external call relocs not implemented");
+ return 0;
+}
+
+
+template <typename A>
+void ExternalRelocationsAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ macho_relocation_info<P>* r = (macho_relocation_info<P>*)buffer;
+
+ // assign symbol index, now that symbol table is built
+ for (typename std::vector<LocAndAtom>::iterator it = _pointerLocations.begin(); it != _pointerLocations.end(); ++it) {
+ it->symbolIndex = symbolIndex(it->atom);
+ }
+ std::sort(_pointerLocations.begin(), _pointerLocations.end());
+ for (typename std::vector<LocAndAtom>::const_iterator it = _pointerLocations.begin(); it != _pointerLocations.end(); ++it, ++r) {
+ r->set_r_address(it->loc);
+ r->set_r_symbolnum(it->symbolIndex);
+ r->set_r_pcrel(false);
+ r->set_r_length();
+ r->set_r_extern(true);
+ r->set_r_type(this->pointerReloc());
+ }
+
+ for (typename std::vector<LocAndAtom>::iterator it = _callSiteLocations.begin(); it != _callSiteLocations.end(); ++it) {
+ it->symbolIndex = symbolIndex(it->atom);
+ }
+ std::sort(_callSiteLocations.begin(), _callSiteLocations.end());
+ for (typename std::vector<LocAndAtom>::const_iterator it = _callSiteLocations.begin(); it != _callSiteLocations.end(); ++it, ++r) {
+ r->set_r_address(it->loc);
+ r->set_r_symbolnum(it->symbolIndex);
+ r->set_r_pcrel(true);
+ r->set_r_length(2);
+ r->set_r_extern(true);
+ r->set_r_type(this->callReloc());
+ }
+}
+
+
+template <typename A>
+class SectionRelocationsAtom : public RelocationsAtomAbstract
+{
+public:
+ SectionRelocationsAtom(const Options& opts, ld::Internal& state, OutputFile& writer)
+ : RelocationsAtomAbstract(opts, state, writer, _s_section, sizeof(pint_t)) { }
+
+ // overrides of ld::Atom
+ virtual const char* name() const { return "section relocations"; }
+ virtual uint64_t size() const;
+ virtual void copyRawContent(uint8_t buffer[]) const;
+ // overrides of ClassicLinkEditAtom
+ virtual void encode();
+ // overrides of RelocationsAtomAbstract
+ virtual void addPointerReloc(uint64_t addr, uint32_t symNum) {}
+ virtual void addTextReloc(uint64_t addr, ld::Fixup::Kind k, uint64_t targetAddr, uint32_t symNum) {}
+ virtual void addExternalPointerReloc(uint64_t addr, const ld::Atom*) {}
+ virtual void addExternalCallSiteReloc(uint64_t addr, const ld::Atom*) {}
+ virtual uint64_t relocBaseAddress(ld::Internal& state) { return 0; }
+ virtual void addSectionReloc(ld::Internal::FinalSection* sect, ld::Fixup::Kind,
+ const ld::Atom* inAtom, uint32_t offsetInAtom,
+ bool toTargetUsesExternalReloc ,bool fromTargetExternalReloc,
+ const ld::Atom* toTarget, uint64_t toAddend,
+ const ld::Atom* fromTarget, uint64_t fromAddend);
+
+private:
+ typedef typename A::P P;
+ typedef typename A::P::E E;
+ typedef typename A::P::uint_t pint_t;
+
+
+ struct Entry {
+ ld::Fixup::Kind kind;
+ bool toTargetUsesExternalReloc;
+ bool fromTargetUsesExternalReloc;
+ const ld::Atom* inAtom;
+ uint32_t offsetInAtom;
+ const ld::Atom* toTarget;
+ uint64_t toAddend;
+ const ld::Atom* fromTarget;
+ uint64_t fromAddend;
+ };
+ uint32_t sectSymNum(bool external, const ld::Atom* target);
+ void encodeSectionReloc(ld::Internal::FinalSection* sect,
+ const Entry& entry, std::vector<macho_relocation_info<P> >& relocs);
+
+ struct SectionAndEntries {
+ ld::Internal::FinalSection* sect;
+ std::vector<Entry> entries;
+ std::vector<macho_relocation_info<P> > relocs;
+ };
+
+ std::vector<SectionAndEntries> _entriesBySection;
+
+ static ld::Section _s_section;
+};
+
+template <typename A>
+ld::Section SectionRelocationsAtom<A>::_s_section("__LINKEDIT", "__sect_relocs", ld::Section::typeLinkEdit, true);
+
+
+
+
+template <typename A>
+uint64_t SectionRelocationsAtom<A>::size() const
+{
+ uint32_t count = 0;
+ for(typename std::vector<SectionAndEntries>::const_iterator it=_entriesBySection.begin(); it != _entriesBySection.end(); ++it) {
+ const SectionAndEntries& se = *it;
+ count += se.relocs.size();
+ }
+ return count * sizeof(macho_relocation_info<P>);
+}
+
+template <typename A>
+void SectionRelocationsAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ uint32_t offset = 0;
+ for(typename std::vector<SectionAndEntries>::const_iterator it=_entriesBySection.begin(); it != _entriesBySection.end(); ++it) {
+ const SectionAndEntries& se = *it;
+ memcpy(&buffer[offset], &se.relocs[0], se.relocs.size()*sizeof(macho_relocation_info<P>));
+ offset += (se.relocs.size() * sizeof(macho_relocation_info<P>));
+ }
+}
+
+
+template <>
+void SectionRelocationsAtom<x86_64>::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::kindStoreX86BranchPCRel32:
+ case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32:
+ case ld::Fixup::kindStoreX86DtraceCallSiteNop:
+ case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear:
+ 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(X86_64_RELOC_BRANCH);
+ relocs.push_back(reloc1);
+ break;
+
+ case ld::Fixup::kindStoreX86BranchPCRel8:
+ reloc1.set_r_address(address);
+ reloc1.set_r_symbolnum(symbolNum);
+ reloc1.set_r_pcrel(true);
+ reloc1.set_r_length(0);
+ reloc1.set_r_extern(external);
+ reloc1.set_r_type(X86_64_RELOC_BRANCH);
+ relocs.push_back(reloc1);
+ break;
+
+ case ld::Fixup::kindStoreX86PCRel32:
+ case ld::Fixup::kindStoreTargetAddressX86PCRel32:
+ 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(X86_64_RELOC_SIGNED);
+ relocs.push_back(reloc1);
+ break;
+
+ case ld::Fixup::kindStoreX86PCRel32_1:
+ 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(X86_64_RELOC_SIGNED_1);
+ relocs.push_back(reloc1);
+ break;
+
+ case ld::Fixup::kindStoreX86PCRel32_2:
+ 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(X86_64_RELOC_SIGNED_2);
+ relocs.push_back(reloc1);
+ break;
+
+ case ld::Fixup::kindStoreX86PCRel32_4:
+ 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(X86_64_RELOC_SIGNED_4);
+ relocs.push_back(reloc1);
+ break;
+
+ case ld::Fixup::kindStoreX86PCRel32GOTLoad:
+ case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad:
+ 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(X86_64_RELOC_GOT_LOAD);
+ relocs.push_back(reloc1);
+ break;
+
+ case ld::Fixup::kindStoreX86PCRel32GOT:
+ 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(X86_64_RELOC_GOT);
+ 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(X86_64_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(X86_64_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(X86_64_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(X86_64_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(X86_64_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(X86_64_RELOC_UNSIGNED);
+ relocs.push_back(reloc1);
+ }
+ break;
+ default:
+ assert(0 && "need to handle -r reloc");
+
+ }
+
+}
+
+
+
+template <typename A>
+uint32_t SectionRelocationsAtom<A>::sectSymNum(bool external, const ld::Atom* target)
+{
+ if ( target->definition() == ld::Atom::definitionAbsolute )
+ return R_ABS;
+ if ( external )
+ return this->symbolIndex(target); // in external relocations, r_symbolnum field is symbol index
+ else
+ return target->machoSection(); // in non-extern relocations, r_symbolnum is mach-o section index of target
+}
+
+template <>
+void SectionRelocationsAtom<x86>::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;
+ macho_scattered_relocation_info<P>* sreloc1 = (macho_scattered_relocation_info<P>*)&reloc1;
+ macho_scattered_relocation_info<P>* sreloc2 = (macho_scattered_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::kindStoreX86PCRel32:
+ case ld::Fixup::kindStoreX86BranchPCRel32:
+ case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32:
+ case ld::Fixup::kindStoreX86DtraceCallSiteNop:
+ case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear:
+ if ( !external && (entry.toAddend != 0) ) {
+ // use scattered reloc is target offset is non-zero
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(true);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(GENERIC_RELOC_VANILLA);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(entry.toTarget->finalAddress());
+ }
+ else {
+ 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(GENERIC_RELOC_VANILLA);
+ }
+ relocs.push_back(reloc1);
+ break;
+
+ case ld::Fixup::kindStoreX86BranchPCRel8:
+ if ( !external && (entry.toAddend != 0) ) {
+ // use scattered reloc is target offset is non-zero
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(true);
+ sreloc1->set_r_length(0);
+ sreloc1->set_r_type(GENERIC_RELOC_VANILLA);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(entry.toTarget->finalAddress());
+ }
+ else {
+ reloc1.set_r_address(address);
+ reloc1.set_r_symbolnum(symbolNum);
+ reloc1.set_r_pcrel(true);
+ reloc1.set_r_length(0);
+ reloc1.set_r_extern(external);
+ reloc1.set_r_type(GENERIC_RELOC_VANILLA);
+ }
+ relocs.push_back(reloc1);
+ break;
+
+ case ld::Fixup::kindStoreX86PCRel16:
+ if ( !external && (entry.toAddend != 0) ) {
+ // use scattered reloc is target offset is non-zero
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(true);
+ sreloc1->set_r_length(1);
+ sreloc1->set_r_type(GENERIC_RELOC_VANILLA);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(entry.toTarget->finalAddress());
+ }
+ else {
+ reloc1.set_r_address(address);
+ reloc1.set_r_symbolnum(symbolNum);
+ reloc1.set_r_pcrel(true);
+ reloc1.set_r_length(1);
+ reloc1.set_r_extern(external);
+ reloc1.set_r_type(GENERIC_RELOC_VANILLA);
+ }
+ relocs.push_back(reloc1);
+ break;
+
+ case ld::Fixup::kindStoreLittleEndian32:
+ case ld::Fixup::kindStoreTargetAddressLittleEndian32:
+ if ( entry.fromTarget != NULL ) {
+ // this is a pointer-diff
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(2);
+ if ( entry.toTarget->scope() == ld::Atom::scopeTranslationUnit )
+ sreloc1->set_r_type(GENERIC_RELOC_LOCAL_SECTDIFF);
+ else
+ sreloc1->set_r_type(GENERIC_RELOC_SECTDIFF);
+ sreloc1->set_r_address(address);
+ if ( entry.toTarget == entry.inAtom )
+ sreloc1->set_r_value(entry.toTarget->finalAddress()+entry.toAddend);
+ else
+ sreloc1->set_r_value(entry.toTarget->finalAddress());
+ sreloc2->set_r_scattered(true);
+ sreloc2->set_r_pcrel(false);
+ sreloc2->set_r_length(2);
+ sreloc2->set_r_type(GENERIC_RELOC_PAIR);
+ sreloc2->set_r_address(0);
+ if ( entry.fromTarget == entry.inAtom ) {
+ if ( entry.fromAddend > entry.fromTarget->size() )
+ sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.offsetInAtom);
+ else
+ sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.fromAddend);
+ }
+ else
+ sreloc2->set_r_value(entry.fromTarget->finalAddress());
+ relocs.push_back(reloc1);
+ relocs.push_back(reloc2);
+ }
+ else {
+ // regular pointer
+ if ( !external && (entry.toAddend != 0) ) {
+ // use scattered reloc is target offset is non-zero
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(GENERIC_RELOC_VANILLA);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(entry.toTarget->finalAddress());
+ }
+ else {
+ 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(GENERIC_RELOC_VANILLA);
+ }
+ relocs.push_back(reloc1);
+ }
+ break;
+ default:
+ assert(0 && "need to handle -r reloc");
+
+ }
+}
+
+
+template <>
+void SectionRelocationsAtom<arm>::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;
+ macho_scattered_relocation_info<P>* sreloc1 = (macho_scattered_relocation_info<P>*)&reloc1;
+ macho_scattered_relocation_info<P>* sreloc2 = (macho_scattered_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::kindStoreTargetAddressARMBranch24:
+ case ld::Fixup::kindStoreARMBranch24:
+ case ld::Fixup::kindStoreARMDtraceCallSiteNop:
+ case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear:
+ if ( !external && (entry.toAddend != 0) ) {
+ // use scattered reloc is target offset is non-zero
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(true);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(ARM_RELOC_BR24);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(entry.toTarget->finalAddress());
+ }
+ else {
+ 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(ARM_RELOC_BR24);
+ }
+ relocs.push_back(reloc1);
+ break;
+
+ case ld::Fixup::kindStoreTargetAddressThumbBranch22:
+ case ld::Fixup::kindStoreThumbBranch22:
+ case ld::Fixup::kindStoreThumbDtraceCallSiteNop:
+ case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear:
+ if ( !external && (entry.toAddend != 0) ) {
+ // use scattered reloc is target offset is non-zero
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(true);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(ARM_THUMB_RELOC_BR22);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(entry.toTarget->finalAddress());
+ }
+ else {
+ 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(ARM_THUMB_RELOC_BR22);
+ }
+ relocs.push_back(reloc1);
+ break;
+
+ case ld::Fixup::kindStoreLittleEndian32:
+ case ld::Fixup::kindStoreTargetAddressLittleEndian32:
+ if ( entry.fromTarget != NULL ) {
+ // this is a pointer-diff
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(2);
+ if ( entry.toTarget->scope() == ld::Atom::scopeTranslationUnit )
+ sreloc1->set_r_type(ARM_RELOC_LOCAL_SECTDIFF);
+ else
+ sreloc1->set_r_type(ARM_RELOC_SECTDIFF);
+ sreloc1->set_r_address(address);
+ if ( entry.toTarget == entry.inAtom )
+ sreloc1->set_r_value(entry.toTarget->finalAddress()+entry.toAddend);
+ else
+ sreloc1->set_r_value(entry.toTarget->finalAddress());
+ sreloc2->set_r_scattered(true);
+ sreloc2->set_r_pcrel(false);
+ sreloc2->set_r_length(2);
+ sreloc2->set_r_type(ARM_RELOC_PAIR);
+ sreloc2->set_r_address(0);
+ if ( entry.fromTarget == entry.inAtom ) {
+ //unsigned int pcBaseOffset = entry.inAtom->isThumb() ? 4 : 8;
+ //if ( entry.fromAddend > pcBaseOffset )
+ // sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.fromAddend-pcBaseOffset);
+ //else
+ sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.fromAddend);
+ }
+ else {
+ sreloc2->set_r_value(entry.fromTarget->finalAddress());
+ }
+ relocs.push_back(reloc1);
+ relocs.push_back(reloc2);
+ }
+ else {
+ // regular pointer
+ if ( !external && (entry.toAddend != 0) ) {
+ // use scattered reloc is target offset is non-zero
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(ARM_RELOC_VANILLA);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(entry.toTarget->finalAddress());
+ }
+ else {
+ 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(ARM_RELOC_VANILLA);
+ }
+ relocs.push_back(reloc1);
+ }
+ break;
+
+ case ld::Fixup::kindStoreARMLow16:
+ case ld::Fixup::kindStoreARMHigh16:
+ case ld::Fixup::kindStoreThumbLow16:
+ case ld::Fixup::kindStoreThumbHigh16:
+ {
+ 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);
+ switch ( entry.kind ) {
+ case ld::Fixup::kindStoreARMLow16:
+ len = 0;
+ otherHalf = value >> 16;
+ break;
+ case ld::Fixup::kindStoreARMHigh16:
+ len = 1;
+ otherHalf = value & 0xFFFF;
+ break;
+ case ld::Fixup::kindStoreThumbLow16:
+ len = 2;
+ otherHalf = value >> 16;
+ break;
+ case ld::Fixup::kindStoreThumbHigh16:
+ len = 3;
+ otherHalf = value & 0xFFFF;
+ break;
+ default:
+ break;
+ }
+ if ( entry.fromTarget != NULL ) {
+ // this is a sect-diff
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(len);
+ sreloc1->set_r_type(ARM_RELOC_HALF_SECTDIFF);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(entry.toTarget->finalAddress());
+ sreloc2->set_r_scattered(true);
+ sreloc2->set_r_pcrel(false);
+ sreloc2->set_r_length(len);
+ sreloc2->set_r_type(ARM_RELOC_PAIR);
+ sreloc2->set_r_address(otherHalf);
+ if ( entry.fromTarget == entry.inAtom )
+ sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.fromAddend);
+ else
+ sreloc2->set_r_value(entry.fromTarget->finalAddress());
+ relocs.push_back(reloc1);
+ relocs.push_back(reloc2);
+ }
+ else {
+ // this is absolute address
+ if ( !external && (entry.toAddend != 0) ) {
+ // use scattered reloc is target offset is non-zero
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(len);
+ sreloc1->set_r_type(ARM_RELOC_HALF);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(entry.toTarget->finalAddress());
+ reloc2.set_r_address(otherHalf);
+ reloc2.set_r_symbolnum(0);
+ reloc2.set_r_pcrel(false);
+ reloc2.set_r_length(len);
+ reloc2.set_r_extern(false);
+ reloc2.set_r_type(ARM_RELOC_PAIR);
+ relocs.push_back(reloc1);
+ relocs.push_back(reloc2);
+ }
+ else {
+ reloc1.set_r_address(address);
+ reloc1.set_r_symbolnum(symbolNum);
+ reloc1.set_r_pcrel(false);
+ reloc1.set_r_length(len);
+ reloc1.set_r_extern(false);
+ reloc1.set_r_type(ARM_RELOC_HALF);
+ reloc2.set_r_address(otherHalf); // other half
+ reloc2.set_r_symbolnum(0);
+ reloc2.set_r_pcrel(false);
+ reloc2.set_r_length(len);
+ reloc2.set_r_extern(false);
+ reloc2.set_r_type(ARM_RELOC_PAIR);
+ relocs.push_back(reloc1);
+ relocs.push_back(reloc2);
+ }
+ }
+ }
+ break;
+
+ default:
+ assert(0 && "need to handle -r reloc");
+
+ }
+}
+
+
+template <>
+void SectionRelocationsAtom<ppc>::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;
+ macho_scattered_relocation_info<P>* sreloc1 = (macho_scattered_relocation_info<P>*)&reloc1;
+ macho_scattered_relocation_info<P>* sreloc2 = (macho_scattered_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);
+ }
+ uint32_t toAddr;
+ uint32_t fromAddr;
+
+ switch ( entry.kind ) {
+
+ case ld::Fixup::kindStorePPCBranch24:
+ case ld::Fixup::kindStoreTargetAddressPPCBranch24:
+ case ld::Fixup::kindStorePPCDtraceCallSiteNop:
+ case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear:
+ if ( !external && (entry.toAddend != 0) ) {
+ // use scattered reloc if target offset is non-zero
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(true);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(PPC_RELOC_BR24);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(entry.toTarget->finalAddress());
+ }
+ else {
+ 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(PPC_RELOC_BR24);
+ }
+ relocs.push_back(reloc1);
+ break;
+
+ case ld::Fixup::kindStorePPCBranch14:
+ if ( !external && (entry.toAddend != 0) ) {
+ // use scattered reloc if target offset is non-zero
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(true);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(PPC_RELOC_BR14);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(entry.toTarget->finalAddress());
+ }
+ else {
+ 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(PPC_RELOC_BR14);
+ }
+ relocs.push_back(reloc1);
+ break;
+
+ case ld::Fixup::kindStoreBigEndian32:
+ case ld::Fixup::kindStoreTargetAddressBigEndian32:
+ if ( entry.fromTarget != NULL ) {
+ // this is a pointer-diff
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(2);
+ if ( entry.toTarget->scope() == ld::Atom::scopeTranslationUnit )
+ sreloc1->set_r_type(PPC_RELOC_LOCAL_SECTDIFF);
+ else
+ sreloc1->set_r_type(PPC_RELOC_SECTDIFF);
+ sreloc1->set_r_address(address);
+ if ( entry.toTarget == entry.inAtom )
+ sreloc1->set_r_value(entry.toTarget->finalAddress()+entry.toAddend);
+ else
+ sreloc1->set_r_value(entry.toTarget->finalAddress());
+ sreloc2->set_r_scattered(true);
+ sreloc2->set_r_pcrel(false);
+ sreloc2->set_r_length(2);
+ sreloc2->set_r_type(PPC_RELOC_PAIR);
+ sreloc2->set_r_address(0);
+ if ( entry.fromTarget == entry.inAtom ) {
+ if ( entry.fromAddend > entry.fromTarget->size() )
+ sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.offsetInAtom);
+ else
+ sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.fromAddend);
+ }
+ else
+ sreloc2->set_r_value(entry.fromTarget->finalAddress());
+ relocs.push_back(reloc1);
+ relocs.push_back(reloc2);
+ }
+ else {
+ // regular pointer
+ if ( !external && (entry.toAddend != 0) ) {
+ // use scattered reloc is target offset is non-zero
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(GENERIC_RELOC_VANILLA);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(entry.toTarget->finalAddress());
+ }
+ else {
+ 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(GENERIC_RELOC_VANILLA);
+ }
+ relocs.push_back(reloc1);
+ }
+ break;
+
+ case ld::Fixup::kindStorePPCAbsLow14:
+ case ld::Fixup::kindStorePPCAbsLow16:
+ if ( !external && (entry.toAddend != 0) ) {
+ // use scattered reloc if target offset is non-zero
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(entry.kind==ld::Fixup::kindStorePPCAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(entry.toTarget->finalAddress());
+ }
+ else {
+ 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(entry.kind==ld::Fixup::kindStorePPCAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14);
+ }
+ if ( external )
+ reloc2.set_r_address(entry.toAddend >> 16);
+ else
+ reloc2.set_r_address((entry.toTarget->finalAddress()+entry.toAddend) >> 16);
+ reloc2.set_r_symbolnum(0);
+ reloc2.set_r_pcrel(false);
+ reloc2.set_r_length(2);
+ reloc2.set_r_extern(false);
+ reloc2.set_r_type(PPC_RELOC_PAIR);
+ relocs.push_back(reloc1);
+ relocs.push_back(reloc2);
+ break;
+
+ case ld::Fixup::kindStorePPCAbsHigh16:
+ if ( !external && (entry.toAddend != 0) ) {
+ // use scattered reloc if target offset is non-zero
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(PPC_RELOC_HI16);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(entry.toTarget->finalAddress());
+ }
+ else {
+ 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(PPC_RELOC_HI16);
+ }
+ if ( external )
+ reloc2.set_r_address(entry.toAddend & 0x0000FFFF);
+ else
+ reloc2.set_r_address((entry.toTarget->finalAddress()+entry.toAddend) & 0x0000FFFF);
+ reloc2.set_r_symbolnum(0);
+ reloc2.set_r_pcrel(false);
+ reloc2.set_r_length(2);
+ reloc2.set_r_extern(false);
+ reloc2.set_r_type(PPC_RELOC_PAIR);
+ relocs.push_back(reloc1);
+ relocs.push_back(reloc2);
+ break;
+
+ case ld::Fixup::kindStorePPCAbsHigh16AddLow:
+ if ( !external && (entry.toAddend != 0) ) {
+ // use scattered reloc if target offset is non-zero
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(PPC_RELOC_HA16);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(entry.toTarget->finalAddress());
+ }
+ else {
+ 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(PPC_RELOC_HA16);
+ }
+ if ( external )
+ reloc2.set_r_address(entry.toAddend & 0x0000FFFF);
+ else
+ reloc2.set_r_address((entry.toTarget->finalAddress()+entry.toAddend) & 0x0000FFFF);
+ reloc2.set_r_symbolnum(0);
+ reloc2.set_r_pcrel(false);
+ reloc2.set_r_length(2);
+ reloc2.set_r_extern(false);
+ reloc2.set_r_type(PPC_RELOC_PAIR);
+ relocs.push_back(reloc1);
+ relocs.push_back(reloc2);
+ break;
+
+ case ld::Fixup::kindStorePPCPicLow14:
+ case ld::Fixup::kindStorePPCPicLow16:
+ fromAddr = entry.fromTarget->finalAddress() + entry.fromAddend;
+ toAddr = entry.toTarget->finalAddress() + entry.toAddend;
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(entry.kind == ld::Fixup::kindStorePPCPicLow16 ? PPC_RELOC_LO16_SECTDIFF : PPC_RELOC_LO14_SECTDIFF);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(entry.toTarget->finalAddress());
+ sreloc2->set_r_scattered(true);
+ sreloc2->set_r_pcrel(false);
+ sreloc2->set_r_length(2);
+ sreloc2->set_r_type(PPC_RELOC_PAIR);
+ sreloc2->set_r_address(((toAddr-fromAddr) >> 16) & 0xFFFF);
+ sreloc2->set_r_value(fromAddr);
+ relocs.push_back(reloc1);
+ relocs.push_back(reloc2);
+ break;
+
+ case ld::Fixup::kindStorePPCPicHigh16AddLow:
+ fromAddr = entry.fromTarget->finalAddress() + entry.fromAddend;
+ toAddr = entry.toTarget->finalAddress() + entry.toAddend;
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(PPC_RELOC_HA16_SECTDIFF);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(entry.toTarget->finalAddress());
+ sreloc2->set_r_scattered(true);
+ sreloc2->set_r_pcrel(false);
+ sreloc2->set_r_length(2);
+ sreloc2->set_r_type(PPC_RELOC_PAIR);
+ sreloc2->set_r_address((toAddr-fromAddr) & 0xFFFF);
+ sreloc2->set_r_value(fromAddr);
+ relocs.push_back(reloc1);
+ relocs.push_back(reloc2);
+ break;
+
+ default:
+ assert(0 && "need to handle -r reloc");
+
+ }
+}
+
+template <>
+void SectionRelocationsAtom<ppc64>::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;
+ macho_scattered_relocation_info<P>* sreloc1 = (macho_scattered_relocation_info<P>*)&reloc1;
+ macho_scattered_relocation_info<P>* sreloc2 = (macho_scattered_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);
+ }
+ uint32_t toAddr;
+ uint32_t fromAddr;
+
+ switch ( entry.kind ) {
+
+ case ld::Fixup::kindStorePPCBranch24:
+ case ld::Fixup::kindStoreTargetAddressPPCBranch24:
+ case ld::Fixup::kindStorePPCDtraceCallSiteNop:
+ case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear:
+ if ( !external && (entry.toAddend != 0) ) {
+ // use scattered reloc if target offset is non-zero
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(true);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(PPC_RELOC_BR24);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(entry.toTarget->finalAddress());
+ }
+ else {
+ 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(PPC_RELOC_BR24);
+ }
+ relocs.push_back(reloc1);
+ break;
+
+ case ld::Fixup::kindStorePPCBranch14:
+ if ( !external && (entry.toAddend != 0) ) {
+ // use scattered reloc if target offset is non-zero
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(true);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(PPC_RELOC_BR14);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(entry.toTarget->finalAddress());
+ }
+ else {
+ 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(PPC_RELOC_BR14);
+ }
+ relocs.push_back(reloc1);
+ break;
+
+ case ld::Fixup::kindStoreBigEndian32:
+ case ld::Fixup::kindStoreTargetAddressBigEndian32:
+ if ( entry.fromTarget != NULL ) {
+ // this is a pointer-diff
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(2);
+ if ( entry.toTarget->scope() == ld::Atom::scopeTranslationUnit )
+ sreloc1->set_r_type(PPC_RELOC_LOCAL_SECTDIFF);
+ else
+ sreloc1->set_r_type(PPC_RELOC_SECTDIFF);
+ sreloc1->set_r_address(address);
+ if ( entry.toTarget == entry.inAtom )
+ sreloc1->set_r_value(entry.toTarget->finalAddress()+entry.toAddend);
+ else
+ sreloc1->set_r_value(entry.toTarget->finalAddress());
+ sreloc2->set_r_scattered(true);
+ sreloc2->set_r_pcrel(false);
+ sreloc2->set_r_length(2);
+ sreloc2->set_r_type(PPC_RELOC_PAIR);
+ sreloc2->set_r_address(0);
+ if ( entry.fromTarget == entry.inAtom ) {
+ if ( entry.fromAddend > entry.fromTarget->size() )
+ sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.offsetInAtom);
+ else
+ sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.fromAddend);
+ }
+ else
+ sreloc2->set_r_value(entry.fromTarget->finalAddress());
+ relocs.push_back(reloc1);
+ relocs.push_back(reloc2);
+ }
+ else {
+ // regular pointer
+ if ( !external && (entry.toAddend != 0) ) {
+ // use scattered reloc is target offset is non-zero
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(GENERIC_RELOC_VANILLA);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(entry.toTarget->finalAddress());
+ }
+ else {
+ 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(GENERIC_RELOC_VANILLA);
+ }
+ relocs.push_back(reloc1);
+ }
+ break;
+
+ case ld::Fixup::kindStoreBigEndian64:
+ case ld::Fixup::kindStoreTargetAddressBigEndian64:
+ if ( entry.fromTarget != NULL ) {
+ // this is a pointer-diff
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(3);
+ if ( entry.toTarget->scope() == ld::Atom::scopeTranslationUnit )
+ sreloc1->set_r_type(PPC_RELOC_LOCAL_SECTDIFF);
+ else
+ sreloc1->set_r_type(PPC_RELOC_SECTDIFF);
+ sreloc1->set_r_address(address);
+ if ( entry.toTarget == entry.inAtom )
+ sreloc1->set_r_value(entry.toTarget->finalAddress()+entry.toAddend);
+ else
+ sreloc1->set_r_value(entry.toTarget->finalAddress());
+ sreloc2->set_r_scattered(true);
+ sreloc2->set_r_pcrel(false);
+ sreloc2->set_r_length(3);
+ sreloc2->set_r_type(PPC_RELOC_PAIR);
+ sreloc2->set_r_address(0);
+ if ( entry.fromTarget == entry.inAtom ) {
+ if ( entry.fromAddend > entry.fromTarget->size() )
+ sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.offsetInAtom);
+ else
+ sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.fromAddend);
+ }
+ else
+ sreloc2->set_r_value(entry.fromTarget->finalAddress());
+ relocs.push_back(reloc1);
+ relocs.push_back(reloc2);
+ }
+ else {
+ // regular pointer
+ if ( !external && (entry.toAddend != 0) ) {
+ // use scattered reloc is target offset is non-zero
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(3);
+ sreloc1->set_r_type(GENERIC_RELOC_VANILLA);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(entry.toTarget->finalAddress());
+ }
+ else {
+ 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(GENERIC_RELOC_VANILLA);
+ }
+ relocs.push_back(reloc1);
+ }
+ break;
+
+ case ld::Fixup::kindStorePPCAbsLow14:
+ case ld::Fixup::kindStorePPCAbsLow16:
+ if ( !external && (entry.toAddend != 0) ) {
+ // use scattered reloc if target offset is non-zero
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(entry.kind==ld::Fixup::kindStorePPCAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(entry.toTarget->finalAddress());
+ }
+ else {
+ 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(entry.kind==ld::Fixup::kindStorePPCAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14);
+ }
+ if ( external )
+ reloc2.set_r_address(entry.toAddend >> 16);
+ else
+ reloc2.set_r_address((entry.toTarget->finalAddress()+entry.toAddend) >> 16);
+ reloc2.set_r_symbolnum(0);
+ reloc2.set_r_pcrel(false);
+ reloc2.set_r_length(2);
+ reloc2.set_r_extern(false);
+ reloc2.set_r_type(PPC_RELOC_PAIR);
+ relocs.push_back(reloc1);
+ relocs.push_back(reloc2);
+ break;
+
+ case ld::Fixup::kindStorePPCAbsHigh16:
+ if ( !external && (entry.toAddend != 0) ) {
+ // use scattered reloc if target offset is non-zero
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(PPC_RELOC_HI16);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(entry.toTarget->finalAddress());
+ }
+ else {
+ 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(PPC_RELOC_HI16);
+ }
+ if ( external )
+ reloc2.set_r_address(entry.toAddend & 0x0000FFFF);
+ else
+ reloc2.set_r_address((entry.toTarget->finalAddress()+entry.toAddend) & 0x0000FFFF);
+ reloc2.set_r_symbolnum(0);
+ reloc2.set_r_pcrel(false);
+ reloc2.set_r_length(2);
+ reloc2.set_r_extern(false);
+ reloc2.set_r_type(PPC_RELOC_PAIR);
+ relocs.push_back(reloc1);
+ relocs.push_back(reloc2);
+ break;
+
+ case ld::Fixup::kindStorePPCAbsHigh16AddLow:
+ if ( !external && (entry.toAddend != 0) ) {
+ // use scattered reloc if target offset is non-zero
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(PPC_RELOC_HA16);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(entry.toTarget->finalAddress());
+ }
+ else {
+ 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(PPC_RELOC_HA16);
+ }
+ if ( external )
+ reloc2.set_r_address(entry.toAddend & 0x0000FFFF);
+ else
+ reloc2.set_r_address((entry.toTarget->finalAddress()+entry.toAddend) & 0x0000FFFF);
+ reloc2.set_r_symbolnum(0);
+ reloc2.set_r_pcrel(false);
+ reloc2.set_r_length(2);
+ reloc2.set_r_extern(false);
+ reloc2.set_r_type(PPC_RELOC_PAIR);
+ relocs.push_back(reloc1);
+ relocs.push_back(reloc2);
+ break;
+
+ case ld::Fixup::kindStorePPCPicLow14:
+ case ld::Fixup::kindStorePPCPicLow16:
+ fromAddr = entry.fromTarget->finalAddress() + entry.fromAddend;
+ toAddr = entry.toTarget->finalAddress() + entry.toAddend;
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(entry.kind == ld::Fixup::kindStorePPCPicLow16 ? PPC_RELOC_LO16_SECTDIFF : PPC_RELOC_LO14_SECTDIFF);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(entry.toTarget->finalAddress());
+ sreloc2->set_r_scattered(true);
+ sreloc2->set_r_pcrel(false);
+ sreloc2->set_r_length(2);
+ sreloc2->set_r_type(PPC_RELOC_PAIR);
+ sreloc2->set_r_address(((toAddr-fromAddr) >> 16) & 0xFFFF);
+ sreloc2->set_r_value(fromAddr);
+ relocs.push_back(reloc1);
+ relocs.push_back(reloc2);
+ break;
+
+ case ld::Fixup::kindStorePPCPicHigh16AddLow:
+ fromAddr = entry.fromTarget->finalAddress() + entry.fromAddend;
+ toAddr = entry.toTarget->finalAddress() + entry.toAddend;
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(PPC_RELOC_HA16_SECTDIFF);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(entry.toTarget->finalAddress());
+ sreloc2->set_r_scattered(true);
+ sreloc2->set_r_pcrel(false);
+ sreloc2->set_r_length(2);
+ sreloc2->set_r_type(PPC_RELOC_PAIR);
+ sreloc2->set_r_address((toAddr-fromAddr) & 0xFFFF);
+ sreloc2->set_r_value(fromAddr);
+ relocs.push_back(reloc1);
+ relocs.push_back(reloc2);
+ break;
+
+ default:
+ assert(0 && "need to handle -r reloc");
+
+ }
+}
+
+template <typename A>
+void SectionRelocationsAtom<A>::addSectionReloc(ld::Internal::FinalSection* sect, ld::Fixup::Kind kind,
+ const ld::Atom* inAtom, uint32_t offsetInAtom,
+ bool toTargetUsesExternalReloc ,bool fromTargetExternalReloc,
+ const ld::Atom* toTarget, uint64_t toAddend,
+ const ld::Atom* fromTarget, uint64_t fromAddend)
+{
+ Entry entry;
+ entry.kind = kind;
+ entry.toTargetUsesExternalReloc = toTargetUsesExternalReloc;
+ entry.fromTargetUsesExternalReloc = fromTargetExternalReloc;
+ entry.inAtom = inAtom;
+ entry.offsetInAtom = offsetInAtom;
+ entry.toTarget = toTarget;
+ entry.toAddend = toAddend;
+ entry.fromTarget = fromTarget;
+ entry.fromAddend = fromAddend;
+
+ static ld::Internal::FinalSection* lastSection = NULL;
+ static SectionAndEntries* lastSectionAndEntries = NULL;
+
+ if ( sect != lastSection ) {
+ for(typename std::vector<SectionAndEntries>::iterator it=_entriesBySection.begin(); it != _entriesBySection.end(); ++it) {
+ if ( sect == it->sect ) {
+ lastSection = sect;
+ lastSectionAndEntries = &*it;
+ break;
+ }
+ }
+ if ( sect != lastSection ) {
+ SectionAndEntries tmp;
+ tmp.sect = sect;
+ _entriesBySection.push_back(tmp);
+ lastSection = sect;
+ lastSectionAndEntries = &_entriesBySection.back();
+ }
+ }
+ lastSectionAndEntries->entries.push_back(entry);
+}
+
+template <typename A>
+void SectionRelocationsAtom<A>::encode()
+{
+ // convert each Entry record to one or two reloc records
+ for(typename std::vector<SectionAndEntries>::iterator it=_entriesBySection.begin(); it != _entriesBySection.end(); ++it) {
+ SectionAndEntries& se = *it;
+ for(typename std::vector<Entry>::iterator eit=se.entries.begin(); eit != se.entries.end(); ++eit) {
+ encodeSectionReloc(se.sect, *eit, se.relocs);
+ }
+ }
+
+ // update sections with start and count or relocs
+ uint32_t index = 0;
+ for(typename std::vector<SectionAndEntries>::iterator it=_entriesBySection.begin(); it != _entriesBySection.end(); ++it) {
+ SectionAndEntries& se = *it;
+ se.sect->relocStart = index;
+ se.sect->relocCount = se.relocs.size();
+ index += se.sect->relocCount;
+ }
+
+}
+
+
+
+template <typename A>
+class IndirectSymbolTableAtom : public ClassicLinkEditAtom
+{
+public:
+ IndirectSymbolTableAtom(const Options& opts, ld::Internal& state, OutputFile& writer)
+ : ClassicLinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) { }
+
+ // overrides of ld::Atom
+ virtual const char* name() const { return "indirect symbol table"; }
+ virtual uint64_t size() const;
+ virtual void copyRawContent(uint8_t buffer[]) const;
+ // overrides of ClassicLinkEditAtom
+ virtual void encode();
+
+private:
+ typedef typename A::P P;
+ typedef typename A::P::E E;
+ typedef typename A::P::uint_t pint_t;
+
+ void encodeStubSection(ld::Internal::FinalSection* sect);
+ void encodeLazyPointerSection(ld::Internal::FinalSection* sect);
+ void encodeNonLazyPointerSection(ld::Internal::FinalSection* sect);
+ uint32_t symIndexOfStubAtom(const ld::Atom*);
+ 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;
+
+ static ld::Section _s_section;
+};
+
+template <typename A>
+ld::Section IndirectSymbolTableAtom<A>::_s_section("__LINKEDIT", "__ind_sym_tab", ld::Section::typeLinkEdit, true);
+
+
+
+
+template <typename A>
+uint32_t IndirectSymbolTableAtom<A>::symbolIndex(const ld::Atom* atom)
+{
+ std::map<const ld::Atom*, uint32_t>::iterator pos = this->_writer._atomToSymbolIndex.find(atom);
+ if ( pos != this->_writer._atomToSymbolIndex.end() )
+ return pos->second;
+ //fprintf(stderr, "_atomToSymbolIndex content:\n");
+ //for(std::map<const ld::Atom*, uint32_t>::iterator it = this->_writer._atomToSymbolIndex.begin(); it != this->_writer._atomToSymbolIndex.end(); ++it) {
+ // fprintf(stderr, "%p(%s) => %d\n", it->first, it->first->name(), it->second);
+ //}
+ throwf("internal error: atom not found in symbolIndex(%s)", atom->name());
+}
+
+template <typename A>
+uint32_t IndirectSymbolTableAtom<A>::symIndexOfStubAtom(const ld::Atom* stubAtom)
+{
+ 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);
+ }
+ }
+ throw "internal error: stub missing fixup to lazy pointer";
+}
+
+
+template <typename A>
+uint32_t IndirectSymbolTableAtom<A>::symIndexOfLazyPointerAtom(const ld::Atom* lpAtom)
+{
+ for (ld::Fixup::iterator fit = lpAtom->fixupsBegin(); fit != lpAtom->fixupsEnd(); ++fit) {
+ if ( fit->kind == ld::Fixup::kindLazyTarget ) {
+ assert(fit->binding == ld::Fixup::bindingDirectlyBound);
+ return symbolIndex(fit->u.target);
+ }
+ }
+ throw "internal error: lazy pointer missing fixupLazyTarget fixup";
+}
+
+template <typename A>
+uint32_t IndirectSymbolTableAtom<A>::symIndexOfNonLazyPointerAtom(const ld::Atom* nlpAtom)
+{
+ //fprintf(stderr, "symIndexOfNonLazyPointerAtom(%p) %s\n", nlpAtom, nlpAtom->name());
+ for (ld::Fixup::iterator fit = nlpAtom->fixupsBegin(); fit != nlpAtom->fixupsEnd(); ++fit) {
+ // non-lazy-pointer to a stripped symbol => no symbol index
+ if ( fit->clusterSize != ld::Fixup::k1of1 )
+ return INDIRECT_SYMBOL_LOCAL;
+ const ld::Atom* target;
+ switch ( fit->binding ) {
+ case ld::Fixup::bindingDirectlyBound:
+ target = fit->u.target;
+ break;
+ case ld::Fixup::bindingsIndirectlyBound:
+ target = _state.indirectBindingTable[fit->u.bindingIndex];
+ break;
+ default:
+ throw "internal error: unexpected non-lazy pointer binding";
+ }
+ // Special case non-lazy-pointer slot used to point to "dyld_stub_binder"
+ // That slot is never bound using indirect symbol table
+ if ( target == _state.compressedFastBinderProxy )
+ return INDIRECT_SYMBOL_ABS;
+ bool targetIsGlobal = (target->scope() == ld::Atom::scopeGlobal);
+ switch ( target->definition() ) {
+ case ld::Atom::definitionRegular:
+ if ( targetIsGlobal ) {
+ if ( _options.outputKind() == Options::kObjectFile ) {
+ // nlpointer to global symbol uses indirect symbol table in .o files
+ return symbolIndex(target);
+ }
+ else if ( target->combine() == ld::Atom::combineByName ) {
+ // dyld needs to bind nlpointer to global weak def
+ return symbolIndex(target);
+ }
+ else if ( _options.nameSpace() != Options::kTwoLevelNameSpace ) {
+ // dyld needs to bind nlpointer to global def linked for flat namespace
+ return symbolIndex(target);
+ }
+ }
+ break;
+ case ld::Atom::definitionTentative:
+ case ld::Atom::definitionAbsolute:
+ if ( _options.outputKind() == Options::kObjectFile ) {
+ // tentative def in .o file always uses symbol index
+ return symbolIndex(target);
+ }
+ // dyld needs to bind nlpointer to global def linked for flat namespace
+ if ( targetIsGlobal && _options.nameSpace() != Options::kTwoLevelNameSpace )
+ return symbolIndex(target);
+ break;
+ case ld::Atom::definitionProxy:
+ // dyld needs to bind nlpointer to something in another dylib
+ {
+ const ld::dylib::File* dylib = dynamic_cast<const ld::dylib::File*>(target->file());
+ if ( (dylib != NULL) && dylib->willBeLazyLoadedDylib() )
+ throwf("illegal data reference to %s in lazy loaded dylib %s", target->name(), dylib->path());
+ }
+ return symbolIndex(target);
+ }
+ }
+ if ( nlpAtom->fixupsBegin() == nlpAtom->fixupsEnd() ) {
+ // no fixups means this is the ImageLoader cache slot
+ return INDIRECT_SYMBOL_ABS;
+ }
+
+ // The magic index INDIRECT_SYMBOL_LOCAL tells dyld it should does not need to bind
+ // this non-lazy pointer.
+ return INDIRECT_SYMBOL_LOCAL;
+}
+
+
+
+template <typename A>
+void IndirectSymbolTableAtom<A>::encodeStubSection(ld::Internal::FinalSection* sect)
+{
+ sect->indirectSymTabStartIndex = _entries.size();
+ sect->indirectSymTabElementSize = sect->atoms[0]->size();
+ for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
+ _entries.push_back(symIndexOfStubAtom(*ait));
+ }
+}
+
+template <typename A>
+void IndirectSymbolTableAtom<A>::encodeLazyPointerSection(ld::Internal::FinalSection* sect)
+{
+ sect->indirectSymTabStartIndex = _entries.size();
+ for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
+ _entries.push_back(symIndexOfLazyPointerAtom(*ait));
+ }
+}
+
+template <typename A>
+void IndirectSymbolTableAtom<A>::encodeNonLazyPointerSection(ld::Internal::FinalSection* sect)
+{
+ sect->indirectSymTabStartIndex = _entries.size();
+ for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
+ _entries.push_back(symIndexOfNonLazyPointerAtom(*ait));
+ }
+}
+
+template <typename A>
+bool IndirectSymbolTableAtom<A>::kextBundlesDontHaveIndirectSymbolTable()
+{
+ return true;
+}
+
+template <typename A>
+void IndirectSymbolTableAtom<A>::encode()
+{
+ // static executables should not have an indirect symbol table
+ if ( this->_options.outputKind() == Options::kStaticExecutable )
+ return;
+
+ // x86_64 kext bundles should not have an indirect symbol table
+ if ( (this->_options.outputKind() == Options::kKextBundle) && kextBundlesDontHaveIndirectSymbolTable() )
+ return;
+
+ // find all special sections that need a range of the indirect symbol table section
+ for (std::vector<ld::Internal::FinalSection*>::iterator sit = this->_state.sections.begin(); sit != this->_state.sections.end(); ++sit) {
+ ld::Internal::FinalSection* sect = *sit;
+ switch ( sect->type() ) {
+ case ld::Section::typeStub:
+ case ld::Section::typeStubClose:
+ this->encodeStubSection(sect);
+ break;
+ case ld::Section::typeLazyPointerClose:
+ case ld::Section::typeLazyPointer:
+ case ld::Section::typeLazyDylibPointer:
+ this->encodeLazyPointerSection(sect);
+ break;
+ case ld::Section::typeNonLazyPointer:
+ this->encodeNonLazyPointerSection(sect);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+template <typename A>
+uint64_t IndirectSymbolTableAtom<A>::size() const
+{
+ return _entries.size() * sizeof(uint32_t);
+}
+
+template <typename A>
+void IndirectSymbolTableAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ uint32_t* array = (uint32_t*)buffer;
+ for(unsigned long i=0; i < _entries.size(); ++i) {
+ E::set32(array[i], _entries[i]);
+ }
+}
+
+
+
+
+
+
+
+
+} // namespace tool
+} // namespace ld
+
+#endif // __LINKEDIT_CLASSIC_HPP__