MachOWriterExecutable.hpp [plain text]
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
* Copyright (c) 2005-2006 Apple Computer, 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 __EXECUTABLE_MACH_O__
#define __EXECUTABLE_MACH_O__
#include <stdint.h>
#include <stddef.h>
#include <fcntl.h>
#include <sys/time.h>
#include <mach-o/loader.h>
#include <mach-o/nlist.h>
#include <mach-o/reloc.h>
//#include <mach-o/ppc/reloc.h>
#include <mach-o/stab.h>
#include <uuid/uuid.h>
#include <mach/i386/thread_status.h>
#include <mach/ppc/thread_status.h>
#include <vector>
#include <algorithm>
#include <map>
#include <set>
#include <ext/hash_map>
#include "ObjectFile.h"
#include "ExecutableFile.h"
#include "Options.h"
#include "MachOFileAbstraction.hpp"
//
//
// To implement architecture xxx, you must write template specializations for the following methods:
// MachHeaderAtom<xxx>::setHeaderInfo()
// ThreadsLoadCommandsAtom<xxx>::getSize()
// ThreadsLoadCommandsAtom<xxx>::copyRawContent()
// Writer<xxx>::addObjectRelocs()
// Writer<xxx>::fixUpReferenceRelocatable()
// Writer<xxx>::fixUpReferenceFinal()
// Writer<xxx>::stubableReferenceKind()
// Writer<xxx>::weakImportReferenceKind()
// Writer<xxx>::GOTReferenceKind()
//
namespace mach_o {
namespace executable {
// forward references
template <typename A> class WriterAtom;
template <typename A> class PageZeroAtom;
template <typename A> class CustomStackAtom;
template <typename A> class MachHeaderAtom;
template <typename A> class SegmentLoadCommandsAtom;
template <typename A> class SymbolTableLoadCommandsAtom;
template <typename A> class ThreadsLoadCommandsAtom;
template <typename A> class DylibIDLoadCommandsAtom;
template <typename A> class RoutinesLoadCommandsAtom;
template <typename A> class DyldLoadCommandsAtom;
template <typename A> class UUIDLoadCommandAtom;
template <typename A> class LinkEditAtom;
template <typename A> class SectionRelocationsLinkEditAtom;
template <typename A> class LocalRelocationsLinkEditAtom;
template <typename A> class ExternalRelocationsLinkEditAtom;
template <typename A> class SymbolTableLinkEditAtom;
template <typename A> class IndirectTableLinkEditAtom;
template <typename A> class StringsLinkEditAtom;
template <typename A> class LoadCommandsPaddingAtom;
template <typename A> class StubAtom;
template <typename A> class StubHelperAtom;
template <typename A> class LazyPointerAtom;
template <typename A> class NonLazyPointerAtom;
// SectionInfo should be nested inside Writer, but I can't figure out how to make the type accessible to the Atom classes
class SectionInfo : public ObjectFile::Section {
public:
SectionInfo() : fFileOffset(0), fSize(0), fRelocCount(0), fRelocOffset(0), fIndirectSymbolOffset(0),
fAlignment(0), fAllLazyPointers(false), fAllNonLazyPointers(false), fAllStubs(false),
fAllSelfModifyingStubs(false), fAllZeroFill(false), fVirtualSection(false)
{ fSegmentName[0] = '\0'; fSectionName[0] = '\0'; }
void setIndex(unsigned int index) { fIndex=index; }
std::vector<ObjectFile::Atom*> fAtoms;
char fSegmentName[20];
char fSectionName[20];
uint64_t fFileOffset;
uint64_t fSize;
uint32_t fRelocCount;
uint32_t fRelocOffset;
uint32_t fIndirectSymbolOffset;
uint8_t fAlignment;
bool fAllLazyPointers;
bool fAllNonLazyPointers;
bool fAllStubs;
bool fAllSelfModifyingStubs;
bool fAllZeroFill;
bool fVirtualSection;
};
// SegmentInfo should be nested inside Writer, but I can't figure out how to make the type accessible to the Atom classes
class SegmentInfo
{
public:
SegmentInfo() : fInitProtection(0), fMaxProtection(0), fFileOffset(0), fFileSize(0),
fBaseAddress(0), fSize(0), fFixedAddress(false) { fName[0] = '\0'; }
std::vector<class SectionInfo*> fSections;
char fName[20];
uint32_t fInitProtection;
uint32_t fMaxProtection;
uint64_t fFileOffset;
uint64_t fFileSize;
uint64_t fBaseAddress;
uint64_t fSize;
bool fFixedAddress;
};
template <typename A>
class Writer : public ExecutableFile::Writer
{
public:
Writer(const char* path, Options& options, std::vector<ExecutableFile::DyLibUsed>& dynamicLibraries);
virtual ~Writer();
virtual const char* getPath() { return fFilePath; }
virtual time_t getModificationTime() { return 0; }
virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; }
virtual std::vector<class ObjectFile::Atom*>& getAtoms() { return fWriterSynthesizedAtoms; }
virtual std::vector<class ObjectFile::Atom*>* getJustInTimeAtomsFor(const char* name) { return NULL; }
virtual std::vector<Stab>* getStabs() { return NULL; }
virtual class ObjectFile::Atom* getUndefinedProxyAtom(const char* name);
virtual uint64_t write(std::vector<class ObjectFile::Atom*>& atoms,
std::vector<class ObjectFile::Reader::Stab>& stabs,
class ObjectFile::Atom* entryPointAtom,
class ObjectFile::Atom* dyldHelperAtom,
bool createUUID);
private:
typedef typename A::P P;
typedef typename A::P::uint_t pint_t;
enum RelocKind { kRelocNone, kRelocInternal, kRelocExternal };
void assignFileOffsets();
void synthesizeStubs();
void partitionIntoSections();
bool addBranchIslands();
bool addPPCBranchIslands();
uint8_t branch24Reference();
void adjustLoadCommandsAndPadding();
void createDynamicLinkerCommand();
void createDylibCommands();
void buildLinkEdit();
uint64_t writeAtoms();
void writeNoOps(uint32_t from, uint32_t to);
void collectExportedAndImportedAndLocalAtoms();
void setNlistRange(std::vector<class ObjectFile::Atom*>& atoms, uint32_t startIndex, uint32_t count);
void buildSymbolTable();
void setExportNlist(const ObjectFile::Atom* atom, macho_nlist<P>* entry);
void setImportNlist(const ObjectFile::Atom* atom, macho_nlist<P>* entry);
void setLocalNlist(const ObjectFile::Atom* atom, macho_nlist<P>* entry);
uint64_t getAtomLoadAddress(const ObjectFile::Atom* atom);
uint8_t ordinalForLibrary(ObjectFile::Reader* file);
bool shouldExport(const ObjectFile::Atom& atom) const;
void buildFixups();
void adjustLinkEditSections();
void buildObjectFileFixups();
void buildExecutableFixups();
uint64_t relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const;
bool referenceRequiresRuntimeFixUp(const ObjectFile::Reference* ref, bool slideable) const;
void fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const;
void fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const;
void fixUpReference_powerpc(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom,
uint8_t buffer[], bool finalLinkedImage) const;
uint32_t symbolIndex(ObjectFile::Atom& atom);
uint32_t addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref);
uint32_t addObjectRelocs_powerpc(ObjectFile::Atom* atom, ObjectFile::Reference* ref);
uint8_t getRelocPointerSize();
bool stubableReferenceKind(uint8_t kind);
bool GOTReferenceKind(uint8_t kind);
bool weakImportReferenceKind(uint8_t kind);
unsigned int collectStabs();
uint64_t valueForStab(const ObjectFile::Reader::Stab& stab);
uint32_t stringOffsetForStab(const ObjectFile::Reader::Stab& stab);
uint8_t sectionIndexForStab(const ObjectFile::Reader::Stab& stab);
void addStabs(uint32_t startIndex);
RelocKind relocationNeededInFinalLinkedImage(const ObjectFile::Atom& target) const;
bool illegalRelocInFinalLinkedImage(const ObjectFile::Reference&, bool slideable);
bool mightNeedPadSegment();
void scanForAbsoluteReferences();
struct DirectLibrary {
class ObjectFile::Reader* fLibrary;
bool fWeak;
bool fReExport;
};
friend class WriterAtom<A>;
friend class PageZeroAtom<A>;
friend class CustomStackAtom<A>;
friend class MachHeaderAtom<A>;
friend class SegmentLoadCommandsAtom<A>;
friend class SymbolTableLoadCommandsAtom<A>;
friend class ThreadsLoadCommandsAtom<A>;
friend class DylibIDLoadCommandsAtom<A>;
friend class RoutinesLoadCommandsAtom<A>;
friend class DyldLoadCommandsAtom<A>;
friend class UUIDLoadCommandAtom<A>;
friend class LinkEditAtom<A>;
friend class SectionRelocationsLinkEditAtom<A>;
friend class LocalRelocationsLinkEditAtom<A>;
friend class ExternalRelocationsLinkEditAtom<A>;
friend class SymbolTableLinkEditAtom<A>;
// friend class IndirectTableLinkEditAtom<A>;
friend class StringsLinkEditAtom<A>;
friend class LoadCommandsPaddingAtom<A>;
friend class StubAtom<A>;
friend class StubHelperAtom<A>;
friend class LazyPointerAtom<A>;
friend class NonLazyPointerAtom<A>;
const char* fFilePath;
Options& fOptions;
int fFileDescriptor;
std::vector<class ObjectFile::Atom*>* fAllAtoms;
std::vector<class ObjectFile::Reader::Stab>* fStabs;
class SectionInfo* fLoadCommandsSection;
class SegmentInfo* fLoadCommandsSegment;
class SegmentLoadCommandsAtom<A>* fSegmentCommands;
class SymbolTableLoadCommandsAtom<A>* fSymbolTableCommands;
class LoadCommandsPaddingAtom<A>* fHeaderPadding;
class UUIDLoadCommandAtom<A>* fUUIDAtom;
std::vector<class ObjectFile::Atom*> fWriterSynthesizedAtoms;
std::vector<SegmentInfo*> fSegmentInfos;
class SegmentInfo* fPadSegmentInfo;
class ObjectFile::Atom* fEntryPoint;
class ObjectFile::Atom* fDyldHelper;
std::vector<DirectLibrary> fDirectLibraries;
std::map<class ObjectFile::Reader*, uint32_t> fLibraryToOrdinal;
std::vector<class ObjectFile::Atom*> fExportedAtoms;
std::vector<class ObjectFile::Atom*> fImportedAtoms;
std::vector<class ObjectFile::Atom*> fLocalSymbolAtoms;
class SectionRelocationsLinkEditAtom<A>* fSectionRelocationsAtom;
class LocalRelocationsLinkEditAtom<A>* fLocalRelocationsAtom;
class ExternalRelocationsLinkEditAtom<A>* fExternalRelocationsAtom;
class SymbolTableLinkEditAtom<A>* fSymbolTableAtom;
class IndirectTableLinkEditAtom<A>* fIndirectTableAtom;
class StringsLinkEditAtom<A>* fStringsAtom;
class PageZeroAtom<A>* fPageZeroAtom;
macho_nlist<P>* fSymbolTable;
std::vector<macho_relocation_info<P> > fSectionRelocs;
std::vector<macho_relocation_info<P> > fInternalRelocs;
std::vector<macho_relocation_info<P> > fExternalRelocs;
std::map<ObjectFile::Atom*,ObjectFile::Atom*> fStubsMap;
std::map<ObjectFile::Atom*,ObjectFile::Atom*> fGOTMap;
std::vector<class StubAtom<A>*> fAllSynthesizedStubs;
std::vector<ObjectFile::Atom*> fAllSynthesizedStubHelpers;
std::vector<class LazyPointerAtom<A>*> fAllSynthesizedLazyPointers;
std::vector<class NonLazyPointerAtom<A>*> fAllSynthesizedNonLazyPointers;
uint32_t fSymbolTableCount;
uint32_t fSymbolTableStabsCount;
uint32_t fSymbolTableStabsStartIndex;
uint32_t fSymbolTableLocalCount;
uint32_t fSymbolTableLocalStartIndex;
uint32_t fSymbolTableExportCount;
uint32_t fSymbolTableExportStartIndex;
uint32_t fSymbolTableImportCount;
uint32_t fSymbolTableImportStartIndex;
uint32_t fLargestAtomSize;
bool fEmitVirtualSections;
bool fHasWeakExports;
bool fReferencesWeakImports;
bool fSeenFollowOnReferences;
bool fWritableSegmentPastFirst4GB;
std::map<const ObjectFile::Atom*,bool> fWeakImportMap;
SegmentInfo* fFirstWritableSegment;
};
class Segment : public ObjectFile::Segment
{
public:
Segment(const char* name, bool readable, bool writable, bool executable, bool fixedAddress)
: fName(name), fReadable(readable), fWritable(writable), fExecutable(executable), fFixedAddress(fixedAddress) {}
virtual const char* getName() const { return fName; }
virtual bool isContentReadable() const { return fReadable; }
virtual bool isContentWritable() const { return fWritable; }
virtual bool isContentExecutable() const { return fExecutable; }
virtual bool hasFixedAddress() const { return fFixedAddress; }
static Segment fgTextSegment;
static Segment fgPageZeroSegment;
static Segment fgLinkEditSegment;
static Segment fgStackSegment;
static Segment fgImportSegment;
static Segment fgDataSegment;
private:
const char* fName;
const bool fReadable;
const bool fWritable;
const bool fExecutable;
const bool fFixedAddress;
};
Segment Segment::fgPageZeroSegment("__PAGEZERO", false, false, false, true);
Segment Segment::fgTextSegment("__TEXT", true, false, true, false);
Segment Segment::fgLinkEditSegment("__LINKEDIT", true, false, false, false);
Segment Segment::fgStackSegment("__UNIXSTACK", true, true, false, true);
Segment Segment::fgImportSegment("__IMPORT", true, true, true, false);
Segment Segment::fgDataSegment("__DATA", true, true, false, false);
template <typename A>
class WriterAtom : public ObjectFile::Atom
{
public:
enum Kind { zeropage, machHeaderApp, machHeaderDylib, machHeaderBundle, machHeaderObject, loadCommands, undefinedProxy };
WriterAtom(Writer<A>& writer, Segment& segment) : fWriter(writer), fSegment(segment) { }
virtual ObjectFile::Reader* getFile() const { return &fWriter; }
virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; }
virtual const char* getName() const { return NULL; }
virtual const char* getDisplayName() const { return this->getName(); }
virtual Scope getScope() const { return ObjectFile::Atom::scopeTranslationUnit; }
virtual DefinitionKind getDefinitionKind() const { return kRegularDefinition; }
virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; }
virtual bool dontDeadStrip() const { return true; }
virtual bool isZeroFill() const { return false; }
virtual std::vector<ObjectFile::Reference*>& getReferences() const { return fgEmptyReferenceList; }
virtual bool mustRemainInSection() const { return true; }
virtual ObjectFile::Segment& getSegment() const { return fSegment; }
virtual bool requiresFollowOnAtom() const { return false; }
virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); }
virtual std::vector<ObjectFile::LineInfo>* getLineInfo() const { return NULL; }
virtual uint8_t getAlignment() const { return 2; }
virtual void copyRawContent(uint8_t buffer[]) const { throw "don't use copyRawContent"; }
virtual void setScope(Scope) { }
protected:
virtual ~WriterAtom() {}
typedef typename A::P P;
typedef typename A::P::E E;
static std::vector<ObjectFile::Reference*> fgEmptyReferenceList;
Writer<A>& fWriter;
Segment& fSegment;
};
template <typename A> std::vector<ObjectFile::Reference*> WriterAtom<A>::fgEmptyReferenceList;
template <typename A>
class PageZeroAtom : public WriterAtom<A>
{
public:
PageZeroAtom(Writer<A>& writer) : WriterAtom<A>(writer, Segment::fgPageZeroSegment),
fSize(fWriter.fOptions.zeroPageSize()) {}
virtual const char* getDisplayName() const { return "page zero content"; }
virtual bool isZeroFill() const { return true; }
virtual uint64_t getSize() const { return fSize; }
virtual const char* getSectionName() const { return "._zeropage"; }
virtual uint8_t getAlignment() const { return 12; }
void setSize(uint64_t size) { fSize = size; }
private:
using WriterAtom<A>::fWriter;
typedef typename A::P P;
uint64_t fSize;
};
template <typename A>
class DsoHandleAtom : public WriterAtom<A>
{
public:
DsoHandleAtom(Writer<A>& writer) : WriterAtom<A>(writer, Segment::fgTextSegment) {}
virtual const char* getName() const { return "___dso_handle"; }
virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; }
virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; }
virtual uint64_t getSize() const { return 0; }
virtual uint8_t getAlignment() const { return 12; }
virtual const char* getSectionName() const { return "._mach_header"; }
virtual void copyRawContent(uint8_t buffer[]) const {}
};
template <typename A>
class MachHeaderAtom : public WriterAtom<A>
{
public:
MachHeaderAtom(Writer<A>& writer) : WriterAtom<A>(writer, Segment::fgTextSegment) {}
virtual const char* getName() const;
virtual const char* getDisplayName() const;
virtual ObjectFile::Atom::Scope getScope() const;
virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const;
virtual uint64_t getSize() const { return sizeof(macho_header<typename A::P>); }
virtual uint8_t getAlignment() const { return 12; }
virtual const char* getSectionName() const { return "._mach_header"; }
virtual void copyRawContent(uint8_t buffer[]) const;
private:
using WriterAtom<A>::fWriter;
typedef typename A::P P;
void setHeaderInfo(macho_header<typename A::P>& header) const;
};
template <typename A>
class CustomStackAtom : public WriterAtom<A>
{
public:
CustomStackAtom(Writer<A>& writer);
virtual const char* getDisplayName() const { return "custom stack content"; }
virtual bool isZeroFill() const { return true; }
virtual uint64_t getSize() const { return fWriter.fOptions.customStackSize(); }
virtual const char* getSectionName() const { return "._stack"; }
virtual uint8_t getAlignment() const { return 12; }
private:
using WriterAtom<A>::fWriter;
typedef typename A::P P;
static bool stackGrowsDown();
};
template <typename A>
class LoadCommandAtom : public WriterAtom<A>
{
protected:
LoadCommandAtom(Writer<A>& writer, Segment& segment) : WriterAtom<A>(writer, segment) {}
static uint64_t alignedSize(uint64_t size);
};
template <typename A>
class SegmentLoadCommandsAtom : public LoadCommandAtom<A>
{
public:
SegmentLoadCommandsAtom(Writer<A>& writer)
: LoadCommandAtom<A>(writer, Segment::fgTextSegment), fCommandCount(0), fSize(0)
{ writer.fSegmentCommands = this; }
virtual const char* getDisplayName() const { return "segment load commands"; }
virtual uint64_t getSize() const { return fSize; }
virtual uint8_t getAlignment() const { return 2; }
virtual const char* getSectionName() const { return "._load_commands"; }
virtual void copyRawContent(uint8_t buffer[]) const;
void computeSize();
void setup();
unsigned int commandCount() { return fCommandCount; }
private:
using WriterAtom<A>::fWriter;
typedef typename A::P P;
unsigned int fCommandCount;
uint32_t fSize;
};
template <typename A>
class SymbolTableLoadCommandsAtom : public LoadCommandAtom<A>
{
public:
SymbolTableLoadCommandsAtom(Writer<A>&);
virtual const char* getDisplayName() const { return "symbol table load commands"; }
virtual uint64_t getSize() const;
virtual uint8_t getAlignment() const { return 2; }
virtual const char* getSectionName() const { return "._load_commands"; }
virtual void copyRawContent(uint8_t buffer[]) const;
unsigned int commandCount();
private:
using WriterAtom<A>::fWriter;
typedef typename A::P P;
macho_symtab_command<typename A::P> fSymbolTable;
macho_dysymtab_command<typename A::P> fDynamicSymbolTable;
};
template <typename A>
class ThreadsLoadCommandsAtom : public LoadCommandAtom<A>
{
public:
ThreadsLoadCommandsAtom(Writer<A>& writer)
: LoadCommandAtom<A>(writer, Segment::fgTextSegment) {}
virtual const char* getDisplayName() const { return "thread load commands"; }
virtual uint64_t getSize() const;
virtual uint8_t getAlignment() const { return 2; }
virtual const char* getSectionName() const { return "._load_commands"; }
virtual void copyRawContent(uint8_t buffer[]) const;
private:
using WriterAtom<A>::fWriter;
typedef typename A::P P;
uint8_t* fBuffer;
uint32_t fBufferSize;
};
template <typename A>
class DyldLoadCommandsAtom : public LoadCommandAtom<A>
{
public:
DyldLoadCommandsAtom(Writer<A>& writer) : LoadCommandAtom<A>(writer, Segment::fgTextSegment) {}
virtual const char* getDisplayName() const { return "dyld load command"; }
virtual uint64_t getSize() const;
virtual uint8_t getAlignment() const { return 2; }
virtual const char* getSectionName() const { return "._load_commands"; }
virtual void copyRawContent(uint8_t buffer[]) const;
private:
using WriterAtom<A>::fWriter;
typedef typename A::P P;
};
template <typename A>
class AllowableClientLoadCommandsAtom : public LoadCommandAtom<A>
{
public:
AllowableClientLoadCommandsAtom(Writer<A>& writer, const char* client) :
LoadCommandAtom<A>(writer, Segment::fgTextSegment), clientString(client) {}
virtual const char* getDisplayName() const { return "allowable_client load command"; }
virtual uint64_t getSize() const;
virtual uint8_t getAlignment() const { return 2; }
virtual const char* getSectionName() const { return "._load_commands"; }
virtual void copyRawContent(uint8_t buffer[]) const;
private:
using WriterAtom<A>::fWriter;
typedef typename A::P P;
const char* clientString;
};
template <typename A>
class DylibLoadCommandsAtom : public LoadCommandAtom<A>
{
public:
DylibLoadCommandsAtom(Writer<A>& writer, ExecutableFile::DyLibUsed& info)
: LoadCommandAtom<A>(writer, Segment::fgTextSegment), fInfo(info) {}
virtual const char* getDisplayName() const { return "dylib load command"; }
virtual uint64_t getSize() const;
virtual uint8_t getAlignment() const { return 2; }
virtual const char* getSectionName() const { return "._load_commands"; }
virtual void copyRawContent(uint8_t buffer[]) const;
private:
using WriterAtom<A>::fWriter;
typedef typename A::P P;
ExecutableFile::DyLibUsed& fInfo;
};
template <typename A>
class DylibIDLoadCommandsAtom : public LoadCommandAtom<A>
{
public:
DylibIDLoadCommandsAtom(Writer<A>& writer) : LoadCommandAtom<A>(writer, Segment::fgTextSegment) {}
virtual const char* getDisplayName() const { return "dylib ID load command"; }
virtual uint64_t getSize() const;
virtual uint8_t getAlignment() const { return 2; }
virtual const char* getSectionName() const { return "._load_commands"; }
virtual void copyRawContent(uint8_t buffer[]) const;
private:
using WriterAtom<A>::fWriter;
typedef typename A::P P;
};
template <typename A>
class RoutinesLoadCommandsAtom : public LoadCommandAtom<A>
{
public:
RoutinesLoadCommandsAtom(Writer<A>& writer) : LoadCommandAtom<A>(writer, Segment::fgTextSegment) {}
virtual const char* getDisplayName() const { return "routines load command"; }
virtual uint64_t getSize() const { return sizeof(macho_routines_command<typename A::P>); }
virtual uint8_t getAlignment() const { return 2; }
virtual const char* getSectionName() const { return "._load_commands"; }
virtual void copyRawContent(uint8_t buffer[]) const;
private:
using WriterAtom<A>::fWriter;
typedef typename A::P P;
};
template <typename A>
class SubUmbrellaLoadCommandsAtom : public LoadCommandAtom<A>
{
public:
SubUmbrellaLoadCommandsAtom(Writer<A>& writer, const char* name)
: LoadCommandAtom<A>(writer, Segment::fgTextSegment), fName(name) {}
virtual const char* getDisplayName() const { return "sub-umbrella load command"; }
virtual uint64_t getSize() const;
virtual uint8_t getAlignment() const { return 2; }
virtual const char* getSectionName() const { return "._load_commands"; }
virtual void copyRawContent(uint8_t buffer[]) const;
private:
typedef typename A::P P;
const char* fName;
};
template <typename A>
class SubLibraryLoadCommandsAtom : public LoadCommandAtom<A>
{
public:
SubLibraryLoadCommandsAtom(Writer<A>& writer, const char* nameStart, int nameLen)
: LoadCommandAtom<A>(writer, Segment::fgTextSegment), fNameStart(nameStart), fNameLength(nameLen) {}
virtual const char* getDisplayName() const { return "sub-library load command"; }
virtual uint64_t getSize() const;
virtual uint8_t getAlignment() const { return 2; }
virtual const char* getSectionName() const { return "._load_commands"; }
virtual void copyRawContent(uint8_t buffer[]) const;
private:
using WriterAtom<A>::fWriter;
typedef typename A::P P;
const char* fNameStart;
int fNameLength;
};
template <typename A>
class UmbrellaLoadCommandsAtom : public LoadCommandAtom<A>
{
public:
UmbrellaLoadCommandsAtom(Writer<A>& writer, const char* name)
: LoadCommandAtom<A>(writer, Segment::fgTextSegment), fName(name) {}
virtual const char* getDisplayName() const { return "umbrella load command"; }
virtual uint64_t getSize() const;
virtual uint8_t getAlignment() const { return 2; }
virtual const char* getSectionName() const { return "._load_commands"; }
virtual void copyRawContent(uint8_t buffer[]) const;
private:
using WriterAtom<A>::fWriter;
typedef typename A::P P;
const char* fName;
};
template <typename A>
class UUIDLoadCommandAtom : public LoadCommandAtom<A>
{
public:
UUIDLoadCommandAtom(Writer<A>& writer)
: LoadCommandAtom<A>(writer, Segment::fgTextSegment), fEmit(false) { ::uuid_generate_random(fUUID);}
virtual const char* getDisplayName() const { return "uuid load command"; }
virtual uint64_t getSize() const { return fEmit ? sizeof(macho_uuid_command<typename A::P>) : 0; }
virtual uint8_t getAlignment() const { return 2; }
virtual const char* getSectionName() const { return "._load_commands"; }
virtual void copyRawContent(uint8_t buffer[]) const;
virtual void emit() { fEmit = true; }
private:
using WriterAtom<A>::fWriter;
typedef typename A::P P;
uuid_t fUUID;
bool fEmit;
};
template <typename A>
class LoadCommandsPaddingAtom : public WriterAtom<A>
{
public:
LoadCommandsPaddingAtom(Writer<A>& writer)
: WriterAtom<A>(writer, Segment::fgTextSegment), fSize(0) {}
virtual const char* getDisplayName() const { return "header padding"; }
virtual uint64_t getSize() const { return fSize; }
virtual uint8_t getAlignment() const { return 2; }
virtual const char* getSectionName() const { return "._load_cmds_pad"; }
virtual void copyRawContent(uint8_t buffer[]) const;
void setSize(uint64_t newSize);
private:
using WriterAtom<A>::fWriter;
typedef typename A::P P;
uint64_t fSize;
};
template <typename A>
class LinkEditAtom : public WriterAtom<A>
{
public:
LinkEditAtom(Writer<A>& writer) : WriterAtom<A>(writer, Segment::fgLinkEditSegment) {}
uint64_t getFileOffset() const;
private:
typedef typename A::P P;
};
template <typename A>
class SectionRelocationsLinkEditAtom : public LinkEditAtom<A>
{
public:
SectionRelocationsLinkEditAtom(Writer<A>& writer) : LinkEditAtom<A>(writer) { }
virtual const char* getDisplayName() const { return "section relocations"; }
virtual uint64_t getSize() const;
virtual uint8_t getAlignment() const { return 3; }
virtual const char* getSectionName() const { return "._section_relocs"; }
virtual void copyRawContent(uint8_t buffer[]) const;
private:
using WriterAtom<A>::fWriter;
typedef typename A::P P;
};
template <typename A>
class LocalRelocationsLinkEditAtom : public LinkEditAtom<A>
{
public:
LocalRelocationsLinkEditAtom(Writer<A>& writer) : LinkEditAtom<A>(writer) { }
virtual const char* getDisplayName() const { return "local relocations"; }
virtual uint64_t getSize() const;
virtual uint8_t getAlignment() const { return 3; }
virtual const char* getSectionName() const { return "._local_relocs"; }
virtual void copyRawContent(uint8_t buffer[]) const;
private:
using WriterAtom<A>::fWriter;
typedef typename A::P P;
};
template <typename A>
class SymbolTableLinkEditAtom : public LinkEditAtom<A>
{
public:
SymbolTableLinkEditAtom(Writer<A>& writer) : LinkEditAtom<A>(writer) { }
virtual const char* getDisplayName() const { return "symbol table"; }
virtual uint64_t getSize() const;
virtual uint8_t getAlignment() const { return 2; }
virtual const char* getSectionName() const { return "._symbol_table"; }
virtual void copyRawContent(uint8_t buffer[]) const;
private:
using WriterAtom<A>::fWriter;
typedef typename A::P P;
};
template <typename A>
class ExternalRelocationsLinkEditAtom : public LinkEditAtom<A>
{
public:
ExternalRelocationsLinkEditAtom(Writer<A>& writer) : LinkEditAtom<A>(writer) { }
virtual const char* getDisplayName() const { return "external relocations"; }
virtual uint64_t getSize() const;
virtual uint8_t getAlignment() const { return 3; }
virtual const char* getSectionName() const { return "._extern_relocs"; }
virtual void copyRawContent(uint8_t buffer[]) const;
private:
using WriterAtom<A>::fWriter;
typedef typename A::P P;
};
struct IndirectEntry {
uint32_t indirectIndex;
uint32_t symbolIndex;
};
template <typename A>
class IndirectTableLinkEditAtom : public LinkEditAtom<A>
{
public:
IndirectTableLinkEditAtom(Writer<A>& writer) : LinkEditAtom<A>(writer) { }
virtual const char* getDisplayName() const { return "indirect symbol table"; }
virtual uint64_t getSize() const;
virtual uint8_t getAlignment() const { return 2; }
virtual const char* getSectionName() const { return "._indirect_syms"; }
virtual void copyRawContent(uint8_t buffer[]) const;
std::vector<IndirectEntry> fTable;
private:
using WriterAtom<A>::fWriter;
typedef typename A::P P;
};
class CStringEquals
{
public:
bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
};
template <typename A>
class StringsLinkEditAtom : public LinkEditAtom<A>
{
public:
StringsLinkEditAtom(Writer<A>& writer);
virtual const char* getDisplayName() const { return "string pool"; }
virtual uint64_t getSize() const;
virtual uint8_t getAlignment() const { return 2; }
virtual const char* getSectionName() const { return "._string_pool"; }
virtual void copyRawContent(uint8_t buffer[]) const;
int32_t add(const char* name);
int32_t addUnique(const char* name);
int32_t emptyString() { return 1; }
private:
using WriterAtom<A>::fWriter;
typedef typename A::P P;
enum { kBufferSize = 0x01000000 };
class CStringComparor {
public:
bool operator()(const char* left, const char* right) const { return (strcmp(left, right) < 0); }
};
typedef __gnu_cxx::hash_map<const char*, int32_t, __gnu_cxx::hash<const char*>, CStringEquals> StringToOffset;
std::vector<char*> fFullBuffers;
char* fCurrentBuffer;
uint32_t fCurrentBufferUsed;
StringToOffset fUniqueStrings;
};
template <typename A>
class UndefinedSymbolProxyAtom : public WriterAtom<A>
{
public:
UndefinedSymbolProxyAtom(Writer<A>& writer, const char* name) : WriterAtom<A>(writer, Segment::fgLinkEditSegment), fName(name) {}
virtual const char* getName() const { return fName; }
virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeGlobal; }
virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ObjectFile::Atom::kExternalDefinition; }
virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableIn; }
virtual uint64_t getSize() const { return 0; }
virtual const char* getSectionName() const { return "._imports"; }
private:
using WriterAtom<A>::fWriter;
typedef typename A::P P;
const char* fName;
};
template <typename A>
class BranchIslandAtom : public WriterAtom<A>
{
public:
BranchIslandAtom(Writer<A>& writer, const char* name, int islandRegion, ObjectFile::Atom& target, uint32_t targetOffset);
virtual const char* getName() const { return fName; }
virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; }
virtual uint64_t getSize() const;
virtual const char* getSectionName() const { return "__text"; }
virtual void copyRawContent(uint8_t buffer[]) const;
private:
using WriterAtom<A>::fWriter;
const char* fName;
ObjectFile::Atom& fTarget;
uint32_t fTargetOffset;
};
template <typename A>
class StubAtom : public WriterAtom<A>
{
public:
StubAtom(Writer<A>& writer, ObjectFile::Atom& target);
virtual const char* getName() const { return fName; }
virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; }
virtual uint8_t getAlignment() const { return 2; }
virtual uint64_t getSize() const;
virtual const char* getSectionName() const { return "__symbol_stub1"; }
virtual std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(fReferences); }
virtual void copyRawContent(uint8_t buffer[]) const;
ObjectFile::Atom* getTarget() { return &fTarget; }
private:
static const char* stubName(const char* importName);
bool pic() const;
using WriterAtom<A>::fWriter;
const char* fName;
ObjectFile::Atom& fTarget;
std::vector<ObjectFile::Reference*> fReferences;
};
template <typename A>
class StubHelperAtom : public WriterAtom<A>
{
public:
StubHelperAtom(Writer<A>& writer, ObjectFile::Atom& target, ObjectFile::Atom& lazyPointer);
virtual const char* getName() const { return fName; }
virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; }
virtual uint8_t getAlignment() const { return 2; }
virtual uint64_t getSize() const;
virtual const char* getSectionName() const { return "__stub_helper"; }
virtual std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(fReferences); }
virtual void copyRawContent(uint8_t buffer[]) const;
ObjectFile::Atom* getTarget() { return &fTarget; }
private:
static const char* stubName(const char* importName);
using WriterAtom<A>::fWriter;
const char* fName;
ObjectFile::Atom& fTarget;
std::vector<ObjectFile::Reference*> fReferences;
};
template <typename A>
class LazyPointerAtom : public WriterAtom<A>
{
public:
LazyPointerAtom(Writer<A>& writer, ObjectFile::Atom& target);
virtual const char* getName() const { return fName; }
virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; }
virtual uint64_t getSize() const { return sizeof(typename A::P::uint_t); }
virtual const char* getSectionName() const { return "__la_symbol_ptr"; }
virtual std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(fReferences); }
virtual void copyRawContent(uint8_t buffer[]) const;
ObjectFile::Atom* getTarget() { return &fTarget; }
private:
using WriterAtom<A>::fWriter;
static const char* lazyPointerName(const char* importName);
const char* fName;
ObjectFile::Atom& fTarget;
std::vector<ObjectFile::Reference*> fReferences;
};
template <typename A>
class NonLazyPointerAtom : public WriterAtom<A>
{
public:
NonLazyPointerAtom(Writer<A>& writer, ObjectFile::Atom& target);
virtual const char* getName() const { return fName; }
virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; }
virtual uint64_t getSize() const { return sizeof(typename A::P::uint_t); }
virtual const char* getSectionName() const { return "__nl_symbol_ptr"; }
virtual std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(fReferences); }
virtual void copyRawContent(uint8_t buffer[]) const;
ObjectFile::Atom* getTarget() { return &fTarget; }
private:
using WriterAtom<A>::fWriter;
static const char* nonlazyPointerName(const char* importName);
const char* fName;
ObjectFile::Atom& fTarget;
std::vector<ObjectFile::Reference*> fReferences;
};
template <typename A>
class WriterReference : public ObjectFile::Reference
{
public:
typedef typename A::ReferenceKinds Kinds;
WriterReference(uint32_t offset, Kinds kind, ObjectFile::Atom* target,
uint32_t toOffset=0, ObjectFile::Atom* fromTarget=NULL, uint32_t fromOffset=0)
: fKind(kind), fFixUpOffsetInSrc(offset), fTarget(target),
fTargetOffset(toOffset), fFromTarget(fromTarget), fFromTargetOffset(fromOffset) {}
virtual ~WriterReference() {}
virtual bool isTargetUnbound() const { return false; }
virtual bool isFromTargetUnbound() const { return false; }
virtual uint8_t getKind() const { return (uint8_t)fKind; }
virtual uint64_t getFixUpOffset() const { return fFixUpOffsetInSrc; }
virtual const char* getTargetName() const { return fTarget->getName(); }
virtual ObjectFile::Atom& getTarget() const { return *fTarget; }
virtual uint64_t getTargetOffset() const { return fTargetOffset; }
virtual bool hasFromTarget() const { return (fFromTarget != NULL); }
virtual ObjectFile::Atom& getFromTarget() const { return *fFromTarget; }
virtual const char* getFromTargetName() const { return fFromTarget->getName(); }
virtual void setTarget(ObjectFile::Atom& target, uint64_t offset) { fTarget = ⌖ fTargetOffset = offset; }
virtual void setFromTarget(ObjectFile::Atom& target) { fFromTarget = ⌖ }
virtual void setFromTargetName(const char* name) { }
virtual void setFromTargetOffset(uint64_t offset) { fFromTargetOffset = offset; }
virtual const char* getDescription() const { return "writer refrence"; }
virtual uint64_t getFromTargetOffset() const { return fFromTargetOffset; }
private:
Kinds fKind;
uint32_t fFixUpOffsetInSrc;
ObjectFile::Atom* fTarget;
uint32_t fTargetOffset;
ObjectFile::Atom* fFromTarget;
uint32_t fFromTargetOffset;
};
struct ExportSorter
{
bool operator()(ObjectFile::Atom* left, ObjectFile::Atom* right)
{
return (strcmp(left->getName(), right->getName()) < 0);
}
};
template <typename A>
Writer<A>::Writer(const char* path, Options& options, std::vector<ExecutableFile::DyLibUsed>& dynamicLibraries)
: ExecutableFile::Writer(dynamicLibraries), fFilePath(strdup(path)), fOptions(options), fLoadCommandsSection(NULL),
fLoadCommandsSegment(NULL), fPadSegmentInfo(NULL), fPageZeroAtom(NULL), fLargestAtomSize(1),
fEmitVirtualSections(false), fHasWeakExports(false), fReferencesWeakImports(false),
fSeenFollowOnReferences(false), fWritableSegmentPastFirst4GB(false), fFirstWritableSegment(NULL)
{
int permissions = 0777;
if ( fOptions.outputKind() == Options::kObjectFile )
permissions = 0666;
// Calling unlink first assures the file is gone so that open creates it with correct permissions
// It also handles the case where fFilePath file is not writeable but its directory is
// And it means we don't have to truncate the file when done writing (in case new is smaller than old)
(void)unlink(fFilePath);
fFileDescriptor = open(fFilePath, O_CREAT | O_WRONLY | O_TRUNC, permissions);
if ( fFileDescriptor == -1 ) {
throw "can't open file for writing";
}
switch ( fOptions.outputKind() ) {
case Options::kDynamicExecutable:
case Options::kStaticExecutable:
fWriterSynthesizedAtoms.push_back(fPageZeroAtom = new PageZeroAtom<A>(*this));
if ( fOptions.outputKind() == Options::kDynamicExecutable )
fWriterSynthesizedAtoms.push_back(new DsoHandleAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(new MachHeaderAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom<A>(*this));
if ( fOptions.outputKind() == Options::kDynamicExecutable )
fWriterSynthesizedAtoms.push_back(new DyldLoadCommandsAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(new ThreadsLoadCommandsAtom<A>(*this));
if ( fOptions.hasCustomStack() )
fWriterSynthesizedAtoms.push_back(new CustomStackAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom<A>(*this));
break;
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
fWriterSynthesizedAtoms.push_back(new DsoHandleAtom<A>(*this));
// fall through
case Options::kObjectFile:
fWriterSynthesizedAtoms.push_back(new MachHeaderAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom<A>(*this));
if ( fOptions.outputKind() == Options::kDynamicLibrary ) {
fWriterSynthesizedAtoms.push_back(new DylibIDLoadCommandsAtom<A>(*this));
if ( fOptions.initFunctionName() != NULL )
fWriterSynthesizedAtoms.push_back(new RoutinesLoadCommandsAtom<A>(*this));
}
fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom<A>(*this));
break;
case Options::kDyld:
fWriterSynthesizedAtoms.push_back(new DsoHandleAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(new MachHeaderAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(new DyldLoadCommandsAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(new ThreadsLoadCommandsAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom<A>(*this));
break;
}
// add extra commmands
uint8_t ordinal = 1;
switch ( fOptions.outputKind() ) {
case Options::kDynamicExecutable:
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
{
// add dylib load command atoms for all dynamic libraries
const unsigned int libCount = dynamicLibraries.size();
for (unsigned int i=0; i < libCount; ++i) {
ExecutableFile::DyLibUsed& dylibInfo = dynamicLibraries[i];
if ( dylibInfo.options.fBundleLoader ) {
fLibraryToOrdinal[dylibInfo.reader] = EXECUTABLE_ORDINAL;
}
else if ( dylibInfo.indirect ) {
// find ordinal of direct reader
if ( fOptions.nameSpace() == Options::kTwoLevelNameSpace ) {
bool found = false;
for (std::map<class ObjectFile::Reader*, uint32_t>::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) {
if ( it->first == dylibInfo.directReader ) {
//fprintf(stderr, "ordinal %d for indirect %s\n", it->second, dylibInfo.reader->getPath());
fLibraryToOrdinal[dylibInfo.reader] = it->second;
found = true;
break;
}
}
if ( ! found )
fprintf(stderr, "ld64 warning: ordinal not found for %s, parent %s\n", dylibInfo.reader->getPath(), dylibInfo.directReader != NULL ? dylibInfo.directReader->getPath() : NULL);
}
}
else {
// see if a DylibLoadCommandsAtom has already been created for this install path
bool newDylib = true;
const char* dylibInstallPath = dylibInfo.reader->getInstallPath();
if ( dylibInfo.options.fInstallPathOverride != NULL )
dylibInstallPath = dylibInfo.options.fInstallPathOverride;
for (unsigned int seenLib=0; seenLib < i; ++seenLib) {
ExecutableFile::DyLibUsed& seenDylibInfo = dynamicLibraries[seenLib];
if ( !seenDylibInfo.indirect && !seenDylibInfo.options.fBundleLoader ) {
const char* seenDylibInstallPath = seenDylibInfo.reader->getInstallPath();
if ( seenDylibInfo.options.fInstallPathOverride != NULL )
seenDylibInstallPath = dylibInfo.options.fInstallPathOverride;
if ( strcmp(seenDylibInstallPath, dylibInstallPath) == 0 ) {
fLibraryToOrdinal[dylibInfo.reader] = fLibraryToOrdinal[seenDylibInfo.reader];
newDylib = false;
break;
}
}
}
if ( newDylib ) {
// assign new ordinal and check for other paired load commands
fLibraryToOrdinal[dylibInfo.reader] = ordinal++;
fWriterSynthesizedAtoms.push_back(new DylibLoadCommandsAtom<A>(*this, dylibInfo));
if ( dylibInfo.options.fReExport ) {
// this dylib also needs a sub_x load command
bool isFrameworkReExport = false;
const char* lastSlash = strrchr(dylibInstallPath, '/');
if ( lastSlash != NULL ) {
char frameworkName[strlen(lastSlash)+20];
sprintf(frameworkName, "/%s.framework/", &lastSlash[1]);
isFrameworkReExport = (strstr(dylibInstallPath, frameworkName) != NULL);
}
if ( isFrameworkReExport ) {
// needs a LC_SUB_UMBRELLA command
fWriterSynthesizedAtoms.push_back(new SubUmbrellaLoadCommandsAtom<A>(*this, &lastSlash[1]));
}
else {
// needs a LC_SUB_LIBRARY command
const char* nameStart = &lastSlash[1];
if ( lastSlash == NULL )
nameStart = dylibInstallPath;
int len = strlen(nameStart);
const char* dot = strchr(nameStart, '.');
if ( dot != NULL )
len = dot - nameStart;
fWriterSynthesizedAtoms.push_back(new SubLibraryLoadCommandsAtom<A>(*this, nameStart, len));
}
}
}
}
}
// add umbrella command if needed
if ( fOptions.umbrellaName() != NULL ) {
fWriterSynthesizedAtoms.push_back(new UmbrellaLoadCommandsAtom<A>(*this, fOptions.umbrellaName()));
}
std::vector<const char*>& allowableClients = fOptions.allowableClients();
if ( allowableClients.size() != 0 ) {
for (std::vector<const char*>::iterator it=allowableClients.begin();
it != allowableClients.end();
it++)
fWriterSynthesizedAtoms.push_back(new AllowableClientLoadCommandsAtom<A>(*this, *it));
}
}
break;
case Options::kStaticExecutable:
case Options::kObjectFile:
case Options::kDyld:
break;
}
//fprintf(stderr, "ordinals table:\n");
//for (std::map<class ObjectFile::Reader*, uint32_t>::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) {
// fprintf(stderr, "%d <== %s\n", it->second, it->first->getPath());
//}
}
template <typename A>
Writer<A>::~Writer()
{
if ( fFilePath != NULL )
free((void*)fFilePath);
if ( fSymbolTable != NULL )
delete [] fSymbolTable;
}
// for ppc64, -mdynamic-no-pic only works in low 2GB, so we might need to split the zeropage into two segments
template <>bool Writer<ppc64>::mightNeedPadSegment() { return (fOptions.zeroPageSize() >= 0x80000000ULL); }
template <typename A> bool Writer<A>::mightNeedPadSegment() { return false; }
template <typename A>
ObjectFile::Atom* Writer<A>::getUndefinedProxyAtom(const char* name)
{
if ( (fOptions.outputKind() == Options::kObjectFile)
|| (fOptions.undefinedTreatment() != Options::kUndefinedError) )
return new UndefinedSymbolProxyAtom<A>(*this, name);
else
return NULL;
}
template <typename A>
uint8_t Writer<A>::ordinalForLibrary(ObjectFile::Reader* lib)
{
// flat namespace images use zero for all ordinals
if ( fOptions.nameSpace() != Options::kTwoLevelNameSpace )
return 0;
// is an UndefinedSymbolProxyAtom
if ( lib == this )
if ( fOptions.nameSpace() == Options::kTwoLevelNameSpace )
return DYNAMIC_LOOKUP_ORDINAL;
std::map<class ObjectFile::Reader*, uint32_t>::iterator pos = fLibraryToOrdinal.find(lib);
if ( pos != fLibraryToOrdinal.end() )
return pos->second;
throw "can't find ordinal for imported symbol";
}
template <typename A>
uint64_t Writer<A>::write(std::vector<class ObjectFile::Atom*>& atoms,
std::vector<class ObjectFile::Reader::Stab>& stabs,
class ObjectFile::Atom* entryPointAtom, class ObjectFile::Atom* dyldHelperAtom,
bool createUUID)
{
fAllAtoms = &atoms;
fStabs = &stabs;
fEntryPoint = entryPointAtom;
fDyldHelper = dyldHelperAtom;
try {
// Set for create UUID
if (createUUID)
fUUIDAtom->emit();
// check for mdynamic-no-pic codegen which force code into low 4GB
scanForAbsoluteReferences();
// create inter-library stubs
synthesizeStubs();
// create SegmentInfo and SectionInfo objects and assign all atoms to a section
partitionIntoSections();
// segment load command can now be sized and padding can be set
adjustLoadCommandsAndPadding();
// assign each section a file offset
assignFileOffsets();
// if need to add branch islands, reassign file offsets
if ( addBranchIslands() )
assignFileOffsets();
// build symbol table and relocations
buildLinkEdit();
// write everything
return writeAtoms();
} catch (...) {
// clean up if any errors
close(fFileDescriptor);
(void)unlink(fFilePath);
throw;
}
}
template <typename A>
void Writer<A>::buildLinkEdit()
{
this->collectExportedAndImportedAndLocalAtoms();
this->buildSymbolTable();
this->buildFixups();
this->adjustLinkEditSections();
}
template <typename A>
uint64_t Writer<A>::getAtomLoadAddress(const ObjectFile::Atom* atom)
{
return atom->getAddress();
// SectionInfo* info = (SectionInfo*)atom->getSection();
// return info->getBaseAddress() + atom->getSectionOffset();
}
template <typename A>
void Writer<A>::setExportNlist(const ObjectFile::Atom* atom, macho_nlist<P>* entry)
{
// set n_type
if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAsAbsolute ) {
entry->set_n_type(N_EXT | N_ABS);
}
else {
entry->set_n_type(N_EXT | N_SECT);
if ( (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit) && (fOptions.outputKind() == Options::kObjectFile) ) {
if ( fOptions.keepPrivateExterns() )
entry->set_n_type(N_EXT | N_SECT | N_PEXT);
}
}
// set n_sect (section number of implementation )
uint8_t sectionIndex = atom->getSection()->getIndex();
entry->set_n_sect(sectionIndex);
// the __mh_execute_header is magic and must be an absolute symbol
if ( (fOptions.outputKind() == Options::kDynamicExecutable) && (sectionIndex==0)
&& (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip ))
entry->set_n_type(N_EXT | N_ABS);
// set n_desc
uint16_t desc = 0;
if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip )
desc |= REFERENCED_DYNAMICALLY;
if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) {
desc |= N_WEAK_DEF;
fHasWeakExports = true;
}
entry->set_n_desc(desc);
// set n_value ( address this symbol will be at if this executable is loaded at it preferred address )
entry->set_n_value(this->getAtomLoadAddress(atom));
}
template <typename A>
void Writer<A>::setImportNlist(const ObjectFile::Atom* atom, macho_nlist<P>* entry)
{
// set n_type
entry->set_n_type(N_UNDF | N_EXT);
if ( fOptions.keepPrivateExterns()
&& (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit)
&& (fOptions.outputKind() == Options::kObjectFile) )
entry->set_n_type(N_UNDF | N_EXT | N_PEXT);
// set n_sect
entry->set_n_sect(0);
uint16_t desc = 0;
if ( fOptions.outputKind() != Options::kObjectFile ) {
// set n_desc ( high byte is library ordinal, low byte is reference type )
desc = REFERENCE_FLAG_UNDEFINED_LAZY; // FIXME
try {
uint8_t ordinal = this->ordinalForLibrary(atom->getFile());
SET_LIBRARY_ORDINAL(desc, ordinal);
}
catch (const char* msg) {
throwf("%s %s from %s", msg, atom->getDisplayName(), atom->getFile()->getPath());
}
}
if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip )
desc |= REFERENCED_DYNAMICALLY;
if ( ( fOptions.outputKind() != Options::kObjectFile) && (atom->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) {
desc |= N_REF_TO_WEAK;
fReferencesWeakImports = true;
}
// set weak_import attribute
if ( fWeakImportMap[atom] )
desc |= N_WEAK_REF;
entry->set_n_desc(desc);
// set n_value, zero for import proxy and size for tentative definition
entry->set_n_value(atom->getSize());
}
template <typename A>
void Writer<A>::setLocalNlist(const ObjectFile::Atom* atom, macho_nlist<P>* entry)
{
// set n_type
uint8_t type = N_SECT;
if ( atom->getScope() == ObjectFile::Atom::scopeLinkageUnit )
type |= N_PEXT;
entry->set_n_type(type);
// set n_sect (section number of implementation )
uint8_t sectIndex = atom->getSection()->getIndex();
if ( sectIndex == 0 ) {
// see <mach-o/ldsyms.h> synthesized lable for mach_header needs special section number...
if ( strcmp(atom->getSectionName(), "._mach_header") == 0 )
sectIndex = 1;
}
entry->set_n_sect(sectIndex);
// set n_desc
uint16_t desc = 0;
if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition )
desc |= N_WEAK_DEF;
entry->set_n_desc(desc);
// set n_value ( address this symbol will be at if this executable is loaded at it preferred address )
entry->set_n_value(this->getAtomLoadAddress(atom));
}
template <typename A>
void Writer<A>::setNlistRange(std::vector<class ObjectFile::Atom*>& atoms, uint32_t startIndex, uint32_t count)
{
macho_nlist<P>* entry = &fSymbolTable[startIndex];
for (uint32_t i=0; i < count; ++i, ++entry) {
ObjectFile::Atom* atom = atoms[i];
entry->set_n_strx(this->fStringsAtom->add(atom->getName()));
if ( &atoms == &fExportedAtoms ) {
this->setExportNlist(atom, entry);
}
else if ( &atoms == &fImportedAtoms ) {
this->setImportNlist(atom, entry);
}
else {
this->setLocalNlist(atom, entry);
}
}
}
template <typename A>
void Writer<A>::buildSymbolTable()
{
fSymbolTableStabsStartIndex = 0;
fSymbolTableStabsCount = fStabs->size();
fSymbolTableLocalStartIndex = fSymbolTableStabsStartIndex + fSymbolTableStabsCount;
fSymbolTableLocalCount = fLocalSymbolAtoms.size();
fSymbolTableExportStartIndex = fSymbolTableLocalStartIndex + fSymbolTableLocalCount;
fSymbolTableExportCount = fExportedAtoms.size();
fSymbolTableImportStartIndex = fSymbolTableExportStartIndex + fSymbolTableExportCount;
fSymbolTableImportCount = fImportedAtoms.size();
// allocate symbol table
fSymbolTableCount = fSymbolTableStabsCount + fSymbolTableLocalCount + fSymbolTableExportCount + fSymbolTableImportCount;
fSymbolTable = new macho_nlist<P>[fSymbolTableCount];
// fill in symbol table and string pool (do stabs last so strings are at end of pool)
setNlistRange(fLocalSymbolAtoms, fSymbolTableLocalStartIndex, fSymbolTableLocalCount);
setNlistRange(fExportedAtoms, fSymbolTableExportStartIndex, fSymbolTableExportCount);
setNlistRange(fImportedAtoms, fSymbolTableImportStartIndex, fSymbolTableImportCount);
addStabs(fSymbolTableStabsStartIndex);
}
template <typename A>
bool Writer<A>::shouldExport(const ObjectFile::Atom& atom) const
{
if ( atom.getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn )
return false;
switch ( atom.getScope() ) {
case ObjectFile::Atom::scopeGlobal:
return true;
case ObjectFile::Atom::scopeLinkageUnit:
return ( (fOptions.outputKind() == Options::kObjectFile) && fOptions.keepPrivateExterns() );
default:
return false;
}
}
template <typename A>
void Writer<A>::collectExportedAndImportedAndLocalAtoms()
{
const int atomCount = fAllAtoms->size();
// guess at sizes of each bucket to minimize re-allocations
fImportedAtoms.reserve(100);
fExportedAtoms.reserve(atomCount/2);
fLocalSymbolAtoms.reserve(atomCount);
for (int i=0; i < atomCount; ++i) {
ObjectFile::Atom* atom = (*fAllAtoms)[i];
// only named atoms go in symbol table
if ( atom->getName() != NULL ) {
// put atom into correct bucket: imports, exports, locals
//printf("collectExportedAndImportedAndLocalAtoms() name=%s\n", atom->getDisplayName());
switch ( atom->getDefinitionKind() ) {
case ObjectFile::Atom::kExternalDefinition:
case ObjectFile::Atom::kExternalWeakDefinition:
fImportedAtoms.push_back(atom);
break;
case ObjectFile::Atom::kTentativeDefinition:
if ( (fOptions.outputKind() == Options::kObjectFile) && !fOptions.makeTentativeDefinitionsReal() ) {
fImportedAtoms.push_back(atom);
break;
}
// else fall into
case ObjectFile::Atom::kRegularDefinition:
case ObjectFile::Atom::kWeakDefinition:
if ( this->shouldExport(*atom) )
fExportedAtoms.push_back(atom);
else if ( !fOptions.stripLocalSymbols()
&& (atom->getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn) )
fLocalSymbolAtoms.push_back(atom);
break;
}
}
}
// sort exported atoms by name
std::sort(fExportedAtoms.begin(), fExportedAtoms.end(), ExportSorter());
// sort imported atoms by name (not required by runtime, but helps make generated files binary diffable)
std::sort(fImportedAtoms.begin(), fImportedAtoms.end(), ExportSorter());
}
template <typename A>
uint64_t Writer<A>::valueForStab(const ObjectFile::Reader::Stab& stab)
{
switch ( stab.type ) {
case N_FUN:
if ( (stab.string == NULL) || (strlen(stab.string) == 0) ) {
// end of function N_FUN has size
return stab.atom->getSize();
}
else {
// start of function N_FUN has address
return getAtomLoadAddress(stab.atom);
}
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 getAtomLoadAddress(stab.atom) + stab.value;
case N_STSYM:
case N_LCSYM:
case N_BNSYM:
// all these need address of atom
return getAtomLoadAddress(stab.atom);;
case N_ENSYM:
return stab.atom->getSize();
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 getAtomLoadAddress(stab.atom) + stab.atom->getSize();
}
else {
// start of translation unit N_SO has address of end of first atom
return getAtomLoadAddress(stab.atom);
}
}
break;
default:
return stab.value;
}
}
template <typename A>
uint32_t Writer<A>::stringOffsetForStab(const ObjectFile::Reader::Stab& stab)
{
switch (stab.type) {
case N_SO:
if ( (stab.string == NULL) || stab.string[0] == '\0' ) {
return this->fStringsAtom->emptyString();
break;
}
// fall into uniquing case
case N_SOL:
case N_BINCL:
case N_EXCL:
return this->fStringsAtom->addUnique(stab.string);
break;
default:
if ( stab.string == NULL )
return 0;
else if ( stab.string[0] == '\0' )
return this->fStringsAtom->emptyString();
else
return this->fStringsAtom->add(stab.string);
}
return 0;
}
template <typename A>
uint8_t Writer<A>::sectionIndexForStab(const ObjectFile::Reader::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.atom != NULL )
return stab.atom->getSection()->getIndex();
else
return stab.other;
}
template <typename A>
void Writer<A>::addStabs(uint32_t startIndex)
{
macho_nlist<P>* entry = &fSymbolTable[startIndex];
for(std::vector<ObjectFile::Reader::Stab>::iterator it = fStabs->begin(); it != fStabs->end(); ++it, ++entry) {
const ObjectFile::Reader::Stab& stab = *it;
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));
}
}
template <typename A>
uint32_t Writer<A>::symbolIndex(ObjectFile::Atom& atom)
{
// search imports
int i = 0;
for(std::vector<ObjectFile::Atom*>::iterator it=fImportedAtoms.begin(); it != fImportedAtoms.end(); ++it) {
if ( &atom == *it )
return i + fSymbolTableImportStartIndex;
++i;
}
// search locals
i = 0;
for(std::vector<ObjectFile::Atom*>::iterator it=fLocalSymbolAtoms.begin(); it != fLocalSymbolAtoms.end(); ++it) {
if ( &atom == *it )
return i + fSymbolTableLocalStartIndex;
++i;
}
// search exports
i = 0;
for(std::vector<ObjectFile::Atom*>::iterator it=fExportedAtoms.begin(); it != fExportedAtoms.end(); ++it) {
if ( &atom == *it )
return i + fSymbolTableExportStartIndex;
++i;
}
throwf("atom not found in symbolIndex(%s) for %s", atom.getDisplayName(), atom.getFile()->getPath());
}
template <typename A>
void Writer<A>::buildFixups()
{
if ( fOptions.outputKind() == Options::kObjectFile ) {
this->buildObjectFileFixups();
}
else {
if ( fOptions.keepRelocations() )
this->buildObjectFileFixups();
this->buildExecutableFixups();
}
}
template <>
uint32_t Writer<x86_64>::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref)
{
ObjectFile::Atom& target = ref->getTarget();
bool external = (target.getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn);
uint32_t symbolIndex = external ? this->symbolIndex(target) : target.getSection()->getIndex();
uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset();
macho_relocation_info<P> reloc1;
macho_relocation_info<P> reloc2;
x86_64::ReferenceKinds kind = (x86_64::ReferenceKinds)ref->getKind();
switch ( kind ) {
case x86_64::kNoFixUp:
case x86_64::kFollowOn:
return 0;
case x86_64::kPointer:
case x86_64::kPointerWeakImport:
reloc1.set_r_address(address);
reloc1.set_r_symbolnum(symbolIndex);
reloc1.set_r_pcrel(false);
reloc1.set_r_length(3);
reloc1.set_r_extern(external);
reloc1.set_r_type(X86_64_RELOC_UNSIGNED);
fSectionRelocs.insert(fSectionRelocs.begin(), reloc1);
return 1;
case x86_64::kPointerDiff32:
case x86_64::kPointerDiff:
{
ObjectFile::Atom& fromTarget = ref->getFromTarget();
bool fromExternal = (fromTarget.getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn);
uint32_t fromSymbolIndex = fromExternal ? this->symbolIndex(fromTarget) : fromTarget.getSection()->getIndex();
reloc1.set_r_address(address);
reloc1.set_r_symbolnum(symbolIndex);
reloc1.set_r_pcrel(false);
reloc1.set_r_length(kind==x86_64::kPointerDiff32 ? 2 : 3);
reloc1.set_r_extern(external);
reloc1.set_r_type(X86_64_RELOC_UNSIGNED);
reloc2.set_r_address(address);
reloc2.set_r_symbolnum(fromSymbolIndex);
reloc2.set_r_pcrel(false);
reloc2.set_r_length(kind==x86_64::kPointerDiff32 ? 2 : 3);
reloc2.set_r_extern(fromExternal);
reloc2.set_r_type(X86_64_RELOC_SUBTRACTOR);
fSectionRelocs.insert(fSectionRelocs.begin(), reloc1);
fSectionRelocs.insert(fSectionRelocs.begin(), reloc2);
return 2;
}
case x86_64::kBranchPCRel32:
case x86_64::kBranchPCRel32WeakImport:
reloc1.set_r_address(address);
reloc1.set_r_symbolnum(symbolIndex);
reloc1.set_r_pcrel(true);
reloc1.set_r_length(2);
reloc1.set_r_extern(external);
reloc1.set_r_type(X86_64_RELOC_BRANCH);
fSectionRelocs.insert(fSectionRelocs.begin(), reloc1);
return 1;
case x86_64::kPCRel32:
reloc1.set_r_address(address);
reloc1.set_r_symbolnum(symbolIndex);
reloc1.set_r_pcrel(true);
reloc1.set_r_length(2);
reloc1.set_r_extern(external);
reloc1.set_r_type(X86_64_RELOC_SIGNED);
fSectionRelocs.insert(fSectionRelocs.begin(), reloc1);
return 1;
case x86_64::kPCRel32_1:
reloc1.set_r_address(address);
reloc1.set_r_symbolnum(symbolIndex);
reloc1.set_r_pcrel(true);
reloc1.set_r_length(2);
reloc1.set_r_extern(external);
reloc1.set_r_type(X86_64_RELOC_SIGNED_1);
fSectionRelocs.insert(fSectionRelocs.begin(), reloc1);
return 1;
case x86_64::kPCRel32_2:
reloc1.set_r_address(address);
reloc1.set_r_symbolnum(symbolIndex);
reloc1.set_r_pcrel(true);
reloc1.set_r_length(2);
reloc1.set_r_extern(external);
reloc1.set_r_type(X86_64_RELOC_SIGNED_2);
fSectionRelocs.insert(fSectionRelocs.begin(), reloc1);
return 1;
case x86_64::kPCRel32_4:
reloc1.set_r_address(address);
reloc1.set_r_symbolnum(symbolIndex);
reloc1.set_r_pcrel(true);
reloc1.set_r_length(2);
reloc1.set_r_extern(external);
reloc1.set_r_type(X86_64_RELOC_SIGNED_4);
fSectionRelocs.insert(fSectionRelocs.begin(), reloc1);
return 1;
case x86_64::kPCRel32GOT:
case x86_64::kPCRel32GOTWeakImport:
reloc1.set_r_address(address);
reloc1.set_r_symbolnum(symbolIndex);
reloc1.set_r_pcrel(true);
reloc1.set_r_length(2);
reloc1.set_r_extern(external);
reloc1.set_r_type(X86_64_RELOC_GOT);
fSectionRelocs.insert(fSectionRelocs.begin(), reloc1);
return 1;
case x86_64::kPCRel32GOTLoad:
case x86_64::kPCRel32GOTLoadWeakImport:
reloc1.set_r_address(address);
reloc1.set_r_symbolnum(symbolIndex);
reloc1.set_r_pcrel(true);
reloc1.set_r_length(2);
reloc1.set_r_extern(external);
reloc1.set_r_type(X86_64_RELOC_GOT_LOAD);
fSectionRelocs.insert(fSectionRelocs.begin(), reloc1);
return 1;
}
return 0;
}
template <>
uint32_t Writer<x86>::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref)
{
ObjectFile::Atom& target = ref->getTarget();
bool isExtern = false;
switch ( target.getDefinitionKind() ) {
case ObjectFile::Atom::kRegularDefinition:
isExtern = false;
break;
case ObjectFile::Atom::kWeakDefinition:
case ObjectFile::Atom::kTentativeDefinition:
case ObjectFile::Atom::kExternalDefinition:
case ObjectFile::Atom::kExternalWeakDefinition:
isExtern = shouldExport(target);
break;
}
uint32_t symbolIndex = 0;
if ( isExtern )
symbolIndex = this->symbolIndex(target);
uint32_t sectionNum = target.getSection()->getIndex();
uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset();
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;
x86::ReferenceKinds kind = (x86::ReferenceKinds)ref->getKind();
switch ( kind ) {
case x86::kNoFixUp:
case x86::kFollowOn:
return 0;
case x86::kPointer:
case x86::kPointerWeakImport:
case x86::kAbsolute32:
if ( !isExtern && (ref->getTargetOffset() != 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(target.getAddress());
}
else {
reloc1.set_r_address(address);
reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum);
reloc1.set_r_pcrel(false);
reloc1.set_r_length(2);
reloc1.set_r_extern(isExtern);
reloc1.set_r_type(GENERIC_RELOC_VANILLA);
}
fSectionRelocs.insert(fSectionRelocs.begin(), reloc1);
return 1;
case x86::kPointerDiff:
{
pint_t fromAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset();
sreloc1->set_r_scattered(true);
sreloc1->set_r_pcrel(false);
sreloc1->set_r_length(2);
if ( ref->getTarget().getScope() == ObjectFile::Atom::scopeTranslationUnit )
sreloc1->set_r_type(GENERIC_RELOC_LOCAL_SECTDIFF);
else
sreloc1->set_r_type(GENERIC_RELOC_SECTDIFF);
sreloc1->set_r_address(address);
sreloc1->set_r_value(target.getAddress());
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);
sreloc2->set_r_value(fromAddr);
fSectionRelocs.insert(fSectionRelocs.begin(), reloc2);
fSectionRelocs.insert(fSectionRelocs.begin(), reloc1);
return 2;
}
case x86::kPCRel32WeakImport:
case x86::kPCRel32:
if ( !isExtern && (ref->getTargetOffset() != 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(target.getAddress());
}
else {
reloc1.set_r_address(address);
reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum);
reloc1.set_r_pcrel(true);
reloc1.set_r_length(2);
reloc1.set_r_extern(isExtern);
reloc1.set_r_type(GENERIC_RELOC_VANILLA);
}
fSectionRelocs.insert(fSectionRelocs.begin(), reloc1);
return 1;
}
return 0;
}
template <>
uint8_t Writer<ppc>::getRelocPointerSize()
{
return 2;
}
template <>
uint8_t Writer<ppc64>::getRelocPointerSize()
{
return 3;
}
template <>
uint32_t Writer<ppc>::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref)
{
return addObjectRelocs_powerpc(atom, ref);
}
template <>
uint32_t Writer<ppc64>::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref)
{
return addObjectRelocs_powerpc(atom, ref);
}
//
// addObjectRelocs<ppc> and addObjectRelocs<ppc64> are almost exactly the same, so
// they use a common addObjectRelocs_powerpc() method.
//
template <typename A>
uint32_t Writer<A>::addObjectRelocs_powerpc(ObjectFile::Atom* atom, ObjectFile::Reference* ref)
{
ObjectFile::Atom& target = ref->getTarget();
bool isExtern = false;
switch ( target.getDefinitionKind() ) {
case ObjectFile::Atom::kRegularDefinition:
isExtern = false;
break;
case ObjectFile::Atom::kWeakDefinition:
case ObjectFile::Atom::kTentativeDefinition:
case ObjectFile::Atom::kExternalDefinition:
case ObjectFile::Atom::kExternalWeakDefinition:
isExtern = shouldExport(target);
break;
}
uint32_t symbolIndex = 0;
if ( isExtern )
symbolIndex = this->symbolIndex(target);
uint32_t sectionNum = target.getSection()->getIndex();
uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset();
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;
typename A::ReferenceKinds kind = (typename A::ReferenceKinds)ref->getKind();
switch ( kind ) {
case A::kNoFixUp:
case A::kFollowOn:
return 0;
case A::kPointer:
case A::kPointerWeakImport:
if ( !isExtern && (ref->getTargetOffset() >= target.getSize()) ) {
// use scattered reloc is target offset is outside target
sreloc1->set_r_scattered(true);
sreloc1->set_r_pcrel(false);
sreloc1->set_r_length(getRelocPointerSize());
sreloc1->set_r_type(GENERIC_RELOC_VANILLA);
sreloc1->set_r_address(address);
sreloc1->set_r_value(target.getAddress());
}
else {
reloc1.set_r_address(address);
if ( isExtern )
reloc1.set_r_symbolnum(symbolIndex);
else
reloc1.set_r_symbolnum(sectionNum);
reloc1.set_r_pcrel(false);
reloc1.set_r_length(getRelocPointerSize());
reloc1.set_r_extern(isExtern);
reloc1.set_r_type(GENERIC_RELOC_VANILLA);
}
fSectionRelocs.insert(fSectionRelocs.begin(), reloc1);
return 1;
case A::kPointerDiff32:
case A::kPointerDiff64:
{
pint_t toAddr = target.getAddress() + ref->getTargetOffset();
pint_t fromAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset();
sreloc1->set_r_scattered(true);
sreloc1->set_r_pcrel(false);
sreloc1->set_r_length( (kind == A::kPointerDiff32) ? 2 : 3);
sreloc1->set_r_type(ref->getTargetOffset() != 0 ? PPC_RELOC_LOCAL_SECTDIFF : PPC_RELOC_SECTDIFF);
sreloc1->set_r_address(address);
sreloc1->set_r_value(toAddr);
sreloc2->set_r_scattered(true);
sreloc2->set_r_pcrel(false);
sreloc2->set_r_length( (kind == A::kPointerDiff32) ? 2 : 3);
sreloc2->set_r_type(PPC_RELOC_PAIR);
sreloc2->set_r_address(0);
sreloc2->set_r_value(fromAddr);
fSectionRelocs.insert(fSectionRelocs.begin(), reloc2);
fSectionRelocs.insert(fSectionRelocs.begin(), reloc1);
return 2;
}
case A::kBranch24WeakImport:
case A::kBranch24:
if ( (ref->getTargetOffset() == 0) || isExtern ) {
reloc1.set_r_address(address);
if ( isExtern )
reloc1.set_r_symbolnum(symbolIndex);
else
reloc1.set_r_symbolnum(sectionNum);
reloc1.set_r_pcrel(true);
reloc1.set_r_length(2);
reloc1.set_r_type(PPC_RELOC_BR24);
reloc1.set_r_extern(isExtern);
}
else {
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(target.getAddress());
}
fSectionRelocs.insert(fSectionRelocs.begin(), reloc1);
return 1;
case A::kBranch14:
if ( (ref->getTargetOffset() == 0) || isExtern ) {
reloc1.set_r_address(address);
if ( isExtern )
reloc1.set_r_symbolnum(symbolIndex);
else
reloc1.set_r_symbolnum(sectionNum);
reloc1.set_r_pcrel(true);
reloc1.set_r_length(2);
reloc1.set_r_type(PPC_RELOC_BR14);
reloc1.set_r_extern(isExtern);
}
else {
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(target.getAddress());
}
fSectionRelocs.insert(fSectionRelocs.begin(), reloc1);
return 1;
case A::kPICBaseLow16:
case A::kPICBaseLow14:
{
pint_t fromAddr = atom->getAddress() + ref->getFromTargetOffset();
pint_t toAddr = target.getAddress() + ref->getTargetOffset();
sreloc1->set_r_scattered(true);
sreloc1->set_r_pcrel(false);
sreloc1->set_r_length(2);
sreloc1->set_r_type(kind == A::kPICBaseLow16 ? PPC_RELOC_LO16_SECTDIFF : PPC_RELOC_LO14_SECTDIFF);
sreloc1->set_r_address(address);
sreloc1->set_r_value(target.getAddress());
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));
sreloc2->set_r_value(fromAddr);
fSectionRelocs.insert(fSectionRelocs.begin(), reloc2);
fSectionRelocs.insert(fSectionRelocs.begin(), reloc1);
return 2;
}
case A::kPICBaseHigh16:
{
pint_t fromAddr = atom->getAddress() + ref->getFromTargetOffset();
pint_t toAddr = target.getAddress() + ref->getTargetOffset();
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(target.getAddress());
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);
fSectionRelocs.insert(fSectionRelocs.begin(), reloc2);
fSectionRelocs.insert(fSectionRelocs.begin(), reloc1);
return 2;
}
case A::kAbsLow14:
case A::kAbsLow16:
{
pint_t toAddr = target.getAddress() + ref->getTargetOffset();
if ( (ref->getTargetOffset() == 0) || isExtern ) {
reloc1.set_r_address(address);
if ( isExtern )
reloc1.set_r_symbolnum(symbolIndex);
else
reloc1.set_r_symbolnum(sectionNum);
reloc1.set_r_pcrel(false);
reloc1.set_r_length(2);
reloc1.set_r_extern(isExtern);
reloc1.set_r_type(kind==A::kAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14);
}
else {
sreloc1->set_r_scattered(true);
sreloc1->set_r_pcrel(false);
sreloc1->set_r_length(2);
sreloc1->set_r_type(kind==A::kAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14);
sreloc1->set_r_address(address);
sreloc1->set_r_value(target.getAddress());
}
if ( isExtern )
reloc2.set_r_address(ref->getTargetOffset() >> 16);
else
reloc2.set_r_address(toAddr >> 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);
fSectionRelocs.insert(fSectionRelocs.begin(), reloc2);
fSectionRelocs.insert(fSectionRelocs.begin(), reloc1);
return 2;
}
case A::kAbsHigh16:
{
pint_t toAddr = target.getAddress() + ref->getTargetOffset();
if ( (ref->getTargetOffset() == 0) || isExtern ) {
reloc1.set_r_address(address);
if ( isExtern )
reloc1.set_r_symbolnum(symbolIndex);
else
reloc1.set_r_symbolnum(sectionNum);
reloc1.set_r_pcrel(false);
reloc1.set_r_length(2);
reloc1.set_r_extern(isExtern);
reloc1.set_r_type(PPC_RELOC_HI16);
}
else {
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(target.getAddress());
}
if ( isExtern )
reloc2.set_r_address(ref->getTargetOffset() & 0xFFFF);
else
reloc2.set_r_address(toAddr & 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);
fSectionRelocs.insert(fSectionRelocs.begin(), reloc2);
fSectionRelocs.insert(fSectionRelocs.begin(), reloc1);
return 2;
}
case A::kAbsHigh16AddLow:
{
pint_t toAddr = target.getAddress() + ref->getTargetOffset();
uint32_t overflow = 0;
if ( (toAddr & 0x00008000) != 0 )
overflow = 0x10000;
if ( (ref->getTargetOffset() == 0) || isExtern ) {
reloc1.set_r_address(address);
if ( isExtern )
reloc1.set_r_symbolnum(symbolIndex);
else
reloc1.set_r_symbolnum(sectionNum);
reloc1.set_r_pcrel(false);
reloc1.set_r_length(2);
reloc1.set_r_extern(isExtern);
reloc1.set_r_type(PPC_RELOC_HA16);
}
else {
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(target.getAddress());
}
if ( isExtern )
reloc2.set_r_address(ref->getTargetOffset() & 0xFFFF);
else
reloc2.set_r_address(toAddr & 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);
fSectionRelocs.insert(fSectionRelocs.begin(), reloc2);
fSectionRelocs.insert(fSectionRelocs.begin(), reloc1);
return 2;
}
}
return 0;
}
template <typename A>
void Writer<A>::buildObjectFileFixups()
{
uint32_t relocIndex = 0;
std::vector<SegmentInfo*>& segmentInfos = fSegmentInfos;
const int segCount = segmentInfos.size();
for(int i=0; i < segCount; ++i) {
SegmentInfo* curSegment = segmentInfos[i];
std::vector<SectionInfo*>& sectionInfos = curSegment->fSections;
const int sectionCount = sectionInfos.size();
for(int j=0; j < sectionCount; ++j) {
SectionInfo* curSection = sectionInfos[j];
//fprintf(stderr, "buildObjectFileFixups(): starting section %s\n", curSection->fSectionName);
std::vector<ObjectFile::Atom*>& sectionAtoms = curSection->fAtoms;
if ( ! curSection->fAllZeroFill ) {
if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers || curSection->fAllStubs )
curSection->fIndirectSymbolOffset = fIndirectTableAtom->fTable.size();
curSection->fRelocOffset = relocIndex;
const int atomCount = sectionAtoms.size();
for (int k=0; k < atomCount; ++k) {
ObjectFile::Atom* atom = sectionAtoms[k];
//fprintf(stderr, "buildObjectFileFixups(): atom %s\n", atom->getDisplayName());
std::vector<ObjectFile::Reference*>& refs = atom->getReferences();
const int refCount = refs.size();
for (int l=0; l < refCount; ++l) {
ObjectFile::Reference* ref = refs[l];
if ( ref->getKind() == A::kFollowOn )
fSeenFollowOnReferences = true;
if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers || curSection->fAllStubs ) {
uint32_t offsetInSection = atom->getSectionOffset();
uint32_t indexInSection = offsetInSection / atom->getSize();
uint32_t undefinedSymbolIndex;
if ( curSection->fAllStubs ) {
ObjectFile::Atom& stubTarget =ref->getTarget();
ObjectFile::Atom& stubTargetTarget = stubTarget.getReferences()[0]->getTarget();
undefinedSymbolIndex = this->symbolIndex(stubTargetTarget);
//fprintf(stderr, "stub %s ==> %s ==> %s ==> index:%u\n", atom->getDisplayName(), stubTarget.getDisplayName(), stubTargetTarget.getDisplayName(), undefinedSymbolIndex);
}
else {
// only use INDIRECT_SYMBOL_LOCAL in non-lazy-pointers for atoms that won't be in symbol table
if ( curSection->fAllNonLazyPointers && (ref->getTarget().getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn) )
undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL;
else
undefinedSymbolIndex = this->symbolIndex(ref->getTarget());
}
uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset;
IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex };
//printf("fIndirectTableAtom->fTable.add(sectionIndex=%u, indirectTableIndex=%u => %u), size=%lld\n", indexInSection, indirectTableIndex, undefinedSymbolIndex, atom->getSize());
fIndirectTableAtom->fTable.push_back(entry);
if ( curSection->fAllLazyPointers ) {
ObjectFile::Atom& target = ref->getTarget();
ObjectFile::Atom& fromTarget = ref->getFromTarget();
if ( &fromTarget == NULL ) {
fprintf(stderr, "lazy pointer %s missing initial binding\n", atom->getDisplayName());
}
else {
bool isExtern = ( ((target.getDefinitionKind() == ObjectFile::Atom::kExternalDefinition)
|| (target.getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition))
&& (target.getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn) );
macho_relocation_info<P> reloc1;
reloc1.set_r_address(atom->getSectionOffset());
reloc1.set_r_symbolnum(isExtern ? this->symbolIndex(target) : target.getSection()->getIndex());
reloc1.set_r_pcrel(false);
reloc1.set_r_length();
reloc1.set_r_extern(isExtern);
reloc1.set_r_type(GENERIC_RELOC_VANILLA);
fSectionRelocs.insert(fSectionRelocs.begin(), reloc1);
++relocIndex;
}
}
else if ( curSection->fAllStubs ) {
relocIndex += this->addObjectRelocs(atom, ref);
}
}
else if ( ref->getKind() != A::kNoFixUp ) {
relocIndex += this->addObjectRelocs(atom, ref);
}
}
}
curSection->fRelocCount = relocIndex - curSection->fRelocOffset;
}
}
}
// now reverse reloc entries
for(int i=0; i < segCount; ++i) {
SegmentInfo* curSegment = segmentInfos[i];
std::vector<SectionInfo*>& sectionInfos = curSegment->fSections;
const int sectionCount = sectionInfos.size();
for(int j=0; j < sectionCount; ++j) {
SectionInfo* curSection = sectionInfos[j];
curSection->fRelocOffset = relocIndex - curSection->fRelocOffset - curSection->fRelocCount;
}
}
}
template <>
bool Writer<ppc>::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref, bool slideable)
{
switch ( ref.getKind() ) {
case ppc::kAbsLow16:
case ppc::kAbsLow14:
case ppc::kAbsHigh16:
case ppc::kAbsHigh16AddLow:
if ( slideable )
return true;
}
return false;
}
template <>
bool Writer<ppc64>::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref, bool slideable)
{
switch ( ref.getKind() ) {
case ppc::kAbsLow16:
case ppc::kAbsLow14:
case ppc::kAbsHigh16:
case ppc::kAbsHigh16AddLow:
if ( slideable )
return true;
}
return false;
}
template <>
bool Writer<x86>::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref, bool slideable)
{
if ( ref.getKind() == x86::kAbsolute32 ) {
switch ( ref.getTarget().getDefinitionKind() ) {
case ObjectFile::Atom::kTentativeDefinition:
case ObjectFile::Atom::kRegularDefinition:
// illegal in dylibs/bundles, until we support TEXT relocs
return slideable;
case ObjectFile::Atom::kWeakDefinition:
// illegal if an exported weak symbol, until we support TEXT relocs
return this->shouldExport(ref.getTarget());
case ObjectFile::Atom::kExternalDefinition:
case ObjectFile::Atom::kExternalWeakDefinition:
// illegal until we support TEXT relocs
return true;
}
}
return false;
}
template <>
bool Writer<x86_64>::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref, bool slideable)
{
return false;
}
template <typename A>
typename Writer<A>::RelocKind Writer<A>::relocationNeededInFinalLinkedImage(const ObjectFile::Atom& target) const
{
const bool slideable = (fOptions.outputKind() != Options::kDynamicExecutable) && (fOptions.outputKind() != Options::kStaticExecutable);
switch ( target.getDefinitionKind() ) {
case ObjectFile::Atom::kTentativeDefinition:
case ObjectFile::Atom::kRegularDefinition:
// for flat-namespace or interposable two-level-namespace
// all references to exported symbols get indirected
if ( this->shouldExport(target) &&
((fOptions.nameSpace() == Options::kFlatNameSpace)
|| (fOptions.nameSpace() == Options::kForceFlatNameSpace)
|| fOptions.interposable()) )
return kRelocExternal;
else if ( slideable )
return kRelocInternal;
else
return kRelocNone;
case ObjectFile::Atom::kWeakDefinition:
// all calls to global weak definitions get indirected
if ( this->shouldExport(target) )
return kRelocExternal;
else if ( slideable )
return kRelocInternal;
else
return kRelocNone;
case ObjectFile::Atom::kExternalDefinition:
case ObjectFile::Atom::kExternalWeakDefinition:
return kRelocExternal;
}
return kRelocNone;
}
template <typename A>
uint64_t Writer<A>::relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const
{
// for 32-bit architectures, the r_address field in relocs
// for final linked images is the offset from the base address
uint64_t result = address - fOptions.baseAddress();
if ( result > 0x7FFFFFFF ) {
throwf("image too large: address can't fit in 31-bit r_address field in %s from %s",
atom->getDisplayName(), atom->getFile()->getPath());
}
return result;
}
template <>
uint64_t Writer<x86_64>::relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const
{
// for x86_64, the r_address field in relocs for final linked images
// is the offset from the start address of the first writable segment
uint64_t result = address - fFirstWritableSegment->fBaseAddress;
if ( result > 0xFFFFFFFF ) {
throwf("image too large: address can't fit in 32-bit r_address field in %s from %s",
atom->getDisplayName(), atom->getFile()->getPath());
}
return result;
}
template <>
uint64_t Writer<ppc64>::relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const
{
// for ppc64, the Mac OS X 10.4 dyld assumes r_address is always the offset from the base address.
// the 10.5 dyld, iterprets the r_address as:
// 1) an offset from the base address, iff there are no writable segments with a address > 4GB from base address, otherwise
// 2) an offset from the base address of the first writable segment
// For dyld, r_address is always the offset from the base address
uint64_t result;
bool badFor10_4 = false;
if ( fWritableSegmentPastFirst4GB ) {
if ( fOptions.macosxVersionMin() < Options::k10_5 )
badFor10_4 = true;
result = address - fFirstWritableSegment->fBaseAddress;
if ( result > 0xFFFFFFFF ) {
throwf("image too large: address can't fit in 32-bit r_address field in %s from %s",
atom->getDisplayName(), atom->getFile()->getPath());
}
}
else {
result = address - fOptions.baseAddress();
if ( (fOptions.macosxVersionMin() < Options::k10_5) && (result > 0x7FFFFFFF) )
badFor10_4 = true;
}
if ( badFor10_4 ) {
throwf("image or pagezero_size too large for Mac OS X 10.4: address can't fit in 31-bit r_address field for %s from %s",
atom->getDisplayName(), atom->getFile()->getPath());
}
return result;
}
template <typename A>
void Writer<A>::buildExecutableFixups()
{
const bool slideable = (fOptions.outputKind() != Options::kDynamicExecutable) && (fOptions.outputKind() != Options::kStaticExecutable);
fIndirectTableAtom->fTable.reserve(50); // minimize reallocations
std::vector<SegmentInfo*>& segmentInfos = fSegmentInfos;
const int segCount = segmentInfos.size();
for(int i=0; i < segCount; ++i) {
SegmentInfo* curSegment = segmentInfos[i];
std::vector<SectionInfo*>& sectionInfos = curSegment->fSections;
const int sectionCount = sectionInfos.size();
for(int j=0; j < sectionCount; ++j) {
SectionInfo* curSection = sectionInfos[j];
//fprintf(stderr, "starting section %p\n", curSection->fSectionName);
std::vector<ObjectFile::Atom*>& sectionAtoms = curSection->fAtoms;
if ( ! curSection->fAllZeroFill ) {
if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers || curSection->fAllStubs || curSection->fAllSelfModifyingStubs )
curSection->fIndirectSymbolOffset = fIndirectTableAtom->fTable.size();
const int atomCount = sectionAtoms.size();
for (int k=0; k < atomCount; ++k) {
ObjectFile::Atom* atom = sectionAtoms[k];
std::vector<ObjectFile::Reference*>& refs = atom->getReferences();
const int refCount = refs.size();
//fprintf(stderr, "atom %s has %d references in section %s, %p\n", atom->getDisplayName(), refCount, curSection->fSectionName, atom->getSection());
for (int l=0; l < refCount; ++l) {
ObjectFile::Reference* ref = refs[l];
if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers ) {
// if atom is in (non)lazy_pointer section, this is encoded as an indirect symbol
if ( atom->getSize() != sizeof(pint_t) ) {
printf("wrong size pointer atom %s from file %s\n", atom->getDisplayName(), atom->getFile()->getPath());
}
ObjectFile::Atom* pointerTarget = &(ref->getTarget());
if ( curSection->fAllLazyPointers ) {
pointerTarget = ((LazyPointerAtom<A>*)atom)->getTarget();
}
uint32_t offsetInSection = atom->getSectionOffset();
uint32_t indexInSection = offsetInSection / sizeof(pint_t);
uint32_t undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL;
if ( this->relocationNeededInFinalLinkedImage(*pointerTarget) == kRelocExternal )
undefinedSymbolIndex = this->symbolIndex(*pointerTarget);
uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset;
IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex };
//fprintf(stderr,"fIndirectTableAtom->fTable.add(%d-%d => 0x%X-%s), size=%lld\n", indexInSection, indirectTableIndex, undefinedSymbolIndex, ref->getTarget().getName(), atom->getSize());
fIndirectTableAtom->fTable.push_back(entry);
if ( slideable && curSection->fAllLazyPointers ) {
// if this is a dylib/bundle, need vanilla internal relocation to fix up binding handler if image slides
macho_relocation_info<P> pblaReloc;
uint32_t sectionNum = 1;
if ( fDyldHelper != NULL )
sectionNum = ((SectionInfo*)(fDyldHelper->getSection()))->getIndex();
//fprintf(stderr, "lazy pointer reloc, section index=%u, section name=%s\n", sectionNum, curSection->fSectionName);
pblaReloc.set_r_address(relocAddressInFinalLinkedImage(atom->getAddress(), atom));
pblaReloc.set_r_symbolnum(sectionNum);
pblaReloc.set_r_pcrel(false);
pblaReloc.set_r_length();
pblaReloc.set_r_extern(false);
pblaReloc.set_r_type(GENERIC_RELOC_VANILLA);
fInternalRelocs.push_back(pblaReloc);
}
}
else if ( ref->getKind() == A::kPointer ) {
if ( slideable && ((curSegment->fInitProtection & VM_PROT_WRITE) == 0) ) {
throwf("pointer in read-only segment not allowed in slidable image, used in %s from %s",
atom->getDisplayName(), atom->getFile()->getPath());
}
switch ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) ) {
case kRelocNone:
// no reloc needed
break;
case kRelocInternal:
{
macho_relocation_info<P> internalReloc;
SectionInfo* sectInfo = (SectionInfo*)ref->getTarget().getSection();
uint32_t sectionNum = sectInfo->getIndex();
// special case _mh_dylib_header and friends which are not in any real section
if ( (sectionNum ==0) && sectInfo->fVirtualSection && (strcmp(sectInfo->fSectionName, "._mach_header") == 0) )
sectionNum = 1;
internalReloc.set_r_address(this->relocAddressInFinalLinkedImage(atom->getAddress() + ref->getFixUpOffset(), atom));
internalReloc.set_r_symbolnum(sectionNum);
internalReloc.set_r_pcrel(false);
internalReloc.set_r_length();
internalReloc.set_r_extern(false);
internalReloc.set_r_type(GENERIC_RELOC_VANILLA);
fInternalRelocs.push_back(internalReloc);
}
break;
case kRelocExternal:
{
macho_relocation_info<P> externalReloc;
externalReloc.set_r_address(this->relocAddressInFinalLinkedImage(atom->getAddress() + ref->getFixUpOffset(), atom));
externalReloc.set_r_symbolnum(this->symbolIndex(ref->getTarget()));
externalReloc.set_r_pcrel(false);
externalReloc.set_r_length();
externalReloc.set_r_extern(true);
externalReloc.set_r_type(GENERIC_RELOC_VANILLA);
fExternalRelocs.push_back(externalReloc);
}
break;
}
}
else if ( this->illegalRelocInFinalLinkedImage(*ref, slideable) ) {
throwf("absolute addressing (perhaps -mdynamic-no-pic) used in %s from %s not allowed in slidable image", atom->getDisplayName(), atom->getFile()->getPath());
}
}
if ( curSection->fAllSelfModifyingStubs || curSection->fAllStubs ) {
ObjectFile::Atom* stubTarget = ((StubAtom<A>*)atom)->getTarget();
uint32_t undefinedSymbolIndex = this->symbolIndex(*stubTarget);
uint32_t offsetInSection = atom->getSectionOffset();
uint32_t indexInSection = offsetInSection / atom->getSize();
uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset;
IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex };
//fprintf(stderr,"for stub: fIndirectTableAtom->fTable.add(%d-%d => 0x%X-%s), size=%lld\n", indexInSection, indirectTableIndex, undefinedSymbolIndex, stubTarget->getName(), atom->getSize());
fIndirectTableAtom->fTable.push_back(entry);
}
}
}
}
}
}
template <>
void Writer<ppc>::writeNoOps(uint32_t from, uint32_t to)
{
uint32_t ppcNop;
OSWriteBigInt32(&ppcNop, 0, 0x60000000);
for (uint32_t p=from; p < to; p += 4)
::pwrite(fFileDescriptor, &ppcNop, 4, p);
}
template <>
void Writer<ppc64>::writeNoOps(uint32_t from, uint32_t to)
{
uint32_t ppcNop;
OSWriteBigInt32(&ppcNop, 0, 0x60000000);
for (uint32_t p=from; p < to; p += 4)
::pwrite(fFileDescriptor, &ppcNop, 4, p);
}
template <>
void Writer<x86>::writeNoOps(uint32_t from, uint32_t to)
{
uint8_t x86Nop = 0x90;
for (uint32_t p=from; p < to; ++p)
::pwrite(fFileDescriptor, &x86Nop, 1, p);
}
template <>
void Writer<x86_64>::writeNoOps(uint32_t from, uint32_t to)
{
uint8_t x86Nop = 0x90;
for (uint32_t p=from; p < to; ++p)
::pwrite(fFileDescriptor, &x86Nop, 1, p);
}
template <typename A>
uint64_t Writer<A>::writeAtoms()
{
uint32_t end = 0;
uint8_t* buffer = new uint8_t[(fLargestAtomSize+4095) & (-4096)];
std::vector<SegmentInfo*>& segmentInfos = fSegmentInfos;
const int segCount = segmentInfos.size();
for(int i=0; i < segCount; ++i) {
SegmentInfo* curSegment = segmentInfos[i];
bool isTextSeg = ((curSegment->fInitProtection & VM_PROT_EXECUTE) != 0);
std::vector<SectionInfo*>& sectionInfos = curSegment->fSections;
const int sectionCount = sectionInfos.size();
for(int j=0; j < sectionCount; ++j) {
SectionInfo* curSection = sectionInfos[j];
std::vector<ObjectFile::Atom*>& sectionAtoms = curSection->fAtoms;
//printf("writing with max atom size 0x%X\n", fLargestAtomSize);
//fprintf(stderr, "writing %d atoms for section %s\n", (int)sectionAtoms.size(), curSection->fSectionName);
if ( ! curSection->fAllZeroFill ) {
const int atomCount = sectionAtoms.size();
end = curSection->fFileOffset;
bool needsNops = isTextSeg && (strcmp(curSection->fSectionName, "__cstring") != 0);
for (int k=0; k < atomCount; ++k) {
ObjectFile::Atom* atom = sectionAtoms[k];
if ( (atom->getDefinitionKind() != ObjectFile::Atom::kExternalDefinition)
&& (atom->getDefinitionKind() != ObjectFile::Atom::kExternalWeakDefinition) ) {
uint32_t offset = curSection->fFileOffset + atom->getSectionOffset();
if ( offset != end ) {
if ( needsNops ) {
// fill gaps with no-ops
writeNoOps(end, offset);
}
else {
// zero fill gaps
if ( (offset-end) == 4 ) {
uint32_t zero = 0;
::pwrite(fFileDescriptor, &zero, 4, end);
}
else {
uint8_t zero = 0x00;
for (uint32_t p=end; p < offset; ++p)
::pwrite(fFileDescriptor, &zero, 1, p);
}
}
}
uint64_t atomSize = atom->getSize();
if ( atomSize > fLargestAtomSize ) {
throwf("ld64 internal error: atom \"%s\"is larger than expected 0x%X > 0x%llX",
atom->getDisplayName(), atomSize, fLargestAtomSize);
}
end = offset+atomSize;
// copy raw bytes
atom->copyRawContent(buffer);
// apply any fix-ups
try {
std::vector<ObjectFile::Reference*>& references = atom->getReferences();
for (std::vector<ObjectFile::Reference*>::iterator it=references.begin(); it != references.end(); it++) {
ObjectFile::Reference* ref = *it;
if ( fOptions.outputKind() == Options::kObjectFile ) {
// doing ld -r
// skip fix-ups for undefined targets
if ( &(ref->getTarget()) != NULL )
this->fixUpReferenceRelocatable(ref, atom, buffer);
}
else {
// producing final linked image
this->fixUpReferenceFinal(ref, atom, buffer);
}
}
}
catch (const char* msg) {
throwf("%s in %s from %s", msg, atom->getDisplayName(), atom->getFile()->getPath());
}
//fprintf(stderr, "writing 0x%08X -> 0x%08X (addr=0x%llX, size=0x%llX), atom %s from %s\n",
// offset, end, atom->getAddress(), atom->getSize(), atom->getDisplayName(), atom->getFile()->getPath());
// write out
::pwrite(fFileDescriptor, buffer, atom->getSize(), offset);
}
}
}
}
}
delete [] buffer;
close(fFileDescriptor);
return end;
}
template <>
void Writer<x86>::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const
{
uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()];
const int64_t bl_twoGigLimit = 0x7FFFFFFF;
int64_t displacement;
switch ( (x86::ReferenceKinds)(ref->getKind()) ) {
case x86::kNoFixUp:
case x86::kFollowOn:
// do nothing
break;
case x86::kPointerWeakImport:
case x86::kPointer:
{
if ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal ) {
// external realocation ==> pointer contains addend
LittleEndian::set32(*fixUp, ref->getTargetOffset());
}
else {
// pointer contains target address
//printf("Atom::fixUpReferenceFinal() target.name=%s, target.address=0x%08llX\n", target.getDisplayName(), target.getAddress());
LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset());
}
}
break;
case x86::kPointerDiff:
LittleEndian::set32(*fixUp,
(ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) );
break;
case x86::kPCRel32WeakImport:
case x86::kPCRel32:
displacement = 0;
switch ( ref->getTarget().getDefinitionKind() ) {
case ObjectFile::Atom::kRegularDefinition:
case ObjectFile::Atom::kWeakDefinition:
displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4);
break;
case ObjectFile::Atom::kExternalDefinition:
case ObjectFile::Atom::kExternalWeakDefinition:
throw "codegen problem, can't use rel32 to external symbol";
case ObjectFile::Atom::kTentativeDefinition:
displacement = 0;
break;
}
if ( (displacement > bl_twoGigLimit) || (displacement < (-bl_twoGigLimit)) ) {
//fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath());
throw "rel32 out of range";
}
LittleEndian::set32(*fixUp, (int32_t)displacement);
break;
case x86::kAbsolute32:
switch ( ref->getTarget().getDefinitionKind() ) {
case ObjectFile::Atom::kRegularDefinition:
case ObjectFile::Atom::kWeakDefinition:
case ObjectFile::Atom::kTentativeDefinition:
// pointer contains target address
LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset());
break;
case ObjectFile::Atom::kExternalDefinition:
case ObjectFile::Atom::kExternalWeakDefinition:
// external realocation ==> pointer contains addend
LittleEndian::set32(*fixUp, ref->getTargetOffset());
break;
}
break;
}
}
template <>
void Writer<x86>::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const
{
uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()];
bool isExternal = ( (ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kRegularDefinition)
&& shouldExport(ref->getTarget()) );
switch ( (x86::ReferenceKinds)(ref->getKind()) ) {
case x86::kNoFixUp:
case x86::kFollowOn:
// do nothing
break;
case x86::kPointer:
case x86::kPointerWeakImport:
case x86::kAbsolute32:
{
if ( isExternal ) {
// external realocation ==> pointer contains addend
LittleEndian::set32(*fixUp, ref->getTargetOffset());
}
else {
// internal relocation
if ( ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition ) {
// pointer contains target address
LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset());
}
else {
// pointer contains addend
LittleEndian::set32(*fixUp, ref->getTargetOffset());
}
}
}
break;
case x86::kPointerDiff:
LittleEndian::set32(*fixUp,
(ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) );
break;
case x86::kPCRel32:
case x86::kPCRel32WeakImport:
int64_t displacement = 0;
if ( isExternal )
displacement = ref->getTargetOffset() - (inAtom->getAddress() + ref->getFixUpOffset() + 4);
else
displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4);
const int64_t bl_twoGigLimit = 0x7FFFFFFF;
if ( (displacement > bl_twoGigLimit) || (displacement < (-bl_twoGigLimit)) ) {
//fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath());
throw "rel32 out of range";
}
LittleEndian::set32(*fixUp, (int32_t)displacement);
break;
}
}
template <>
void Writer<x86_64>::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const
{
const int64_t twoGigLimit = 0x7FFFFFFF;
uint64_t* fixUp = (uint64_t*)&buffer[ref->getFixUpOffset()];
int64_t displacement = 0;
switch ( (x86_64::ReferenceKinds)(ref->getKind()) ) {
case x86_64::kNoFixUp:
case x86_64::kFollowOn:
// do nothing
break;
case x86_64::kPointerWeakImport:
case x86_64::kPointer:
{
//fprintf(stderr, "fixUpReferenceFinal: %s reference to %s\n", this->getDisplayName(), target.getDisplayName());
if ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal ) {
// external realocation ==> pointer contains addend
LittleEndian::set64(*fixUp, ref->getTargetOffset());
}
else {
// internal relocation
// pointer contains target address
//printf("Atom::fixUpReferenceFinal) target.name=%s, target.address=0x%08llX\n", target.getDisplayName(), target.getAddress());
LittleEndian::set64(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset());
}
}
break;
case x86_64::kPointerDiff32:
displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset());
if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) )
throw "32-bit pointer difference out of range";
LittleEndian::set32(*((uint32_t*)fixUp), (uint32_t)displacement);
break;
case x86_64::kPointerDiff:
LittleEndian::set64(*fixUp,
(ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) );
break;
case x86_64::kBranchPCRel32WeakImport:
case x86_64::kBranchPCRel32:
case x86_64::kPCRel32:
case x86_64::kPCRel32_1:
case x86_64::kPCRel32_2:
case x86_64::kPCRel32_4:
case x86_64::kPCRel32GOT:
case x86_64::kPCRel32GOTWeakImport:
case x86_64::kPCRel32GOTLoad:
case x86_64::kPCRel32GOTLoadWeakImport:
switch ( ref->getTarget().getDefinitionKind() ) {
case ObjectFile::Atom::kRegularDefinition:
case ObjectFile::Atom::kWeakDefinition:
case ObjectFile::Atom::kTentativeDefinition:
displacement = (ref->getTarget().getAddress() + (int32_t)ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4);
break;
case ObjectFile::Atom::kExternalDefinition:
case ObjectFile::Atom::kExternalWeakDefinition:
throw "codegen problem, can't use rel32 to external symbol";
break;
}
switch ( ref->getKind() ) {
case x86_64::kPCRel32_1:
displacement -= 1;
break;
case x86_64::kPCRel32_2:
displacement -= 2;
break;
case x86_64::kPCRel32_4:
displacement -= 4;
break;
}
if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) {
//fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath());
throw "rel32 out of range";
}
LittleEndian::set32(*((uint32_t*)fixUp), (int32_t)displacement);
break;
}
}
template <>
void Writer<x86_64>::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const
{
const int64_t twoGigLimit = 0x7FFFFFFF;
bool external = (ref->getTarget().getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn);
uint64_t* fixUp = (uint64_t*)&buffer[ref->getFixUpOffset()];
int64_t displacement = 0;
int32_t temp32;
switch ( (x86_64::ReferenceKinds)(ref->getKind()) ) {
case x86_64::kNoFixUp:
case x86_64::kFollowOn:
// do nothing
break;
case x86_64::kPointer:
case x86_64::kPointerWeakImport:
{
if ( external ) {
// external realocation ==> pointer contains addend
LittleEndian::set64(*fixUp, ref->getTargetOffset());
}
else {
// internal relocation ==> pointer contains target address
LittleEndian::set64(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset());
}
}
break;
case x86_64::kPointerDiff32:
// addend in content
LittleEndian::set32(*((uint32_t*)fixUp), ref->getTargetOffset() - ref->getFromTargetOffset() );
break;
case x86_64::kPointerDiff:
// addend in content
LittleEndian::set64(*fixUp, ref->getTargetOffset() - ref->getFromTargetOffset() );
break;
case x86_64::kBranchPCRel32:
case x86_64::kBranchPCRel32WeakImport:
case x86_64::kPCRel32:
case x86_64::kPCRel32_1:
case x86_64::kPCRel32_2:
case x86_64::kPCRel32_4:
// turn unsigned 64-bit target offset in signed 32-bit offset, since that is what source originally had
temp32 = ref->getTargetOffset();
if ( external ) {
// extern relocation contains addend
displacement = temp32;
}
else {
// internal relocations contain delta to target address
displacement = (ref->getTarget().getAddress() + temp32) - (inAtom->getAddress() + ref->getFixUpOffset() + 4);
}
switch ( ref->getKind() ) {
case x86_64::kPCRel32_1:
displacement -= 1;
break;
case x86_64::kPCRel32_2:
displacement -= 2;
break;
case x86_64::kPCRel32_4:
displacement -= 4;
break;
}
if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) {
//fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath());
throw "rel32 out of range";
}
LittleEndian::set32(*((uint32_t*)fixUp), (int32_t)displacement);
break;
case x86_64::kPCRel32GOT:
case x86_64::kPCRel32GOTLoad:
case x86_64::kPCRel32GOTWeakImport:
case x86_64::kPCRel32GOTLoadWeakImport:
// contains addend (usually zero)
LittleEndian::set32(*((uint32_t*)fixUp), (uint32_t)(ref->getTargetOffset()));
break;
}
}
template <>
void Writer<ppc>::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const
{
fixUpReference_powerpc(ref, inAtom, buffer, true);
}
template <>
void Writer<ppc64>::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const
{
fixUpReference_powerpc(ref, inAtom, buffer, true);
}
template <>
void Writer<ppc>::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const
{
fixUpReference_powerpc(ref, inAtom, buffer, false);
}
template <>
void Writer<ppc64>::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const
{
fixUpReference_powerpc(ref, inAtom, buffer, false);
}
//
// ppc and ppc64 are mostly the same, so they share a template specialzation
//
template <typename A>
void Writer<A>::fixUpReference_powerpc(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[], bool finalLinkedImage) const
{
uint32_t instruction;
uint32_t newInstruction;
int64_t displacement;
uint64_t targetAddr = ref->getTarget().getAddress() + ref->getTargetOffset();
uint64_t picBaseAddr;
uint16_t instructionLowHalf;
uint16_t instructionHighHalf;
uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()];
pint_t* fixUpPointer = (pint_t*)&buffer[ref->getFixUpOffset()];
bool relocateableExternal;
if ( finalLinkedImage )
relocateableExternal = (relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal);
else
relocateableExternal = ( (ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kRegularDefinition)
&& shouldExport(ref->getTarget()) );
const int64_t picbase_twoGigLimit = 0x80000000;
switch ( (typename A::ReferenceKinds)(ref->getKind()) ) {
case A::kNoFixUp:
case A::kFollowOn:
// do nothing
break;
case A::kPointerWeakImport:
case A::kPointer:
{
//fprintf(stderr, "fixUpReferenceFinal: %s reference to %s\n", this->getDisplayName(), target.getDisplayName());
if ( finalLinkedImage && ((SectionInfo*)inAtom->getSection())->fAllLazyPointers ) {
// lazy-symbol ==> pointer contains address of dyld_stub_binding_helper (stored in "from" target)
if ( fDyldHelper == NULL )
throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)";
P::setP(*fixUpPointer, fDyldHelper->getAddress());
}
else if ( !finalLinkedImage && ((SectionInfo*)inAtom->getSection())->fAllNonLazyPointers ) {
// indirect symbol table has INDIRECT_SYMBOL_LOCAL, so we must put address in content
if ( ref->getTarget().getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn )
P::setP(*fixUpPointer, targetAddr);
else
P::setP(*fixUpPointer, 0);
}
else if ( relocateableExternal ) {
// external realocation ==> pointer contains addend
P::setP(*fixUpPointer, ref->getTargetOffset());
}
else {
// internal relocation
if ( finalLinkedImage || (ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition) ) {
// pointer contains target address
//printf("Atom::fixUpReference_powerpc() target.name=%s, target.address=0x%08llX\n", target.getDisplayName(), target.getAddress());
P::setP(*fixUpPointer, targetAddr);
}
else {
// pointer contains addend
P::setP(*fixUpPointer, ref->getTargetOffset());
}
}
}
break;
case A::kPointerDiff64:
P::setP(*fixUpPointer, targetAddr - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) );
break;
case A::kPointerDiff32:
P::E::set32(*fixUp, targetAddr - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) );
break;
case A::kBranch24WeakImport:
case A::kBranch24:
{
//fprintf(stderr, "bl fixup to %s at 0x%08llX, ", target.getDisplayName(), target.getAddress());
int64_t displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset());
if ( relocateableExternal ) {
// doing "ld -r" to an external symbol
// the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target
displacement -= ref->getTarget().getAddress();
}
else {
const int64_t bl_eightMegLimit = 0x00FFFFFF;
if ( (displacement > bl_eightMegLimit) || (displacement < (-bl_eightMegLimit)) ) {
//fprintf(stderr, "bl out of range (%lld max is +/-16M) from %s in %s to %s in %s\n", displacement, this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath());
throwf("bl out of range (%lld max is +/-16M) from %s in %s to %s in %s",
displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(),
ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath());
}
}
instruction = BigEndian::get32(*fixUp);
newInstruction = (instruction & 0xFC000003) | ((uint32_t)displacement & 0x03FFFFFC);
//fprintf(stderr, "bl fixup: 0x%08X -> 0x%08X\n", instruction, newInstruction);
BigEndian::set32(*fixUp, newInstruction);
}
break;
case A::kBranch14:
{
//fprintf(stderr, "bc fixup %p to %s+0x%08X == 0x%08llX\n", this, ref->getTarget().getDisplayName(), ref->getTargetOffset(), targetAddr);
int64_t displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset());
if ( relocateableExternal ) {
// doing "ld -r" to an external symbol
// the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target
displacement -= ref->getTarget().getAddress();
}
else {
const int64_t b_sixtyFourKiloLimit = 0x0000FFFF;
if ( (displacement > b_sixtyFourKiloLimit) || (displacement < (-b_sixtyFourKiloLimit)) ) {
//fprintf(stderr, "bl out of range (%lld max is +/-16M) from %s in %s to %s in %s\n", displacement, this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath());
throwf("bc out of range (%lld max is +/-64K) from %s in %s to %s in %s",
displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(),
ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath());
}
}
//fprintf(stderr, "bc fixup displacement=0x%08llX, atom.addr=0x%08llX, atom.offset=0x%08X\n", displacement, inAtom->getAddress(), (uint32_t)ref->getFixUpOffset());
instruction = BigEndian::get32(*fixUp);
newInstruction = (instruction & 0xFFFF0003) | ((uint32_t)displacement & 0x0000FFFC);
//fprintf(stderr, "bc fixup: 0x%08X -> 0x%08X\n", instruction, newInstruction);
BigEndian::set32(*fixUp, newInstruction);
}
break;
case A::kPICBaseLow16:
picBaseAddr = inAtom->getAddress() + ref->getFromTargetOffset();
displacement = targetAddr - picBaseAddr;
if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) )
throw "32-bit pic-base out of range";
instructionLowHalf = (displacement & 0xFFFF);
instruction = BigEndian::get32(*fixUp);
newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf;
BigEndian::set32(*fixUp, newInstruction);
break;
case A::kPICBaseLow14:
picBaseAddr = inAtom->getAddress() + ref->getFromTargetOffset();
displacement = targetAddr - picBaseAddr;
if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) )
throw "32-bit pic-base out of range";
if ( (displacement & 0x3) != 0 )
throwf("bad offset (0x%08X) for lo14 instruction pic-base fix-up", (uint32_t)displacement);
instructionLowHalf = (displacement & 0xFFFC);
instruction = BigEndian::get32(*fixUp);
newInstruction = (instruction & 0xFFFF0003) | instructionLowHalf;
BigEndian::set32(*fixUp, newInstruction);
break;
case A::kPICBaseHigh16:
picBaseAddr = inAtom->getAddress() + ref->getFromTargetOffset();
displacement = targetAddr - picBaseAddr;
if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) )
throw "32-bit pic-base out of range";
instructionLowHalf = displacement >> 16;
if ( (displacement & 0x00008000) != 0 )
++instructionLowHalf;
instruction = BigEndian::get32(*fixUp);
newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf;
BigEndian::set32(*fixUp, newInstruction);
break;
case A::kAbsLow16:
if ( relocateableExternal )
targetAddr -= ref->getTarget().getAddress();
instructionLowHalf = (targetAddr & 0xFFFF);
instruction = BigEndian::get32(*fixUp);
newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf;
BigEndian::set32(*fixUp, newInstruction);
break;
case A::kAbsLow14:
if ( relocateableExternal )
targetAddr -= ref->getTarget().getAddress();
if ( (targetAddr & 0x3) != 0 )
throw "bad address for absolute lo14 instruction fix-up";
instructionLowHalf = (targetAddr & 0xFFFF);
instruction = BigEndian::get32(*fixUp);
newInstruction = (instruction & 0xFFFF0003) | instructionLowHalf;
BigEndian::set32(*fixUp, newInstruction);
break;
case A::kAbsHigh16:
if ( relocateableExternal )
targetAddr -= ref->getTarget().getAddress();
instructionHighHalf = (targetAddr >> 16);
instruction = BigEndian::get32(*fixUp);
newInstruction = (instruction & 0xFFFF0000) | instructionHighHalf;
BigEndian::set32(*fixUp, newInstruction);
break;
case A::kAbsHigh16AddLow:
if ( relocateableExternal )
targetAddr -= ref->getTarget().getAddress();
if ( targetAddr & 0x00008000 )
targetAddr += 0x00010000;
instruction = BigEndian::get32(*fixUp);
newInstruction = (instruction & 0xFFFF0000) | (targetAddr >> 16);
BigEndian::set32(*fixUp, newInstruction);
break;
}
}
template <>
bool Writer<ppc>::stubableReferenceKind(uint8_t kind)
{
return (kind == ppc::kBranch24 || kind == ppc::kBranch24WeakImport);
}
template <>
bool Writer<ppc64>::stubableReferenceKind(uint8_t kind)
{
return (kind == ppc64::kBranch24 || kind == ppc64::kBranch24WeakImport);
}
template <>
bool Writer<x86>::stubableReferenceKind(uint8_t kind)
{
return (kind == x86::kPCRel32 || kind == x86::kPCRel32WeakImport);
}
template <>
bool Writer<x86_64>::stubableReferenceKind(uint8_t kind)
{
return (kind == x86_64::kBranchPCRel32 || kind == x86_64::kBranchPCRel32WeakImport);
}
template <>
bool Writer<ppc>::weakImportReferenceKind(uint8_t kind)
{
return (kind == ppc::kBranch24WeakImport || kind == ppc::kPointerWeakImport);
}
template <>
bool Writer<ppc64>::weakImportReferenceKind(uint8_t kind)
{
return (kind == ppc64::kBranch24WeakImport || kind == ppc64::kPointerWeakImport);
}
template <>
bool Writer<x86>::weakImportReferenceKind(uint8_t kind)
{
return (kind == x86::kPCRel32WeakImport || kind == x86::kPointerWeakImport);
}
template <>
bool Writer<x86_64>::weakImportReferenceKind(uint8_t kind)
{
switch ( kind ) {
case x86_64::kPointerWeakImport:
case x86_64::kBranchPCRel32WeakImport:
case x86_64::kPCRel32GOTWeakImport:
case x86_64::kPCRel32GOTLoadWeakImport:
return true;
}
return false;
}
template <>
bool Writer<ppc>::GOTReferenceKind(uint8_t kind)
{
return false;
}
template <>
bool Writer<ppc64>::GOTReferenceKind(uint8_t kind)
{
return false;
}
template <>
bool Writer<x86>::GOTReferenceKind(uint8_t kind)
{
return false;
}
template <>
bool Writer<x86_64>::GOTReferenceKind(uint8_t kind)
{
switch ( kind ) {
case x86_64::kPCRel32GOT:
case x86_64::kPCRel32GOTWeakImport:
case x86_64::kPCRel32GOTLoad:
case x86_64::kPCRel32GOTLoadWeakImport:
return true;
}
return false;
}
template <typename A>
void Writer<A>::scanForAbsoluteReferences()
{
// do nothing
}
// for ppc64 look for any -mdynamic-no-pic codegen
template <>
void Writer<ppc64>::scanForAbsoluteReferences()
{
// only do this for main executable
if ( mightNeedPadSegment() && (fPageZeroAtom != NULL) ) {
for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) {
ObjectFile::Atom* atom = *it;
std::vector<ObjectFile::Reference*>& references = atom->getReferences();
for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) {
ObjectFile::Reference* ref = *rit;
switch (ref->getKind()) {
case ppc64::kAbsLow16:
case ppc64::kAbsLow14:
case ppc64::kAbsHigh16:
case ppc64::kAbsHigh16AddLow:
//fprintf(stderr, "found -mdyanmic-no-pic codegen in %s in %s\n", atom->getDisplayName(), atom->getFile()->getPath());
// shrink page-zero and add pad segment to compensate
fPadSegmentInfo = new SegmentInfo();
strcpy(fPadSegmentInfo->fName, "__4BGFILL");
fPageZeroAtom->setSize(0x1000);
return;
}
}
}
}
}
template <typename A>
void Writer<A>::synthesizeStubs()
{
switch ( fOptions.outputKind() ) {
case Options::kStaticExecutable:
case Options::kObjectFile:
// these output kinds never have stubs
return;
case Options::kDyld:
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
case Options::kDynamicExecutable:
// try to synthesize stubs for these
break;
}
// walk every atom and reference
for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) {
ObjectFile::Atom* atom = *it;
std::vector<ObjectFile::Reference*>& references = atom->getReferences();
for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) {
ObjectFile::Reference* ref = *rit;
ObjectFile::Atom& target = ref->getTarget();
// build map of which symbols need weak importing
if ( (target.getDefinitionKind() == ObjectFile::Atom::kExternalDefinition)
|| (target.getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) {
bool weakImport = this->weakImportReferenceKind(ref->getKind());
std::map<const ObjectFile::Atom*,bool>::iterator pos = fWeakImportMap.find(&target);
if ( pos == fWeakImportMap.end() ) {
// target not in fWeakImportMap, so add
fWeakImportMap[&target] = weakImport;
}
else {
// target in fWeakImportMap, check for weakness mismatch
if ( pos->second != weakImport ) {
// found mismatch
switch ( fOptions.weakReferenceMismatchTreatment() ) {
case Options::kWeakReferenceMismatchError:
throwf("mismatching weak references for symbol: %s", target.getName());
case Options::kWeakReferenceMismatchWeak:
pos->second = true;
break;
case Options::kWeakReferenceMismatchNonWeak:
pos->second = false;
break;
}
}
}
}
// create stubs as needed
if ( this->stubableReferenceKind(ref->getKind())
&& this->relocationNeededInFinalLinkedImage(target) == kRelocExternal ) {
ObjectFile::Atom* stub = NULL;
std::map<ObjectFile::Atom*,ObjectFile::Atom*>::iterator pos = fStubsMap.find(&target);
if ( pos == fStubsMap.end() ) {
stub = new StubAtom<A>(*this, target);
fStubsMap[&target] = stub;
}
else {
stub = pos->second;
}
// alter reference to use stub instead
ref->setTarget(*stub, 0);
}
// create GOT slots (non-lazy pointers) as needed
else if ( this->GOTReferenceKind(ref->getKind()) ) {
ObjectFile::Atom* nlp = NULL;
std::map<ObjectFile::Atom*,ObjectFile::Atom*>::iterator pos = fGOTMap.find(&target);
if ( pos == fGOTMap.end() ) {
nlp = new NonLazyPointerAtom<A>(*this, target);
fGOTMap[&target] = nlp;
}
else {
nlp = pos->second;
}
// alter reference to use non lazy pointer instead
ref->setTarget(*nlp, ref->getTargetOffset());
}
}
}
// sort stubs
// sort lazy pointers
// add stubs to fAllAtoms
if ( fAllSynthesizedStubs.size() != 0 ) {
std::vector<ObjectFile::Atom*>* stubs = (std::vector<ObjectFile::Atom*>*)&fAllSynthesizedStubs;
std::vector<ObjectFile::Atom*> mergedStubs;
if ( fAllSynthesizedStubHelpers.size() != 0 ) {
// when we have stubs and helpers, insert both into fAllAtoms
mergedStubs.insert(mergedStubs.end(), fAllSynthesizedStubs.begin(), fAllSynthesizedStubs.end());
mergedStubs.insert(mergedStubs.end(), fAllSynthesizedStubHelpers.begin(), fAllSynthesizedStubHelpers.end());
stubs = &mergedStubs;
}
ObjectFile::Section* curSection = NULL;
ObjectFile::Atom* prevAtom = NULL;
for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) {
ObjectFile::Atom* atom = *it;
ObjectFile::Section* nextSection = atom->getSection();
if ( nextSection != curSection ) {
// HACK HACK for i386 where stubs are not in _TEXT segment
if ( strcmp(fAllSynthesizedStubs[0]->getSegment().getName(), "__IMPORT") == 0 ) {
if ( ((prevAtom != NULL) && (strcmp(prevAtom->getSegment().getName(), "__IMPORT") == 0))
|| (strcmp(atom->getSegment().getName(), "__LINKEDIT") == 0) ) {
// insert stubs at end of __IMPORT segment, or before __LINKEDIT
fAllAtoms->insert(it, fAllSynthesizedStubs.begin(), fAllSynthesizedStubs.end());
break;
}
}
else {
if ( (prevAtom != NULL) && (strcmp(prevAtom->getSectionName(), "__text") == 0) ) {
// found end of __text section, insert stubs here
fAllAtoms->insert(it, stubs->begin(), stubs->end());
break;
}
}
curSection = nextSection;
}
prevAtom = atom;
}
}
// add lazy pointers to fAllAtoms
if ( fAllSynthesizedLazyPointers.size() != 0 ) {
ObjectFile::Section* curSection = NULL;
ObjectFile::Atom* prevAtom = NULL;
bool inserted = false;
for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) {
ObjectFile::Atom* atom = *it;
ObjectFile::Section* nextSection = atom->getSection();
if ( nextSection != curSection ) {
if ( (prevAtom != NULL) && (strcmp(prevAtom->getSectionName(), "__dyld") == 0) ) {
// found end of __dyld section, insert lazy pointers here
fAllAtoms->insert(it, fAllSynthesizedLazyPointers.begin(), fAllSynthesizedLazyPointers.end());
inserted = true;
break;
}
curSection = nextSection;
}
prevAtom = atom;
}
if ( !inserted ) {
throw "can't insert lazy pointers, __dyld section not found";
}
}
// add non-lazy pointers to fAllAtoms
if ( fAllSynthesizedNonLazyPointers.size() != 0 ) {
ObjectFile::Section* curSection = NULL;
ObjectFile::Atom* prevAtom = NULL;
bool inserted = false;
for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) {
ObjectFile::Atom* atom = *it;
ObjectFile::Section* nextSection = atom->getSection();
if ( nextSection != curSection ) {
if ( (prevAtom != NULL)
&& ((strcmp(prevAtom->getSectionName(), "__dyld") == 0)
|| ((fOptions.outputKind() == Options::kDyld) && (strcmp(prevAtom->getSectionName(), "__data") == 0))) ) {
// found end of __dyld section, insert lazy pointers here
fAllAtoms->insert(it, fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end());
inserted = true;
break;
}
curSection = nextSection;
}
prevAtom = atom;
}
if ( !inserted ) {
throw "can't insert non-lazy pointers, __dyld section not found";
}
}
}
template <typename A>
void Writer<A>::partitionIntoSections()
{
const bool oneSegmentCommand = (fOptions.outputKind() == Options::kObjectFile);
// for every atom, set its sectionInfo object and section offset
// build up fSegmentInfos along the way
ObjectFile::Section* curSection = NULL;
SectionInfo* currentSectionInfo = NULL;
SegmentInfo* currentSegmentInfo = NULL;
unsigned int sectionIndex = 1;
fSegmentInfos.reserve(8);
for (unsigned int i=0; i < fAllAtoms->size(); ++i) {
ObjectFile::Atom* atom = (*fAllAtoms)[i];
if ( (atom->getSection() != curSection) || ((curSection==NULL) && (strcmp(atom->getSectionName(),currentSectionInfo->fSectionName) != 0)) ) {
if ( oneSegmentCommand ) {
if ( currentSegmentInfo == NULL ) {
currentSegmentInfo = new SegmentInfo();
currentSegmentInfo->fInitProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE;
currentSegmentInfo->fMaxProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE;
this->fSegmentInfos.push_back(currentSegmentInfo);
}
currentSectionInfo = new SectionInfo();
strcpy(currentSectionInfo->fSectionName, atom->getSectionName());
strcpy(currentSectionInfo->fSegmentName, atom->getSegment().getName());
currentSectionInfo->fAlignment = atom->getAlignment();
currentSectionInfo->fAllZeroFill = atom->isZeroFill();
currentSectionInfo->fVirtualSection = ( (currentSectionInfo->fSectionName[0] == '.') ||
(oneSegmentCommand && (atom->getDefinitionKind()==ObjectFile::Atom::kTentativeDefinition)) && !fOptions.makeTentativeDefinitionsReal() );
if ( !currentSectionInfo->fVirtualSection || fEmitVirtualSections )
currentSectionInfo->setIndex(sectionIndex++);
currentSegmentInfo->fSections.push_back(currentSectionInfo);
}
else {
if ( (currentSegmentInfo == NULL) || (strcmp(currentSegmentInfo->fName, atom->getSegment().getName()) != 0) ) {
currentSegmentInfo = new SegmentInfo();
strcpy(currentSegmentInfo->fName, atom->getSegment().getName());
uint32_t initprot = 0;
if ( atom->getSegment().isContentReadable() )
initprot |= VM_PROT_READ;
if ( atom->getSegment().isContentWritable() )
initprot |= VM_PROT_WRITE;
if ( atom->getSegment().isContentExecutable() )
initprot |= VM_PROT_EXECUTE;
currentSegmentInfo->fInitProtection = initprot;
if ( initprot == 0 )
currentSegmentInfo->fMaxProtection = 0; // pagezero should have maxprot==initprot==0
else
currentSegmentInfo->fMaxProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE;
currentSegmentInfo->fBaseAddress = atom->getSegment().getBaseAddress();
currentSegmentInfo->fFixedAddress = atom->getSegment().hasFixedAddress();
this->fSegmentInfos.push_back(currentSegmentInfo);
}
currentSectionInfo = new SectionInfo();
currentSectionInfo->fAtoms.reserve(fAllAtoms->size()/4); // reduce reallocations by starting large
strcpy(currentSectionInfo->fSectionName, atom->getSectionName());
strcpy(currentSectionInfo->fSegmentName, atom->getSegment().getName());
currentSectionInfo->fAlignment = atom->getAlignment();
// check for -sectalign override
std::vector<Options::SectionAlignment>& alignmentOverrides = fOptions.sectionAlignments();
for(std::vector<Options::SectionAlignment>::iterator it=alignmentOverrides.begin(); it != alignmentOverrides.end(); ++it) {
if ( (strcmp(it->segmentName, currentSectionInfo->fSegmentName) == 0) && (strcmp(it->sectionName, currentSectionInfo->fSectionName) == 0) )
currentSectionInfo->fAlignment = it->alignment;
}
currentSectionInfo->fAllZeroFill = atom->isZeroFill();
currentSectionInfo->fVirtualSection = ( currentSectionInfo->fSectionName[0] == '.');
if ( !currentSectionInfo->fVirtualSection || fEmitVirtualSections )
currentSectionInfo->setIndex(sectionIndex++);
currentSegmentInfo->fSections.push_back(currentSectionInfo);
}
if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "._load_commands") == 0) ) {
fLoadCommandsSection = currentSectionInfo;
fLoadCommandsSegment = currentSegmentInfo;
}
if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__la_symbol_ptr") == 0) )
currentSectionInfo->fAllLazyPointers = true;
if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__la_sym_ptr2") == 0) )
currentSectionInfo->fAllLazyPointers = true;
if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__nl_symbol_ptr") == 0) )
currentSectionInfo->fAllNonLazyPointers = true;
if ( (strcmp(currentSectionInfo->fSegmentName, "__IMPORT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__pointers") == 0) )
currentSectionInfo->fAllNonLazyPointers = true;
if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__picsymbolstub1") == 0) )
currentSectionInfo->fAllStubs = true;
if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__symbol_stub1") == 0) )
currentSectionInfo->fAllStubs = true;
if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__picsymbolstub2") == 0) )
currentSectionInfo->fAllStubs = true;
if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__symbol_stub") == 0) )
currentSectionInfo->fAllStubs = true;
if ( (strcmp(currentSectionInfo->fSegmentName, "__IMPORT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__jump_table") == 0) )
currentSectionInfo->fAllSelfModifyingStubs = true;
curSection = atom->getSection();
}
// any non-zero fill atoms make whole section marked not-zero-fill
if ( currentSectionInfo->fAllZeroFill && ! atom->isZeroFill() )
currentSectionInfo->fAllZeroFill = false;
// change section object to be Writer's SectionInfo object
atom->setSection(currentSectionInfo);
// section alignment is that of a contained atom with the greatest alignment
uint8_t atomAlign = atom->getAlignment();
if ( currentSectionInfo->fAlignment < atomAlign )
currentSectionInfo->fAlignment = atomAlign;
// calculate section offset for this atom
uint64_t offset = currentSectionInfo->fSize;
uint64_t alignment = 1 << atomAlign;
offset = ( (offset+alignment-1) & (-alignment) );
atom->setSectionOffset(offset);
uint64_t curAtomSize = atom->getSize();
currentSectionInfo->fSize = offset + curAtomSize;
// add atom to section vector
currentSectionInfo->fAtoms.push_back(atom);
// update largest size
if ( !currentSectionInfo->fAllZeroFill && (curAtomSize > fLargestAtomSize) )
fLargestAtomSize = curAtomSize;
}
}
struct TargetAndOffset { ObjectFile::Atom* atom; uint32_t offset; };
class TargetAndOffsetComparor
{
public:
bool operator()(const TargetAndOffset& left, const TargetAndOffset& right) const
{
if ( left.atom != right.atom )
return ( left.atom < right.atom );
return ( left.offset < right.offset );
}
};
template <>
bool Writer<ppc>::addBranchIslands()
{
return this->addPPCBranchIslands();
}
template <>
bool Writer<ppc64>::addBranchIslands()
{
return this->addPPCBranchIslands();
}
template <>
bool Writer<x86>::addBranchIslands()
{
// x86 branches can reach entire 4G address space, so no need for branch islands
return false;
}
template <>
bool Writer<x86_64>::addBranchIslands()
{
// x86 branches can reach entire 4G size of largest image
return false;
}
template <>
inline uint8_t Writer<ppc>::branch24Reference()
{
return ppc::kBranch24;
}
template <>
inline uint8_t Writer<ppc64>::branch24Reference()
{
return ppc64::kBranch24;
}
//
// PowerPC can do PC relative branches as far as +/-16MB.
// If a branch target is >16MB then we insert one or more
// "branch islands" between the branch and its target that
// allows island hoping to the target.
//
// Branch Island Algorithm
//
// If the __TEXT segment < 16MB, then no branch islands needed
// Otherwise, every 15MB into the __TEXT segment is region is
// added which can contain branch islands. Every out of range
// bl instruction is checked. If it crosses a region, an island
// is added to that region with the same target and the bl is
// adjusted to target the island instead.
//
// In theory, if too many islands are added to one region, it
// could grow the __TEXT enough that other previously in-range
// bl branches could be pushed out of range. We reduce the
// probability this could happen by placing the ranges every
// 15MB which means the region would have to be 1MB (256K islands)
// before any branches could be pushed out of range.
//
template <typename A>
bool Writer<A>::addPPCBranchIslands()
{
bool result = false;
// Can only possibly need branch islands if __TEXT segment > 16M
if ( fLoadCommandsSegment->fSize > 16000000 ) {
//fprintf(stderr, "ld64: checking for branch islands, __TEXT segment size=%llu\n", fLoadCommandsSegment->fSize);
const uint32_t kBetweenRegions = 15000000; // place regions of islands every 15MB in __text section
SectionInfo* textSection = NULL;
for (std::vector<SectionInfo*>::iterator it=fLoadCommandsSegment->fSections.begin(); it != fLoadCommandsSegment->fSections.end(); it++) {
if ( strcmp((*it)->fSectionName, "__text") == 0 ) {
textSection = *it;
//fprintf(stderr, "ld64: checking for branch islands, __text section size=%llu\n", textSection->fSize);
break;
}
}
const int kIslandRegionsCount = fLoadCommandsSegment->fSize / kBetweenRegions;
typedef std::map<TargetAndOffset,ObjectFile::Atom*, TargetAndOffsetComparor> AtomToIsland;
AtomToIsland regionsMap[kIslandRegionsCount];
std::vector<ObjectFile::Atom*> regionsIslands[kIslandRegionsCount];
unsigned int islandCount = 0;
// create islands for branch references that are out of range
for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) {
ObjectFile::Atom* atom = *it;
std::vector<ObjectFile::Reference*>& references = atom->getReferences();
for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) {
ObjectFile::Reference* ref = *rit;
if ( ref->getKind() == this->branch24Reference() ) {
ObjectFile::Atom& target = ref->getTarget();
int64_t srcAddr = atom->getAddress() + ref->getFixUpOffset();
int64_t dstAddr = target.getAddress() + ref->getTargetOffset();
int64_t displacement = dstAddr - srcAddr;
const int64_t kFifteenMegLimit = kBetweenRegions;
if ( (displacement > kFifteenMegLimit) || (displacement < (-kFifteenMegLimit)) ) {
for (int i=0; i < kIslandRegionsCount; ++i) {
AtomToIsland* region=®ionsMap[i];
int64_t islandRegionAddr = kBetweenRegions * (i+1);
if ( ((srcAddr < islandRegionAddr) && (dstAddr > islandRegionAddr))
||((dstAddr < islandRegionAddr) && (srcAddr > islandRegionAddr)) ) {
TargetAndOffset islandTarget = { &target, ref->getTargetOffset() };
AtomToIsland::iterator pos = region->find(islandTarget);
if ( pos == region->end() ) {
BranchIslandAtom<A>* island = new BranchIslandAtom<A>(*this, target.getDisplayName(), i, target, ref->getTargetOffset());
island->setSection(textSection);
(*region)[islandTarget] = island;
regionsIslands[i].push_back(island);
++islandCount;
ref->setTarget(*island, 0);
}
else {
ref->setTarget(*(pos->second), 0);
}
}
}
}
}
}
}
// insert islands into __text section and adjust section offsets
if ( islandCount > 0 ) {
//fprintf(stderr, "ld64: %u branch islands required\n", islandCount);
std::vector<ObjectFile::Atom*> newAtomList;
newAtomList.reserve(textSection->fAtoms.size()+islandCount);
uint64_t islandRegionAddr = kBetweenRegions;
int regionIndex = 0;
uint64_t sectionOffset = 0;
for (std::vector<ObjectFile::Atom*>::iterator it=textSection->fAtoms.begin(); it != textSection->fAtoms.end(); it++) {
ObjectFile::Atom* atom = *it;
newAtomList.push_back(atom);
if ( atom->getAddress() > islandRegionAddr ) {
std::vector<ObjectFile::Atom*>* regionIslands = ®ionsIslands[regionIndex];
for (std::vector<ObjectFile::Atom*>::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) {
ObjectFile::Atom* islandAtom = *rit;
newAtomList.push_back(islandAtom);
uint64_t alignment = 1 << (islandAtom->getAlignment());
sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) );
islandAtom->setSectionOffset(sectionOffset);
sectionOffset += islandAtom->getSize();
}
++regionIndex;
islandRegionAddr += kBetweenRegions;
}
uint64_t alignment = 1 << (atom->getAlignment());
sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) );
atom->setSectionOffset(sectionOffset);
sectionOffset += atom->getSize();
}
// put any remaining islands at end of __text section
if ( regionIndex < kIslandRegionsCount ) {
sectionOffset = textSection->fSize;
std::vector<ObjectFile::Atom*>* regionIslands = ®ionsIslands[regionIndex];
for (std::vector<ObjectFile::Atom*>::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) {
ObjectFile::Atom* islandAtom = *rit;
newAtomList.push_back(islandAtom);
uint64_t alignment = 1 << (islandAtom->getAlignment());
sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) );
islandAtom->setSectionOffset(sectionOffset);
sectionOffset += islandAtom->getSize();
}
}
textSection->fAtoms = newAtomList;
textSection->fSize = sectionOffset;
result = true;
}
}
return result;
}
template <typename A>
void Writer<A>::adjustLoadCommandsAndPadding()
{
fSegmentCommands->computeSize();
// recompute load command section offsets
uint64_t offset = 0;
std::vector<class ObjectFile::Atom*>& loadCommandAtoms = fLoadCommandsSection->fAtoms;
const unsigned int atomCount = loadCommandAtoms.size();
for (unsigned int i=0; i < atomCount; ++i) {
ObjectFile::Atom* atom = loadCommandAtoms[i];
uint64_t alignment = 1 << atom->getAlignment();
offset = ( (offset+alignment-1) & (-alignment) );
atom->setSectionOffset(offset);
uint32_t atomSize = atom->getSize();
if ( atomSize > fLargestAtomSize )
fLargestAtomSize = atomSize;
offset += atomSize;
fLoadCommandsSection->fSize = offset;
}
std::vector<SectionInfo*>& sectionInfos = fLoadCommandsSegment->fSections;
const int sectionCount = sectionInfos.size();
uint64_t paddingSize = 0;
if ( fOptions.outputKind() == Options::kDyld ) {
// dyld itself has special padding requirements. We want the beginning __text section to start at a stable address
uint32_t totalSizeOfHeaderAndLoadCommands = 0;
for(int j=0; j < sectionCount; ++j) {
SectionInfo* curSection = sectionInfos[j];
totalSizeOfHeaderAndLoadCommands += curSection->fSize;
if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 )
break;
}
paddingSize = 4096 - (totalSizeOfHeaderAndLoadCommands % 4096);
}
else if ( fOptions.outputKind() == Options::kObjectFile ) {
// mach-o .o files need no padding between load commands and first section
paddingSize = 0;
}
else {
// calculate max padding to keep segment size same, but all free space at end of load commands
uint64_t totalSize = 0;
uint64_t worstCaseAlignmentPadding = 0;
for(int j=0; j < sectionCount; ++j) {
SectionInfo* curSection = sectionInfos[j];
totalSize += curSection->fSize;
if ( j != 0 ) // don't count aligment of mach_header which is page-aligned
worstCaseAlignmentPadding += (1 << curSection->fAlignment) - 1;
}
uint64_t segmentSize = ((totalSize+worstCaseAlignmentPadding+4095) & (-4096));
// don't know exactly how it will layout, but we can inflate padding atom this big and still keep aligment constraints
paddingSize = segmentSize - (totalSize+worstCaseAlignmentPadding);
// if command line requires more padding than this
if ( paddingSize < fOptions.minimumHeaderPad() ) {
int extraPages = (fOptions.minimumHeaderPad() - paddingSize + 4095)/4096;
paddingSize += extraPages * 4096;
}
}
// adjust atom size and update section size
fHeaderPadding->setSize(paddingSize);
for(int j=0; j < sectionCount; ++j) {
SectionInfo* curSection = sectionInfos[j];
if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 )
curSection->fSize = paddingSize;
}
}
// assign file offsets and logical address to all segments
template <typename A>
void Writer<A>::assignFileOffsets()
{
bool finalLinkedImage = (fOptions.outputKind() != Options::kObjectFile);
bool haveFixedSegments = false;
uint64_t fileOffset = 0;
uint64_t nextContiguousAddress = fOptions.baseAddress();
// Run through the segments and each segment's sections to assign addresses
for (std::vector<SegmentInfo*>::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) {
SegmentInfo* curSegment = *segit;
fileOffset = (fileOffset+4095) & (-4096);
curSegment->fFileOffset = fileOffset;
// Set the segment base address
if ( curSegment->fFixedAddress )
haveFixedSegments = true;
else
curSegment->fBaseAddress = nextContiguousAddress;
// We've set the segment address, now run through each section.
uint64_t address = curSegment->fBaseAddress;
SectionInfo* firstZeroFillSection = NULL;
SectionInfo* prevSection = NULL;
std::vector<SectionInfo*>& sectionInfos = curSegment->fSections;
for (std::vector<SectionInfo*>::iterator it = sectionInfos.begin(); it != sectionInfos.end(); ++it) {
SectionInfo* curSection = *it;
// adjust section address based on alignment
uint64_t alignment = 1 << curSection->fAlignment;
address = ( (address+alignment-1) & (-alignment) );
// adjust file offset to match address
if ( prevSection != NULL ) {
if ( finalLinkedImage || !prevSection->fVirtualSection )
fileOffset = (address - prevSection->getBaseAddress()) + prevSection->fFileOffset;
else
fileOffset = ( (fileOffset+alignment-1) & (-alignment) );
}
// update section info
curSection->fFileOffset = fileOffset;
curSection->setBaseAddress(address);
// keep track of trailing zero fill sections
if ( curSection->fAllZeroFill && (firstZeroFillSection == NULL) )
firstZeroFillSection = curSection;
if ( !curSection->fAllZeroFill && (firstZeroFillSection != NULL) && finalLinkedImage )
throwf("zero-fill section %s not at end of segment", curSection->fSectionName);
// update running pointers
if ( finalLinkedImage || !curSection->fVirtualSection )
address += curSection->fSize;
fileOffset += curSection->fSize;
// update segment info
curSegment->fFileSize = fileOffset - curSegment->fFileOffset;
curSegment->fSize = curSegment->fFileSize;
prevSection = curSection;
}
if ( fOptions.outputKind() == Options::kObjectFile ) {
// don't page align .o files
}
else {
// optimize trailing zero-fill sections to not occupy disk space
if ( firstZeroFillSection != NULL ) {
curSegment->fFileSize = firstZeroFillSection->fFileOffset - curSegment->fFileOffset;
fileOffset = firstZeroFillSection->fFileOffset;
}
// page align segment size
curSegment->fFileSize = (curSegment->fFileSize+4095) & (-4096);
curSegment->fSize = (curSegment->fSize+4095) & (-4096);
if ( curSegment->fBaseAddress == nextContiguousAddress )
nextContiguousAddress = (curSegment->fBaseAddress+curSegment->fSize+4095) & (-4096);
}
}
// check for segment overlaps caused by user specified fixed segments (e.g. __PAGEZERO, __UNIXSTACK)
if ( haveFixedSegments ) {
int segCount = fSegmentInfos.size();
for(int i=0; i < segCount; ++i) {
SegmentInfo* segment1 = fSegmentInfos[i];
for(int j=0; j < segCount; ++j) {
if ( i != j ) {
SegmentInfo* segment2 = fSegmentInfos[j];
if ( segment1->fBaseAddress < segment2->fBaseAddress ) {
if ( (segment1->fBaseAddress+segment1->fSize) > segment2->fBaseAddress )
throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)",
segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize);
}
else if ( segment1->fBaseAddress > segment2->fBaseAddress ) {
if ( (segment2->fBaseAddress+segment2->fSize) > segment1->fBaseAddress )
throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)",
segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize);
}
else if ( (segment1->fSize != 0) && (segment2->fSize != 0) ) {
throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)",
segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize);
}
}
}
}
}
// set up fFirstWritableSegment and fWritableSegmentPastFirst4GB
for (std::vector<SegmentInfo*>::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) {
SegmentInfo* curSegment = *segit;
if ( (curSegment->fInitProtection & VM_PROT_WRITE) != 0 ) {
if ( fFirstWritableSegment == NULL )
fFirstWritableSegment = curSegment;
if ( (curSegment->fBaseAddress + curSegment->fSize - fOptions.baseAddress()) >= 0x100000000LL )
fWritableSegmentPastFirst4GB = true;
}
}
}
template <typename A>
void Writer<A>::adjustLinkEditSections()
{
// link edit content is always in last segment
SegmentInfo* lastSeg = fSegmentInfos[fSegmentInfos.size()-1];
unsigned int firstLinkEditSectionIndex = 0;
while ( strcmp(lastSeg->fSections[firstLinkEditSectionIndex]->fSegmentName, "__LINKEDIT") != 0 )
++firstLinkEditSectionIndex;
const unsigned int sectionCount = lastSeg->fSections.size();
uint64_t fileOffset = lastSeg->fSections[firstLinkEditSectionIndex]->fFileOffset;
uint64_t address = lastSeg->fSections[firstLinkEditSectionIndex]->getBaseAddress();
if ( fPadSegmentInfo != NULL ) {
// insert __4GBFILL segment into segments vector before LINKEDIT
for(std::vector<SegmentInfo*>::iterator it = fSegmentInfos.begin(); it != fSegmentInfos.end(); ++it) {
if ( *it == lastSeg ) {
fSegmentInfos.insert(it, fPadSegmentInfo);
break;
}
}
// adjust __4GBFILL segment to span from end of last segment to zeroPageSize
fPadSegmentInfo->fSize = fOptions.zeroPageSize() - address;
fPadSegmentInfo->fBaseAddress = address;
// adjust LINKEDIT to start at zeroPageSize
address = fOptions.zeroPageSize();
lastSeg->fBaseAddress = fOptions.zeroPageSize();
}
for (unsigned int i=firstLinkEditSectionIndex; i < sectionCount; ++i) {
std::vector<class ObjectFile::Atom*>& atoms = lastSeg->fSections[i]->fAtoms;
const unsigned int atomCount = atoms.size();
uint64_t sectionOffset = 0;
lastSeg->fSections[i]->fFileOffset = fileOffset;
lastSeg->fSections[i]->setBaseAddress(address);
for (unsigned int j=0; j < atomCount; ++j) {
ObjectFile::Atom* atom = atoms[j];
uint64_t alignment = 1 << atom->getAlignment();
sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) );
atom->setSectionOffset(sectionOffset);
uint64_t size = atom->getSize();
sectionOffset += size;
if ( size > fLargestAtomSize )
fLargestAtomSize = size;
}
//fprintf(stderr, "setting: lastSeg->fSections[%d]->fSize = 0x%08llX\n", i, sectionOffset);
lastSeg->fSections[i]->fSize = sectionOffset;
fileOffset += sectionOffset;
address += sectionOffset;
}
if ( fOptions.outputKind() == Options::kObjectFile ) {
//lastSeg->fBaseAddress = 0;
//lastSeg->fSize = lastSeg->fSections[firstLinkEditSectionIndex]->
//lastSeg->fFileOffset = 0;
//lastSeg->fFileSize =
}
else {
lastSeg->fFileSize = fileOffset - lastSeg->fFileOffset;
lastSeg->fSize = (address - lastSeg->fBaseAddress+4095) & (-4096);
}
}
template <typename A>
ObjectFile::Atom::Scope MachHeaderAtom<A>::getScope() const
{
switch ( fWriter.fOptions.outputKind() ) {
case Options::kDynamicExecutable:
case Options::kStaticExecutable:
return ObjectFile::Atom::scopeGlobal;
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
case Options::kDyld:
case Options::kObjectFile:
return ObjectFile::Atom::scopeLinkageUnit;
}
throw "unknown header type";
}
template <typename A>
ObjectFile::Atom::SymbolTableInclusion MachHeaderAtom<A>::getSymbolTableInclusion() const
{
switch ( fWriter.fOptions.outputKind() ) {
case Options::kDynamicExecutable:
case Options::kStaticExecutable:
return ObjectFile::Atom::kSymbolTableInAndNeverStrip;
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
case Options::kDyld:
return ObjectFile::Atom::kSymbolTableIn;
case Options::kObjectFile:
return ObjectFile::Atom::kSymbolTableNotIn;
}
throw "unknown header type";
}
template <typename A>
const char* MachHeaderAtom<A>::getName() const
{
switch ( fWriter.fOptions.outputKind() ) {
case Options::kDynamicExecutable:
case Options::kStaticExecutable:
return "__mh_execute_header";
case Options::kDynamicLibrary:
return "__mh_dylib_header";
case Options::kDynamicBundle:
return "__mh_bundle_header";
case Options::kObjectFile:
return NULL;
case Options::kDyld:
return "__mh_dylinker_header";
}
throw "unknown header type";
}
template <typename A>
const char* MachHeaderAtom<A>::getDisplayName() const
{
switch ( fWriter.fOptions.outputKind() ) {
case Options::kDynamicExecutable:
case Options::kStaticExecutable:
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
case Options::kDyld:
return this->getName();
case Options::kObjectFile:
return "mach header";
}
throw "unknown header type";
}
template <typename A>
void MachHeaderAtom<A>::copyRawContent(uint8_t buffer[]) const
{
// get file type
uint32_t fileType = 0;
switch ( fWriter.fOptions.outputKind() ) {
case Options::kDynamicExecutable:
case Options::kStaticExecutable:
fileType = MH_EXECUTE;
break;
case Options::kDynamicLibrary:
fileType = MH_DYLIB;
break;
case Options::kDynamicBundle:
fileType = MH_BUNDLE;
break;
case Options::kObjectFile:
fileType = MH_OBJECT;
break;
case Options::kDyld:
fileType = MH_DYLINKER;
break;
}
// get flags
uint32_t flags = 0;
if ( fWriter.fOptions.outputKind() == Options::kObjectFile ) {
if ( ! fWriter.fSeenFollowOnReferences )
flags = MH_SUBSECTIONS_VIA_SYMBOLS;
}
else {
if ( fWriter.fOptions.outputKind() == Options::kStaticExecutable ) {
flags |= MH_NOUNDEFS;
}
else {
flags = MH_DYLDLINK;
if ( fWriter.fOptions.bindAtLoad() )
flags |= MH_BINDATLOAD;
switch ( fWriter.fOptions.nameSpace() ) {
case Options::kTwoLevelNameSpace:
flags |= MH_TWOLEVEL | MH_NOUNDEFS;
break;
case Options::kFlatNameSpace:
break;
case Options::kForceFlatNameSpace:
flags |= MH_FORCE_FLAT;
break;
}
if ( fWriter.fHasWeakExports )
flags |= MH_WEAK_DEFINES;
if ( fWriter.fReferencesWeakImports || fWriter.fHasWeakExports )
flags |= MH_BINDS_TO_WEAK;
}
if ( fWriter.fOptions.hasExecutableStack() )
flags |= MH_ALLOW_STACK_EXECUTION;
}
// get commands info
uint32_t commandsSize = 0;
uint32_t commandsCount = 0;
std::vector<class ObjectFile::Atom*>& loadCommandAtoms = fWriter.fLoadCommandsSection->fAtoms;
const unsigned int atomCount = loadCommandAtoms.size();
for (unsigned int i=0; i < atomCount; ++i) {
ObjectFile::Atom* atom = loadCommandAtoms[i];
commandsSize += atom->getSize();
// segment and symbol table atoms can contain more than one load command
if ( atom == fWriter.fSegmentCommands )
commandsCount += fWriter.fSegmentCommands->commandCount();
else if ( atom == fWriter.fSymbolTableCommands )
commandsCount += fWriter.fSymbolTableCommands->commandCount();
else if ( atom->getSize() != 0)
++commandsCount;
}
// fill out mach_header
macho_header<typename A::P>* mh = (macho_header<typename A::P>*)buffer;
setHeaderInfo(*mh);
mh->set_filetype(fileType);
mh->set_ncmds(commandsCount);
mh->set_sizeofcmds(commandsSize);
mh->set_flags(flags);
}
template <>
void MachHeaderAtom<ppc>::setHeaderInfo(macho_header<ppc::P>& header) const
{
header.set_magic(MH_MAGIC);
header.set_cputype(CPU_TYPE_POWERPC);
header.set_cpusubtype(CPU_SUBTYPE_POWERPC_ALL);
}
template <>
void MachHeaderAtom<ppc64>::setHeaderInfo(macho_header<ppc64::P>& header) const
{
header.set_magic(MH_MAGIC_64);
header.set_cputype(CPU_TYPE_POWERPC64);
header.set_cpusubtype(CPU_SUBTYPE_POWERPC_ALL);
header.set_reserved(0);
}
template <>
void MachHeaderAtom<x86>::setHeaderInfo(macho_header<x86::P>& header) const
{
header.set_magic(MH_MAGIC);
header.set_cputype(CPU_TYPE_I386);
header.set_cpusubtype(CPU_SUBTYPE_I386_ALL);
}
template <>
void MachHeaderAtom<x86_64>::setHeaderInfo(macho_header<x86_64::P>& header) const
{
header.set_magic(MH_MAGIC_64);
header.set_cputype(CPU_TYPE_X86_64);
header.set_cpusubtype(CPU_SUBTYPE_X86_64_ALL);
}
template <typename A>
CustomStackAtom<A>::CustomStackAtom(Writer<A>& writer)
: WriterAtom<A>(writer, Segment::fgStackSegment)
{
if ( stackGrowsDown() )
Segment::fgStackSegment.setBaseAddress(writer.fOptions.customStackAddr() - writer.fOptions.customStackSize());
else
Segment::fgStackSegment.setBaseAddress(writer.fOptions.customStackAddr());
}
template <>
bool CustomStackAtom<ppc>::stackGrowsDown()
{
return true;
}
template <>
bool CustomStackAtom<ppc64>::stackGrowsDown()
{
return true;
}
template <>
bool CustomStackAtom<x86>::stackGrowsDown()
{
return true;
}
template <>
bool CustomStackAtom<x86_64>::stackGrowsDown()
{
return true;
}
template <typename A>
void SegmentLoadCommandsAtom<A>::computeSize()
{
uint64_t size = 0;
std::vector<SegmentInfo*>& segmentInfos = fWriter.fSegmentInfos;
const int segCount = segmentInfos.size();
for(int i=0; i < segCount; ++i) {
size += sizeof(macho_segment_command<P>);
std::vector<SectionInfo*>& sectionInfos = segmentInfos[i]->fSections;
const int sectionCount = sectionInfos.size();
for(int j=0; j < sectionCount; ++j) {
if ( fWriter.fEmitVirtualSections || ! sectionInfos[j]->fVirtualSection )
size += sizeof(macho_section<P>);
}
}
fSize = size;
fCommandCount = segCount;
if ( fWriter.fPadSegmentInfo != NULL ) {
++fCommandCount;
fSize += sizeof(macho_segment_command<P>);
}
}
template <>
uint64_t LoadCommandAtom<ppc>::alignedSize(uint64_t size)
{
return ((size+3) & (-4)); // 4-byte align all load commands for 32-bit mach-o
}
template <>
uint64_t LoadCommandAtom<ppc64>::alignedSize(uint64_t size)
{
return ((size+7) & (-8)); // 8-byte align all load commands for 64-bit mach-o
}
template <>
uint64_t LoadCommandAtom<x86>::alignedSize(uint64_t size)
{
return ((size+3) & (-4)); // 4-byte align all load commands for 32-bit mach-o
}
template <>
uint64_t LoadCommandAtom<x86_64>::alignedSize(uint64_t size)
{
return ((size+7) & (-8)); // 8-byte align all load commands for 64-bit mach-o
}
template <typename A>
void SegmentLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
{
uint64_t size = this->getSize();
const bool oneSegment =( fWriter.fOptions.outputKind() == Options::kObjectFile );
bzero(buffer, size);
uint8_t* p = buffer;
typename std::vector<SegmentInfo*>& segmentInfos = fWriter.fSegmentInfos;
const int segCount = segmentInfos.size();
for(int i=0; i < segCount; ++i) {
SegmentInfo* segInfo = segmentInfos[i];
const int sectionCount = segInfo->fSections.size();
macho_segment_command<P>* cmd = (macho_segment_command<P>*)p;
cmd->set_cmd(macho_segment_command<P>::CMD);
cmd->set_segname(segInfo->fName);
cmd->set_vmaddr(segInfo->fBaseAddress);
cmd->set_vmsize(segInfo->fSize);
cmd->set_fileoff(segInfo->fFileOffset);
cmd->set_filesize(segInfo->fFileSize);
cmd->set_maxprot(segInfo->fMaxProtection);
cmd->set_initprot(segInfo->fInitProtection);
// add sections array
macho_section<P>* const sections = (macho_section<P>*)&p[sizeof(macho_segment_command<P>)];
unsigned int sectionsEmitted = 0;
for (int j=0; j < sectionCount; ++j) {
SectionInfo* sectInfo = segInfo->fSections[j];
if ( fWriter.fEmitVirtualSections || !sectInfo->fVirtualSection ) {
macho_section<P>* sect = §ions[sectionsEmitted++];
if ( oneSegment ) {
// .o file segment does not cover load commands, so recalc at first real section
if ( sectionsEmitted == 1 ) {
cmd->set_vmaddr(sectInfo->getBaseAddress());
cmd->set_fileoff(sectInfo->fFileOffset);
}
cmd->set_filesize((sectInfo->fFileOffset+sectInfo->fSize)-cmd->fileoff());
cmd->set_vmsize(sectInfo->getBaseAddress() + sectInfo->fSize);
}
sect->set_sectname(sectInfo->fSectionName);
sect->set_segname(sectInfo->fSegmentName);
sect->set_addr(sectInfo->getBaseAddress());
sect->set_size(sectInfo->fSize);
sect->set_offset(sectInfo->fFileOffset);
sect->set_align(sectInfo->fAlignment);
if ( sectInfo->fRelocCount != 0 ) {
sect->set_reloff(sectInfo->fRelocOffset * sizeof(macho_relocation_info<P>) + fWriter.fSectionRelocationsAtom->getFileOffset());
sect->set_nreloc(sectInfo->fRelocCount);
}
if ( sectInfo->fAllZeroFill ) {
sect->set_flags(S_ZEROFILL);
sect->set_offset(0);
}
else if ( sectInfo->fAllLazyPointers ) {
sect->set_flags(S_LAZY_SYMBOL_POINTERS);
sect->set_reserved1(sectInfo->fIndirectSymbolOffset);
}
else if ( sectInfo->fAllNonLazyPointers ) {
sect->set_flags(S_NON_LAZY_SYMBOL_POINTERS);
sect->set_reserved1(sectInfo->fIndirectSymbolOffset);
}
else if ( sectInfo->fAllStubs ) {
sect->set_flags(S_SYMBOL_STUBS | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS);
sect->set_reserved1(sectInfo->fIndirectSymbolOffset);
sect->set_reserved2(sectInfo->fSize / sectInfo->fAtoms.size());
}
else if ( sectInfo->fAllSelfModifyingStubs ) {
sect->set_flags(S_SYMBOL_STUBS | S_ATTR_SELF_MODIFYING_CODE);
sect->set_reserved1(sectInfo->fIndirectSymbolOffset);
sect->set_reserved2(sectInfo->fSize / sectInfo->fAtoms.size());
}
else if ( (strcmp(sectInfo->fSectionName, "__mod_init_func") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) {
sect->set_flags(S_MOD_INIT_FUNC_POINTERS);
}
else if ( (strcmp(sectInfo->fSectionName, "__mod_term_func") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) {
sect->set_flags(S_MOD_TERM_FUNC_POINTERS);
}
else if ( (strcmp(sectInfo->fSectionName, "__eh_frame") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) {
sect->set_flags(S_COALESCED);
}
else if ( (strcmp(sectInfo->fSectionName, "__textcoal_nt") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) {
sect->set_flags(S_COALESCED);
}
else if ( (strcmp(sectInfo->fSectionName, "__const_coal") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) {
sect->set_flags(S_COALESCED);
}
else if ( (strcmp(sectInfo->fSectionName, "__interpose") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) {
sect->set_flags(S_INTERPOSING);
}
else if ( (strcmp(sectInfo->fSectionName, "__cstring") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) {
sect->set_flags(S_CSTRING_LITERALS);
}
else if ( (strcmp(sectInfo->fSectionName, "__literal4") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) {
sect->set_flags(S_4BYTE_LITERALS);
}
else if ( (strcmp(sectInfo->fSectionName, "__literal8") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) {
sect->set_flags(S_8BYTE_LITERALS);
}
else if ( (strcmp(sectInfo->fSectionName, "__literal16") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) {
sect->set_flags(S_16BYTE_LITERALS);
}
else if ( (strcmp(sectInfo->fSectionName, "__message_refs") == 0) && (strcmp(sectInfo->fSegmentName, "__OBJC") == 0) ) {
sect->set_flags(S_LITERAL_POINTERS);
}
else if ( (strncmp(sectInfo->fSectionName, "__text", 6) == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) {
sect->set_flags(S_REGULAR | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS);
}
}
}
p = &p[sizeof(macho_segment_command<P>) + sectionsEmitted*sizeof(macho_section<P>)];
cmd->set_cmdsize(sizeof(macho_segment_command<P>) + sectionsEmitted*sizeof(macho_section<P>));
cmd->set_nsects(sectionsEmitted);
}
}
template <typename A>
SymbolTableLoadCommandsAtom<A>::SymbolTableLoadCommandsAtom(Writer<A>& writer)
: LoadCommandAtom<A>(writer, Segment::fgTextSegment)
{
bzero(&fSymbolTable, sizeof(macho_symtab_command<P>));
bzero(&fDynamicSymbolTable, sizeof(macho_dysymtab_command<P>));
writer.fSymbolTableCommands = this;
}
template <typename A>
uint64_t SymbolTableLoadCommandsAtom<A>::getSize() const
{
if ( fWriter.fOptions.outputKind() == Options::kStaticExecutable )
return this->alignedSize(sizeof(macho_symtab_command<P>));
else
return this->alignedSize(sizeof(macho_symtab_command<P>) + sizeof(macho_dysymtab_command<P>));
}
template <typename A>
void SymbolTableLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
{
// build LC_DYSYMTAB command
macho_symtab_command<P>* symbolTableCmd = (macho_symtab_command<P>*)buffer;
bzero(symbolTableCmd, sizeof(macho_symtab_command<P>));
symbolTableCmd->set_cmd(LC_SYMTAB);
symbolTableCmd->set_cmdsize(sizeof(macho_symtab_command<P>));
symbolTableCmd->set_nsyms(fWriter.fSymbolTableCount);
symbolTableCmd->set_symoff(fWriter.fSymbolTableAtom->getFileOffset());
symbolTableCmd->set_stroff(fWriter.fStringsAtom->getFileOffset());
symbolTableCmd->set_strsize(fWriter.fStringsAtom->getSize());
// build LC_DYSYMTAB command
if ( fWriter.fOptions.outputKind() != Options::kStaticExecutable ) {
macho_dysymtab_command<P>* dynamicSymbolTableCmd = (macho_dysymtab_command<P>*)&buffer[sizeof(macho_symtab_command<P>)];
bzero(dynamicSymbolTableCmd, sizeof(macho_dysymtab_command<P>));
dynamicSymbolTableCmd->set_cmd(LC_DYSYMTAB);
dynamicSymbolTableCmd->set_cmdsize(sizeof(macho_dysymtab_command<P>));
dynamicSymbolTableCmd->set_ilocalsym(fWriter.fSymbolTableStabsStartIndex);
dynamicSymbolTableCmd->set_nlocalsym(fWriter.fSymbolTableStabsCount + fWriter.fSymbolTableLocalCount);
dynamicSymbolTableCmd->set_iextdefsym(fWriter.fSymbolTableExportStartIndex);
dynamicSymbolTableCmd->set_nextdefsym(fWriter.fSymbolTableExportCount);
dynamicSymbolTableCmd->set_iundefsym(fWriter.fSymbolTableImportStartIndex);
dynamicSymbolTableCmd->set_nundefsym(fWriter.fSymbolTableImportCount);
dynamicSymbolTableCmd->set_indirectsymoff(fWriter.fIndirectTableAtom->getFileOffset());
dynamicSymbolTableCmd->set_nindirectsyms(fWriter.fIndirectTableAtom->fTable.size());
if ( fWriter.fOptions.outputKind() != Options::kObjectFile ) {
dynamicSymbolTableCmd->set_extreloff((fWriter.fExternalRelocs.size()==0) ? 0 : fWriter.fExternalRelocationsAtom->getFileOffset());
dynamicSymbolTableCmd->set_nextrel(fWriter.fExternalRelocs.size());
dynamicSymbolTableCmd->set_locreloff((fWriter.fInternalRelocs.size()==0) ? 0 : fWriter.fLocalRelocationsAtom->getFileOffset());
dynamicSymbolTableCmd->set_nlocrel(fWriter.fInternalRelocs.size());
}
}
}
template <typename A>
unsigned int SymbolTableLoadCommandsAtom<A>::commandCount()
{
return (fWriter.fOptions.outputKind() == Options::kStaticExecutable) ? 1 : 2;
}
template <typename A>
uint64_t DyldLoadCommandsAtom<A>::getSize() const
{
return this->alignedSize(sizeof(macho_dylinker_command<P>) + strlen("/usr/lib/dyld") + 1);
}
template <typename A>
void DyldLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
{
uint64_t size = this->getSize();
bzero(buffer, size);
macho_dylinker_command<P>* cmd = (macho_dylinker_command<P>*)buffer;
if ( fWriter.fOptions.outputKind() == Options::kDyld )
cmd->set_cmd(LC_ID_DYLINKER);
else
cmd->set_cmd(LC_LOAD_DYLINKER);
cmd->set_cmdsize(this->getSize());
cmd->set_name_offset();
strcpy((char*)&buffer[sizeof(macho_dylinker_command<P>)], "/usr/lib/dyld");
}
template <typename A>
uint64_t AllowableClientLoadCommandsAtom<A>::getSize() const
{
return this->alignedSize(sizeof(macho_sub_client_command<P>) + strlen(this->clientString) + 1);
}
template <typename A>
void AllowableClientLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
{
uint64_t size = this->getSize();
bzero(buffer, size);
macho_sub_client_command<P>* cmd = (macho_sub_client_command<P>*)buffer;
cmd->set_cmd(LC_SUB_CLIENT);
cmd->set_cmdsize(size);
cmd->set_client_offset();
strcpy((char*)&buffer[sizeof(macho_sub_client_command<P>)], this->clientString);
}
template <typename A>
uint64_t DylibLoadCommandsAtom<A>::getSize() const
{
const char* path = fInfo.reader->getInstallPath();
if ( fInfo.options.fInstallPathOverride != NULL )
path = fInfo.options.fInstallPathOverride;
return this->alignedSize(sizeof(macho_dylib_command<P>) + strlen(path) + 1);
}
template <typename A>
void DylibLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
{
uint64_t size = this->getSize();
bzero(buffer, size);
const char* path = fInfo.reader->getInstallPath();
if ( fInfo.options.fInstallPathOverride != NULL )
path = fInfo.options.fInstallPathOverride;
macho_dylib_command<P>* cmd = (macho_dylib_command<P>*)buffer;
if ( fInfo.options.fWeakImport )
cmd->set_cmd(LC_LOAD_WEAK_DYLIB);
else
cmd->set_cmd(LC_LOAD_DYLIB);
cmd->set_cmdsize(this->getSize());
cmd->set_timestamp(fInfo.reader->getTimestamp());
cmd->set_current_version(fInfo.reader->getCurrentVersion());
cmd->set_compatibility_version(fInfo.reader->getCompatibilityVersion());
cmd->set_name_offset();
strcpy((char*)&buffer[sizeof(macho_dylib_command<P>)], path);
}
template <typename A>
uint64_t DylibIDLoadCommandsAtom<A>::getSize() const
{
return this->alignedSize(sizeof(macho_dylib_command<P>) + strlen(fWriter.fOptions.installPath()) + 1);
}
template <typename A>
void DylibIDLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
{
struct timeval currentTime = { 0 , 0 };
gettimeofday(¤tTime, NULL);
time_t timestamp = currentTime.tv_sec;
uint64_t size = this->getSize();
bzero(buffer, size);
macho_dylib_command<P>* cmd = (macho_dylib_command<P>*)buffer;
cmd->set_cmd(LC_ID_DYLIB);
cmd->set_cmdsize(this->getSize());
cmd->set_name_offset();
cmd->set_timestamp(timestamp);
cmd->set_current_version(fWriter.fOptions.currentVersion());
cmd->set_compatibility_version(fWriter.fOptions.compatibilityVersion());
strcpy((char*)&buffer[sizeof(macho_dylib_command<P>)], fWriter.fOptions.installPath());
}
template <typename A>
void RoutinesLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
{
uint64_t initAddr = fWriter.getAtomLoadAddress(fWriter.fEntryPoint);
bzero(buffer, sizeof(macho_routines_command<P>));
macho_routines_command<P>* cmd = (macho_routines_command<P>*)buffer;
cmd->set_cmd(macho_routines_command<P>::CMD);
cmd->set_cmdsize(this->getSize());
cmd->set_init_address(initAddr);
}
template <typename A>
uint64_t SubUmbrellaLoadCommandsAtom<A>::getSize() const
{
return this->alignedSize(sizeof(macho_sub_umbrella_command<P>) + strlen(fName) + 1);
}
template <typename A>
void SubUmbrellaLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
{
uint64_t size = this->getSize();
bzero(buffer, size);
macho_sub_umbrella_command<P>* cmd = (macho_sub_umbrella_command<P>*)buffer;
cmd->set_cmd(LC_SUB_UMBRELLA);
cmd->set_cmdsize(this->getSize());
cmd->set_sub_umbrella_offset();
strcpy((char*)&buffer[sizeof(macho_sub_umbrella_command<P>)], fName);
}
template <typename A>
void UUIDLoadCommandAtom<A>::copyRawContent(uint8_t buffer[]) const
{
if (fEmit) {
uint64_t size = this->getSize();
bzero(buffer, size);
macho_uuid_command<P>* cmd = (macho_uuid_command<P>*)buffer;
cmd->set_cmd(LC_UUID);
cmd->set_cmdsize(this->getSize());
cmd->set_uuid((uint8_t*)fUUID);
}
}
template <typename A>
uint64_t SubLibraryLoadCommandsAtom<A>::getSize() const
{
return this->alignedSize(sizeof(macho_sub_library_command<P>) + fNameLength + 1);
}
template <typename A>
void SubLibraryLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
{
uint64_t size = this->getSize();
bzero(buffer, size);
macho_sub_library_command<P>* cmd = (macho_sub_library_command<P>*)buffer;
cmd->set_cmd(LC_SUB_LIBRARY);
cmd->set_cmdsize(this->getSize());
cmd->set_sub_library_offset();
strncpy((char*)&buffer[sizeof(macho_sub_library_command<P>)], fNameStart, fNameLength);
buffer[sizeof(macho_sub_library_command<P>)+fNameLength] = '\0';
}
template <typename A>
uint64_t UmbrellaLoadCommandsAtom<A>::getSize() const
{
return this->alignedSize(sizeof(macho_sub_framework_command<P>) + strlen(fName) + 1);
}
template <typename A>
void UmbrellaLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
{
uint64_t size = this->getSize();
bzero(buffer, size);
macho_sub_framework_command<P>* cmd = (macho_sub_framework_command<P>*)buffer;
cmd->set_cmd(LC_SUB_FRAMEWORK);
cmd->set_cmdsize(this->getSize());
cmd->set_umbrella_offset();
strcpy((char*)&buffer[sizeof(macho_sub_framework_command<P>)], fName);
}
template <>
uint64_t ThreadsLoadCommandsAtom<ppc>::getSize() const
{
return this->alignedSize(16 + 40*4); // base size + PPC_THREAD_STATE_COUNT * 4
}
template <>
uint64_t ThreadsLoadCommandsAtom<ppc64>::getSize() const
{
return this->alignedSize(16 + 76*4); // base size + PPC_THREAD_STATE64_COUNT * 4
}
template <>
uint64_t ThreadsLoadCommandsAtom<x86>::getSize() const
{
return this->alignedSize(16 + 16*4); // base size + i386_THREAD_STATE_COUNT * 4
}
template <>
uint64_t ThreadsLoadCommandsAtom<x86_64>::getSize() const
{
return this->alignedSize(16 + x86_THREAD_STATE64_COUNT * 4);
}
template <>
void ThreadsLoadCommandsAtom<ppc>::copyRawContent(uint8_t buffer[]) const
{
uint64_t size = this->getSize();
uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint);
bzero(buffer, size);
macho_thread_command<ppc::P>* cmd = (macho_thread_command<ppc::P>*)buffer;
cmd->set_cmd(LC_UNIXTHREAD);
cmd->set_cmdsize(size);
cmd->set_flavor(1); // PPC_THREAD_STATE
cmd->set_count(40); // PPC_THREAD_STATE_COUNT;
cmd->set_thread_register(0, start);
if ( fWriter.fOptions.hasCustomStack() )
cmd->set_thread_register(3, fWriter.fOptions.customStackAddr()); // r1
}
template <>
void ThreadsLoadCommandsAtom<ppc64>::copyRawContent(uint8_t buffer[]) const
{
uint64_t size = this->getSize();
uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint);
bzero(buffer, size);
macho_thread_command<ppc64::P>* cmd = (macho_thread_command<ppc64::P>*)buffer;
cmd->set_cmd(LC_UNIXTHREAD);
cmd->set_cmdsize(size);
cmd->set_flavor(5); // PPC_THREAD_STATE64
cmd->set_count(76); // PPC_THREAD_STATE64_COUNT;
cmd->set_thread_register(0, start);
if ( fWriter.fOptions.hasCustomStack() )
cmd->set_thread_register(3, fWriter.fOptions.customStackAddr()); // r1
}
template <>
void ThreadsLoadCommandsAtom<x86>::copyRawContent(uint8_t buffer[]) const
{
uint64_t size = this->getSize();
uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint);
bzero(buffer, size);
macho_thread_command<x86::P>* cmd = (macho_thread_command<x86::P>*)buffer;
cmd->set_cmd(LC_UNIXTHREAD);
cmd->set_cmdsize(size);
cmd->set_flavor(1); // i386_THREAD_STATE
cmd->set_count(16); // i386_THREAD_STATE_COUNT;
cmd->set_thread_register(10, start);
if ( fWriter.fOptions.hasCustomStack() )
cmd->set_thread_register(15, fWriter.fOptions.customStackAddr()); // uesp
}
template <>
void ThreadsLoadCommandsAtom<x86_64>::copyRawContent(uint8_t buffer[]) const
{
uint64_t size = this->getSize();
uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint);
bzero(buffer, size);
macho_thread_command<x86_64::P>* cmd = (macho_thread_command<x86_64::P>*)buffer;
cmd->set_cmd(LC_UNIXTHREAD);
cmd->set_cmdsize(size);
cmd->set_flavor(x86_THREAD_STATE64);
cmd->set_count(x86_THREAD_STATE64_COUNT);
cmd->set_thread_register(16, start); // rip
if ( fWriter.fOptions.hasCustomStack() )
cmd->set_thread_register(7, fWriter.fOptions.customStackAddr()); // uesp
}
template <typename A>
void LoadCommandsPaddingAtom<A>::copyRawContent(uint8_t buffer[]) const
{
bzero(buffer, fSize);
}
template <typename A>
void LoadCommandsPaddingAtom<A>::setSize(uint64_t newSize)
{
fSize = newSize;
// this resizing by-passes the way fLargestAtomSize is set, so re-check here
if ( fWriter.fLargestAtomSize < newSize )
fWriter.fLargestAtomSize = newSize;
}
template <typename A>
uint64_t LinkEditAtom<A>::getFileOffset() const
{
return ((SectionInfo*)this->getSection())->fFileOffset + this->getSectionOffset();
}
template <typename A>
uint64_t SectionRelocationsLinkEditAtom<A>::getSize() const
{
return fWriter.fSectionRelocs.size() * sizeof(macho_relocation_info<P>);
}
template <typename A>
void SectionRelocationsLinkEditAtom<A>::copyRawContent(uint8_t buffer[]) const
{
memcpy(buffer, &fWriter.fSectionRelocs[0], this->getSize());
}
template <typename A>
uint64_t LocalRelocationsLinkEditAtom<A>::getSize() const
{
return fWriter.fInternalRelocs.size() * sizeof(macho_relocation_info<P>);
}
template <typename A>
void LocalRelocationsLinkEditAtom<A>::copyRawContent(uint8_t buffer[]) const
{
memcpy(buffer, &fWriter.fInternalRelocs[0], this->getSize());
}
template <typename A>
uint64_t SymbolTableLinkEditAtom<A>::getSize() const
{
return fWriter.fSymbolTableCount * sizeof(macho_nlist<P>);
}
template <typename A>
void SymbolTableLinkEditAtom<A>::copyRawContent(uint8_t buffer[]) const
{
memcpy(buffer, fWriter.fSymbolTable, this->getSize());
}
template <typename A>
uint64_t ExternalRelocationsLinkEditAtom<A>::getSize() const
{
return fWriter.fExternalRelocs.size() * sizeof(macho_relocation_info<P>);
}
template <typename A>
void ExternalRelocationsLinkEditAtom<A>::copyRawContent(uint8_t buffer[]) const
{
memcpy(buffer, &fWriter.fExternalRelocs[0], this->getSize());
}
template <typename A>
uint64_t IndirectTableLinkEditAtom<A>::getSize() const
{
return fTable.size() * sizeof(uint32_t);
}
template <typename A>
void IndirectTableLinkEditAtom<A>::copyRawContent(uint8_t buffer[]) const
{
uint64_t size = this->getSize();
bzero(buffer, size);
const uint32_t indirectTableSize = fTable.size();
uint32_t* indirectTable = (uint32_t*)buffer;
for(std::vector<IndirectEntry>::const_iterator it = fTable.begin(); it != fTable.end(); ++it) {
if ( it->indirectIndex < indirectTableSize ) {
A::P::E::set32(indirectTable[it->indirectIndex], it->symbolIndex);
}
else {
throwf("malformed indirect table. size=%d, index=%d", indirectTableSize, it->indirectIndex);
}
}
}
template <typename A>
StringsLinkEditAtom<A>::StringsLinkEditAtom(Writer<A>& writer)
: LinkEditAtom<A>(writer), fCurrentBuffer(NULL), fCurrentBufferUsed(0)
{
fCurrentBuffer = new char[kBufferSize];
// burn first byte of string pool (so zero is never a valid string offset)
fCurrentBuffer[fCurrentBufferUsed++] = ' ';
// make offset 1 always point to an empty string
fCurrentBuffer[fCurrentBufferUsed++] = '\0';
}
template <typename A>
uint64_t StringsLinkEditAtom<A>::getSize() const
{
return kBufferSize * fFullBuffers.size() + fCurrentBufferUsed;
}
template <typename A>
void StringsLinkEditAtom<A>::copyRawContent(uint8_t buffer[]) const
{
uint64_t offset = 0;
for (unsigned int i=0; i < fFullBuffers.size(); ++i) {
memcpy(&buffer[offset], fFullBuffers[i], kBufferSize);
offset += kBufferSize;
}
memcpy(&buffer[offset], fCurrentBuffer, fCurrentBufferUsed);
}
template <typename A>
int32_t StringsLinkEditAtom<A>::add(const char* name)
{
int32_t offset = kBufferSize * fFullBuffers.size() + fCurrentBufferUsed;
int lenNeeded = strlcpy(&fCurrentBuffer[fCurrentBufferUsed], name, kBufferSize-fCurrentBufferUsed)+1;
if ( (fCurrentBufferUsed+lenNeeded) < kBufferSize ) {
fCurrentBufferUsed += lenNeeded;
}
else {
int copied = kBufferSize-fCurrentBufferUsed-1;
// change trailing '\0' that strlcpy added to real char
fCurrentBuffer[kBufferSize-1] = name[copied];
// alloc next buffer
fFullBuffers.push_back(fCurrentBuffer);
fCurrentBuffer = new char[kBufferSize];
fCurrentBufferUsed = 0;
// append rest of string
this->add(&name[copied+1]);
}
return offset;
}
template <typename A>
int32_t StringsLinkEditAtom<A>::addUnique(const char* name)
{
StringToOffset::iterator pos = fUniqueStrings.find(name);
if ( pos != fUniqueStrings.end() ) {
return pos->second;
}
else {
int32_t offset = this->add(name);
fUniqueStrings[name] = offset;
return offset;
}
}
template <typename A>
BranchIslandAtom<A>::BranchIslandAtom(Writer<A>& writer, const char* name, int islandRegion, ObjectFile::Atom& target, uint32_t targetOffset)
: WriterAtom<A>(writer, Segment::fgTextSegment), fTarget(target), fTargetOffset(targetOffset)
{
char* buf = new char[strlen(name)+32];
if ( targetOffset == 0 ) {
if ( islandRegion == 0 )
sprintf(buf, "%s$island", name);
else
sprintf(buf, "%s$island_%d", name, islandRegion);
}
else {
sprintf(buf, "%s_plus_%d$island_%d", name, targetOffset, islandRegion);
}
fName = buf;
}
template <>
void BranchIslandAtom<ppc>::copyRawContent(uint8_t buffer[]) const
{
int64_t displacement = fTarget.getAddress() + fTargetOffset - this->getAddress();
int32_t branchInstruction = 0x48000000 | ((uint32_t)displacement & 0x03FFFFFC);
OSWriteBigInt32(buffer, 0, branchInstruction);
}
template <>
void BranchIslandAtom<ppc64>::copyRawContent(uint8_t buffer[]) const
{
int64_t displacement = fTarget.getAddress() + fTargetOffset - this->getAddress();
int32_t branchInstruction = 0x48000000 | ((uint32_t)displacement & 0x03FFFFFC);
OSWriteBigInt32(buffer, 0, branchInstruction);
}
template <>
uint64_t BranchIslandAtom<ppc>::getSize() const
{
return 4;
}
template <>
uint64_t BranchIslandAtom<ppc64>::getSize() const
{
return 4;
}
template <>
bool StubAtom<ppc64>::pic() const
{
// no-pic stubs for ppc64 don't work if lazy pointer is above low 2GB.
// This usually only happens when a large zero-page is requested
switch ( fWriter.fOptions.outputKind() ) {
case Options::kDynamicExecutable:
return (fWriter.fPageZeroAtom->getSize() > 4096);
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
return true;
case Options::kObjectFile:
case Options::kDyld:
case Options::kStaticExecutable:
break;
}
throw "internal ld64 error: file type does not use stubs";
}
template <>
bool StubAtom<ppc>::pic() const
{
return ( fWriter.fOptions.outputKind() != Options::kDynamicExecutable );
}
template <>
StubAtom<ppc>::StubAtom(Writer<ppc>& writer, ObjectFile::Atom& target)
: WriterAtom<ppc>(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target)
{
writer.fAllSynthesizedStubs.push_back(this);
LazyPointerAtom<ppc>* lp = new LazyPointerAtom<ppc>(writer, target);
if ( pic() ) {
// picbase is 8 bytes into atom
fReferences.push_back(new WriterReference<ppc>(12, ppc::kPICBaseHigh16, lp, 0, NULL, 8));
fReferences.push_back(new WriterReference<ppc>(20, ppc::kPICBaseLow16, lp, 0, NULL, 8));
}
else {
fReferences.push_back(new WriterReference<ppc>(0, ppc::kAbsHigh16AddLow, lp));
fReferences.push_back(new WriterReference<ppc>(4, ppc::kAbsLow16, lp));
}
}
template <>
StubAtom<ppc64>::StubAtom(Writer<ppc64>& writer, ObjectFile::Atom& target)
: WriterAtom<ppc64>(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target)
{
writer.fAllSynthesizedStubs.push_back(this);
LazyPointerAtom<ppc64>* lp = new LazyPointerAtom<ppc64>(writer, target);
if ( pic() ) {
// picbase is 8 bytes into atom
fReferences.push_back(new WriterReference<ppc64>(12, ppc64::kPICBaseHigh16, lp, 0, NULL, 8));
fReferences.push_back(new WriterReference<ppc64>(20, ppc64::kPICBaseLow14, lp, 0, NULL, 8));
}
else {
fReferences.push_back(new WriterReference<ppc64>(0, ppc64::kAbsHigh16AddLow, lp));
fReferences.push_back(new WriterReference<ppc64>(4, ppc64::kAbsLow14, lp));
}
}
// specialize to put x86 fast stub in __IMPORT segment with no lazy pointer
template <>
StubAtom<x86>::StubAtom(Writer<x86>& writer, ObjectFile::Atom& target)
: WriterAtom<x86>(writer, Segment::fgImportSegment), fName(stubName(target.getName())), fTarget(target)
{
writer.fAllSynthesizedStubs.push_back(this);
}
template <>
StubAtom<x86_64>::StubAtom(Writer<x86_64>& writer, ObjectFile::Atom& target)
: WriterAtom<x86_64>(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target)
{
writer.fAllSynthesizedStubs.push_back(this);
LazyPointerAtom<x86_64>* lp = new LazyPointerAtom<x86_64>(writer, target);
fReferences.push_back(new WriterReference<x86_64>(2, x86_64::kPCRel32, lp));
}
template <typename A>
const char* StubAtom<A>::stubName(const char* name)
{
char* buf;
asprintf(&buf, "%s$stub", name);
return buf;
}
template <>
uint64_t StubAtom<ppc>::getSize() const
{
return ( pic() ? 32 : 16 );
}
template <>
uint64_t StubAtom<ppc64>::getSize() const
{
return ( pic() ? 32 : 16 );
}
template <>
uint64_t StubAtom<x86>::getSize() const
{
return 5;
}
template <>
uint64_t StubAtom<x86_64>::getSize() const
{
return 6;
}
template <>
uint8_t StubAtom<x86>::getAlignment() const
{
// special case x86 fast stubs to be byte aligned
return 0;
}
template <>
void StubAtom<ppc64>::copyRawContent(uint8_t buffer[]) const
{
if ( pic() ) {
OSWriteBigInt32(&buffer [0], 0, 0x7c0802a6); // mflr r0
OSWriteBigInt32(&buffer[ 4], 0, 0x429f0005); // bcl 20,31,Lpicbase
OSWriteBigInt32(&buffer[ 8], 0, 0x7d6802a6); // Lpicbase: mflr r11
OSWriteBigInt32(&buffer[12], 0, 0x3d6b0000); // addis r11,r11,ha16(L_fwrite$lazy_ptr-Lpicbase)
OSWriteBigInt32(&buffer[16], 0, 0x7c0803a6); // mtlr r0
OSWriteBigInt32(&buffer[20], 0, 0xe98b0001); // ldu r12,lo16(L_fwrite$lazy_ptr-Lpicbase)(r11)
OSWriteBigInt32(&buffer[24], 0, 0x7d8903a6); // mtctr r12
OSWriteBigInt32(&buffer[28], 0, 0x4e800420); // bctr
}
else {
OSWriteBigInt32(&buffer[ 0], 0, 0x3d600000); // lis r11,ha16(L_fwrite$lazy_ptr)
OSWriteBigInt32(&buffer[ 4], 0, 0xe98b0001); // ldu r12,lo16(L_fwrite$lazy_ptr)(r11)
OSWriteBigInt32(&buffer[ 8], 0, 0x7d8903a6); // mtctr r12
OSWriteBigInt32(&buffer[12], 0, 0x4e800420); // bctr
}
}
template <>
void StubAtom<ppc>::copyRawContent(uint8_t buffer[]) const
{
if ( pic() ) {
OSWriteBigInt32(&buffer[ 0], 0, 0x7c0802a6); // mflr r0
OSWriteBigInt32(&buffer[ 4], 0, 0x429f0005); // bcl 20,31,Lpicbase
OSWriteBigInt32(&buffer[ 8], 0, 0x7d6802a6); // Lpicbase: mflr r11
OSWriteBigInt32(&buffer[12], 0, 0x3d6b0000); // addis r11,r11,ha16(L_fwrite$lazy_ptr-Lpicbase)
OSWriteBigInt32(&buffer[16], 0, 0x7c0803a6); // mtlr r0
OSWriteBigInt32(&buffer[20], 0, 0x858b0000); // lwzu r12,lo16(L_fwrite$lazy_ptr-Lpicbase)(r11)
OSWriteBigInt32(&buffer[24], 0, 0x7d8903a6); // mtctr r12
OSWriteBigInt32(&buffer[28], 0, 0x4e800420); // bctr
}
else {
OSWriteBigInt32(&buffer[ 0], 0, 0x3d600000); // lis r11,ha16(L_fwrite$lazy_ptr)
OSWriteBigInt32(&buffer[ 4], 0, 0x858b0000); // lwzu r12,lo16(L_fwrite$lazy_ptr)(r11)
OSWriteBigInt32(&buffer[ 8], 0, 0x7d8903a6); // mtctr r12
OSWriteBigInt32(&buffer[12], 0, 0x4e800420); // bctr
}
}
template <>
void StubAtom<x86>::copyRawContent(uint8_t buffer[]) const
{
buffer[0] = 0xF4;
buffer[1] = 0xF4;
buffer[2] = 0xF4;
buffer[3] = 0xF4;
buffer[4] = 0xF4;
}
template <>
void StubAtom<x86_64>::copyRawContent(uint8_t buffer[]) const
{
buffer[0] = 0xFF; // jmp *foo$lazy_pointer(%rip)
buffer[1] = 0x25;
buffer[2] = 0x00;
buffer[3] = 0x00;
buffer[4] = 0x00;
buffer[5] = 0x00;
}
// x86_64 stubs are 7 bytes and need no alignment
template <>
uint8_t StubAtom<x86_64>::getAlignment() const
{
return 0;
}
template <>
const char* StubAtom<ppc>::getSectionName() const
{
return ( pic() ? "__picsymbolstub1" : "__symbol_stub1");
}
template <>
const char* StubAtom<ppc64>::getSectionName() const
{
return ( pic() ? "__picsymbolstub1" : "__symbol_stub1");
}
template <>
const char* StubAtom<x86>::getSectionName() const
{
return "__jump_table";
}
template <>
StubHelperAtom<x86_64>::StubHelperAtom(Writer<x86_64>& writer, ObjectFile::Atom& target, ObjectFile::Atom& lazyPointer)
: WriterAtom<x86_64>(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target)
{
writer.fAllSynthesizedStubHelpers.push_back(this);
fReferences.push_back(new WriterReference<x86_64>(3, x86_64::kPCRel32, &lazyPointer));
fReferences.push_back(new WriterReference<x86_64>(8, x86_64::kPCRel32, writer.fDyldHelper));
if ( writer.fDyldHelper == NULL )
throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)";
}
template <>
uint64_t StubHelperAtom<x86_64>::getSize() const
{
return 12;
}
template <>
void StubHelperAtom<x86_64>::copyRawContent(uint8_t buffer[]) const
{
buffer[0] = 0x4C; // lea foo$lazy_ptr(%rip),%r11
buffer[1] = 0x8D;
buffer[2] = 0x1D;
buffer[3] = 0x00;
buffer[4] = 0x00;
buffer[5] = 0x00;
buffer[6] = 0x00;
buffer[7] = 0xE9; // jmp dyld_stub_binding_helper
buffer[8] = 0x00;
buffer[9] = 0x00;
buffer[10] = 0x00;
buffer[11] = 0x00;
}
template <typename A>
const char* StubHelperAtom<A>::stubName(const char* name)
{
char* buf;
asprintf(&buf, "%s$stubHelper", name);
return buf;
}
// specialize lazy pointer for x86_64 to initially pointer to stub helper
template <>
LazyPointerAtom<x86_64>::LazyPointerAtom(Writer<x86_64>& writer, ObjectFile::Atom& target)
: WriterAtom<x86_64>(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target)
{
writer.fAllSynthesizedLazyPointers.push_back(this);
StubHelperAtom<x86_64>* helper = new StubHelperAtom<x86_64>(writer, target, *this);
fReferences.push_back(new WriterReference<x86_64>(0, x86_64::kPointer, helper));
}
template <typename A>
LazyPointerAtom<A>::LazyPointerAtom(Writer<A>& writer, ObjectFile::Atom& target)
: WriterAtom<A>(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target)
{
writer.fAllSynthesizedLazyPointers.push_back(this);
fReferences.push_back(new WriterReference<A>(0, A::kPointer, &target));
}
template <typename A>
const char* LazyPointerAtom<A>::lazyPointerName(const char* name)
{
char* buf;
asprintf(&buf, "%s$lazy_pointer", name);
return buf;
}
template <typename A>
void LazyPointerAtom<A>::copyRawContent(uint8_t buffer[]) const
{
bzero(buffer, getSize());
}
template <typename A>
NonLazyPointerAtom<A>::NonLazyPointerAtom(Writer<A>& writer, ObjectFile::Atom& target)
: WriterAtom<A>(writer, Segment::fgDataSegment), fName(nonlazyPointerName(target.getName())), fTarget(target)
{
writer.fAllSynthesizedNonLazyPointers.push_back(this);
fReferences.push_back(new WriterReference<A>(0, A::kPointer, &target));
}
template <typename A>
const char* NonLazyPointerAtom<A>::nonlazyPointerName(const char* name)
{
char* buf;
asprintf(&buf, "%s$non_lazy_pointer", name);
return buf;
}
template <typename A>
void NonLazyPointerAtom<A>::copyRawContent(uint8_t buffer[]) const
{
bzero(buffer, getSize());
}
}; // namespace executable
}; // namespace mach_o
#endif // __EXECUTABLE_MACH_O__