X-Git-Url: https://git.saurik.com/apple/ld64.git/blobdiff_plain/c7f24d34adf9643c842ab6c44f8e530661cffcc7..d696c285d331ab577dcabd00419d8c30336673da:/src/MachOWriterExecutable.hpp diff --git a/src/MachOWriterExecutable.hpp b/src/MachOWriterExecutable.hpp new file mode 100644 index 0000000..30aa24b --- /dev/null +++ b/src/MachOWriterExecutable.hpp @@ -0,0 +1,4574 @@ +/* -*- 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 +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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::setHeaderInfo() +// ThreadsLoadCommandsAtom::getSize() +// ThreadsLoadCommandsAtom::copyRawContent() +// Writer::addObjectRelocs() +// Writer::fixUpReferenceRelocatable() +// Writer::fixUpReferenceFinal() +// Writer::stubableReferenceKind() +// Writer::weakImportReferenceKind() +// Writer::GOTReferenceKind() +// + + +namespace mach_o { +namespace executable { + +// forward references +template class WriterAtom; +template class PageZeroAtom; +template class CustomStackAtom; +template class MachHeaderAtom; +template class SegmentLoadCommandsAtom; +template class SymbolTableLoadCommandsAtom; +template class ThreadsLoadCommandsAtom; +template class DylibIDLoadCommandsAtom; +template class RoutinesLoadCommandsAtom; +template class DyldLoadCommandsAtom; +template class UUIDLoadCommandAtom; +template class LinkEditAtom; +template class SectionRelocationsLinkEditAtom; +template class LocalRelocationsLinkEditAtom; +template class ExternalRelocationsLinkEditAtom; +template class SymbolTableLinkEditAtom; +template class IndirectTableLinkEditAtom; +template class StringsLinkEditAtom; +template class LoadCommandsPaddingAtom; +template class StubAtom; +template class StubHelperAtom; +template class LazyPointerAtom; +template 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 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 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 +class Writer : public ExecutableFile::Writer +{ +public: + Writer(const char* path, Options& options, std::vector& 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& getAtoms() { return fWriterSynthesizedAtoms; } + virtual std::vector* getJustInTimeAtomsFor(const char* name) { return NULL; } + virtual std::vector* getStabs() { return NULL; } + + virtual class ObjectFile::Atom* getUndefinedProxyAtom(const char* name); + virtual uint64_t write(std::vector& atoms, + std::vector& 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& atoms, uint32_t startIndex, uint32_t count); + void buildSymbolTable(); + void setExportNlist(const ObjectFile::Atom* atom, macho_nlist

* entry); + void setImportNlist(const ObjectFile::Atom* atom, macho_nlist

* entry); + void setLocalNlist(const ObjectFile::Atom* atom, macho_nlist

* 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(); + 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(uint8_t kind, bool slideable); + + + struct DirectLibrary { + class ObjectFile::Reader* fLibrary; + bool fWeak; + bool fReExport; + }; + + friend class WriterAtom; + friend class PageZeroAtom; + friend class CustomStackAtom; + friend class MachHeaderAtom; + friend class SegmentLoadCommandsAtom; + friend class SymbolTableLoadCommandsAtom; + friend class ThreadsLoadCommandsAtom; + friend class DylibIDLoadCommandsAtom; + friend class RoutinesLoadCommandsAtom; + friend class DyldLoadCommandsAtom; + friend class UUIDLoadCommandAtom; + friend class LinkEditAtom; + friend class SectionRelocationsLinkEditAtom; + friend class LocalRelocationsLinkEditAtom; + friend class ExternalRelocationsLinkEditAtom; + friend class SymbolTableLinkEditAtom; +// friend class IndirectTableLinkEditAtom; + friend class StringsLinkEditAtom; + friend class LoadCommandsPaddingAtom; + friend class StubAtom; + friend class StubHelperAtom; + friend class LazyPointerAtom; + friend class NonLazyPointerAtom; + + const char* fFilePath; + Options& fOptions; + int fFileDescriptor; + std::vector* fAllAtoms; + std::vector* fStabs; + class SectionInfo* fLoadCommandsSection; + class SegmentInfo* fLoadCommandsSegment; + class SegmentLoadCommandsAtom* fSegmentCommands; + class SymbolTableLoadCommandsAtom* fSymbolTableCommands; + class LoadCommandsPaddingAtom* fHeaderPadding; + class UUIDLoadCommandAtom* fUUIDAtom; + std::vector fWriterSynthesizedAtoms; + std::vector fSegmentInfos; + class ObjectFile::Atom* fEntryPoint; + class ObjectFile::Atom* fDyldHelper; + std::vector fDirectLibraries; + std::map fLibraryToOrdinal; + std::vector fExportedAtoms; + std::vector fImportedAtoms; + std::vector fLocalSymbolAtoms; + class SectionRelocationsLinkEditAtom* fSectionRelocationsAtom; + class LocalRelocationsLinkEditAtom* fLocalRelocationsAtom; + class ExternalRelocationsLinkEditAtom* fExternalRelocationsAtom; + class SymbolTableLinkEditAtom* fSymbolTableAtom; + class IndirectTableLinkEditAtom* fIndirectTableAtom; + class StringsLinkEditAtom* fStringsAtom; + macho_nlist

* fSymbolTable; + std::vector > fSectionRelocs; + std::vector > fInternalRelocs; + std::vector > fExternalRelocs; + std::map fStubsMap; + std::map fGOTMap; + std::vector*> fAllSynthesizedStubs; + std::vector fAllSynthesizedStubHelpers; + std::vector*> fAllSynthesizedLazyPointers; + std::vector*> 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; + std::map fWeakImportMap; +}; + + +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 +class WriterAtom : public ObjectFile::Atom +{ +public: + enum Kind { zeropage, machHeaderApp, machHeaderDylib, machHeaderBundle, machHeaderObject, loadCommands, undefinedProxy }; + WriterAtom(Writer& writer, Segment& segment) : fWriter(writer), fSegment(segment) { setDontDeadStrip(); } + + 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 isZeroFill() const { return false; } + virtual std::vector& 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* 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 fgEmptyReferenceList; + + Writer& fWriter; + Segment& fSegment; +}; + +template std::vector WriterAtom::fgEmptyReferenceList; + + +template +class PageZeroAtom : public WriterAtom +{ +public: + PageZeroAtom(Writer& writer) : WriterAtom(writer, Segment::fgPageZeroSegment) {} + virtual const char* getDisplayName() const { return "page zero content"; } + virtual bool isZeroFill() const { return true; } + virtual uint64_t getSize() const { return fWriter.fOptions.zeroPageSize(); } + virtual const char* getSectionName() const { return "._zeropage"; } + virtual uint8_t getAlignment() const { return 12; } +private: + using WriterAtom::fWriter; + typedef typename A::P P; +}; + +template +class DsoHandleAtom : public WriterAtom +{ +public: + DsoHandleAtom(Writer& writer) : WriterAtom(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 +class MachHeaderAtom : public WriterAtom +{ +public: + MachHeaderAtom(Writer& writer) : WriterAtom(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); } + 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::fWriter; + typedef typename A::P P; + void setHeaderInfo(macho_header& header) const; +}; + +template +class CustomStackAtom : public WriterAtom +{ +public: + CustomStackAtom(Writer& 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::fWriter; + typedef typename A::P P; + static bool stackGrowsDown(); +}; + +template +class LoadCommandAtom : public WriterAtom +{ +protected: + LoadCommandAtom(Writer& writer, Segment& segment) : WriterAtom(writer, segment) {} + static uint64_t alignedSize(uint64_t size); +}; + + +template +class SegmentLoadCommandsAtom : public LoadCommandAtom +{ +public: + SegmentLoadCommandsAtom(Writer& writer) + : LoadCommandAtom(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; } + void assignFileOffsets(); +private: + using WriterAtom::fWriter; + typedef typename A::P P; + unsigned int fCommandCount; + uint32_t fSize; +}; + +template +class SymbolTableLoadCommandsAtom : public LoadCommandAtom +{ +public: + SymbolTableLoadCommandsAtom(Writer&); + 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::fWriter; + typedef typename A::P P; + macho_symtab_command fSymbolTable; + macho_dysymtab_command fDynamicSymbolTable; +}; + +template +class ThreadsLoadCommandsAtom : public LoadCommandAtom +{ +public: + ThreadsLoadCommandsAtom(Writer& writer) + : LoadCommandAtom(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::fWriter; + typedef typename A::P P; + uint8_t* fBuffer; + uint32_t fBufferSize; +}; + +template +class DyldLoadCommandsAtom : public LoadCommandAtom +{ +public: + DyldLoadCommandsAtom(Writer& writer) : LoadCommandAtom(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::fWriter; + typedef typename A::P P; +}; + +template +class AllowableClientLoadCommandsAtom : public LoadCommandAtom +{ +public: + AllowableClientLoadCommandsAtom(Writer& writer, const char* client) : + LoadCommandAtom(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::fWriter; + typedef typename A::P P; + const char* clientString; +}; + +template +class DylibLoadCommandsAtom : public LoadCommandAtom +{ +public: + DylibLoadCommandsAtom(Writer& writer, ExecutableFile::DyLibUsed& info) + : LoadCommandAtom(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::fWriter; + typedef typename A::P P; + ExecutableFile::DyLibUsed& fInfo; +}; + +template +class DylibIDLoadCommandsAtom : public LoadCommandAtom +{ +public: + DylibIDLoadCommandsAtom(Writer& writer) : LoadCommandAtom(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::fWriter; + typedef typename A::P P; +}; + +template +class RoutinesLoadCommandsAtom : public LoadCommandAtom +{ +public: + RoutinesLoadCommandsAtom(Writer& writer) : LoadCommandAtom(writer, Segment::fgTextSegment) {} + virtual const char* getDisplayName() const { return "routines load command"; } + virtual uint64_t getSize() const { return sizeof(macho_routines_command); } + 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::fWriter; + typedef typename A::P P; +}; + +template +class SubUmbrellaLoadCommandsAtom : public LoadCommandAtom +{ +public: + SubUmbrellaLoadCommandsAtom(Writer& writer, const char* name) + : LoadCommandAtom(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 +class SubLibraryLoadCommandsAtom : public LoadCommandAtom +{ +public: + SubLibraryLoadCommandsAtom(Writer& writer, const char* nameStart, int nameLen) + : LoadCommandAtom(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::fWriter; + typedef typename A::P P; + const char* fNameStart; + int fNameLength; +}; + +template +class UmbrellaLoadCommandsAtom : public LoadCommandAtom +{ +public: + UmbrellaLoadCommandsAtom(Writer& writer, const char* name) + : LoadCommandAtom(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::fWriter; + typedef typename A::P P; + const char* fName; +}; + +template +class UUIDLoadCommandAtom : public LoadCommandAtom +{ +public: + UUIDLoadCommandAtom(Writer& writer) + : LoadCommandAtom(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) : 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::fWriter; + typedef typename A::P P; + uuid_t fUUID; + bool fEmit; +}; + +template +class LoadCommandsPaddingAtom : public WriterAtom +{ +public: + LoadCommandsPaddingAtom(Writer& writer) + : WriterAtom(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::fWriter; + typedef typename A::P P; + uint64_t fSize; +}; + +template +class LinkEditAtom : public WriterAtom +{ +public: + LinkEditAtom(Writer& writer) : WriterAtom(writer, Segment::fgLinkEditSegment) {} + uint64_t getFileOffset() const; +private: + typedef typename A::P P; +}; + +template +class SectionRelocationsLinkEditAtom : public LinkEditAtom +{ +public: + SectionRelocationsLinkEditAtom(Writer& writer) : LinkEditAtom(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::fWriter; + typedef typename A::P P; +}; + +template +class LocalRelocationsLinkEditAtom : public LinkEditAtom +{ +public: + LocalRelocationsLinkEditAtom(Writer& writer) : LinkEditAtom(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::fWriter; + typedef typename A::P P; +}; + +template +class SymbolTableLinkEditAtom : public LinkEditAtom +{ +public: + SymbolTableLinkEditAtom(Writer& writer) : LinkEditAtom(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::fWriter; + typedef typename A::P P; +}; + +template +class ExternalRelocationsLinkEditAtom : public LinkEditAtom +{ +public: + ExternalRelocationsLinkEditAtom(Writer& writer) : LinkEditAtom(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::fWriter; + typedef typename A::P P; +}; + +struct IndirectEntry { + uint32_t indirectIndex; + uint32_t symbolIndex; +}; + +template +class IndirectTableLinkEditAtom : public LinkEditAtom +{ +public: + IndirectTableLinkEditAtom(Writer& writer) : LinkEditAtom(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 fTable; + +private: + using WriterAtom::fWriter; + typedef typename A::P P; +}; + +class CStringEquals +{ +public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } +}; + +template +class StringsLinkEditAtom : public LinkEditAtom +{ +public: + StringsLinkEditAtom(Writer& 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::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, CStringEquals> StringToOffset; + + std::vector fFullBuffers; + char* fCurrentBuffer; + uint32_t fCurrentBufferUsed; + StringToOffset fUniqueStrings; +}; + + + +template +class UndefinedSymbolProxyAtom : public WriterAtom +{ +public: + UndefinedSymbolProxyAtom(Writer& writer, const char* name) : WriterAtom(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::fWriter; + typedef typename A::P P; + const char* fName; +}; + +template +class BranchIslandAtom : public WriterAtom +{ +public: + BranchIslandAtom(Writer& 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::fWriter; + const char* fName; + ObjectFile::Atom& fTarget; + uint32_t fTargetOffset; +}; + +template +class StubAtom : public WriterAtom +{ +public: + StubAtom(Writer& 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& getReferences() const { return (std::vector&)(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::fWriter; + const char* fName; + ObjectFile::Atom& fTarget; + std::vector fReferences; +}; + + +template +class LazyPointerAtom : public WriterAtom +{ +public: + LazyPointerAtom(Writer& 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& getReferences() const { return (std::vector&)(fReferences); } + virtual void copyRawContent(uint8_t buffer[]) const; + ObjectFile::Atom* getTarget() { return &fTarget; } +private: + using WriterAtom::fWriter; + static const char* lazyPointerName(const char* importName); + const char* fName; + ObjectFile::Atom& fTarget; + std::vector fReferences; +}; + + +template +class NonLazyPointerAtom : public WriterAtom +{ +public: + NonLazyPointerAtom(Writer& 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& getReferences() const { return (std::vector&)(fReferences); } + virtual void copyRawContent(uint8_t buffer[]) const; + ObjectFile::Atom* getTarget() { return &fTarget; } +private: + using WriterAtom::fWriter; + static const char* nonlazyPointerName(const char* importName); + const char* fName; + ObjectFile::Atom& fTarget; + std::vector fReferences; +}; + + +template +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 +Writer::Writer(const char* path, Options& options, std::vector& dynamicLibraries) + : ExecutableFile::Writer(dynamicLibraries), fFilePath(strdup(path)), fOptions(options), fLoadCommandsSection(NULL), + fLoadCommandsSegment(NULL), fLargestAtomSize(1), + fEmitVirtualSections(false), fHasWeakExports(false), fReferencesWeakImports(false), + fSeenFollowOnReferences(false) +{ + 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(new PageZeroAtom(*this)); + if ( fOptions.outputKind() == Options::kDynamicExecutable ) + fWriterSynthesizedAtoms.push_back(new DsoHandleAtom(*this)); + fWriterSynthesizedAtoms.push_back(new MachHeaderAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom(*this)); + if ( fOptions.outputKind() == Options::kDynamicExecutable ) + fWriterSynthesizedAtoms.push_back(new DyldLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom(*this)); + fWriterSynthesizedAtoms.push_back(new ThreadsLoadCommandsAtom(*this)); + if ( fOptions.hasCustomStack() ) + fWriterSynthesizedAtoms.push_back(new CustomStackAtom(*this)); + fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); + fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom(*this)); + break; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + fWriterSynthesizedAtoms.push_back(new DsoHandleAtom(*this)); + // fall through + case Options::kObjectFile: + fWriterSynthesizedAtoms.push_back(new MachHeaderAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom(*this)); + if ( fOptions.outputKind() == Options::kDynamicLibrary ) { + fWriterSynthesizedAtoms.push_back(new DylibIDLoadCommandsAtom(*this)); + if ( fOptions.initFunctionName() != NULL ) + fWriterSynthesizedAtoms.push_back(new RoutinesLoadCommandsAtom(*this)); + } + fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); + fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom(*this)); + break; + case Options::kDyld: + fWriterSynthesizedAtoms.push_back(new DsoHandleAtom(*this)); + fWriterSynthesizedAtoms.push_back(new MachHeaderAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(new DyldLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom(*this)); + fWriterSynthesizedAtoms.push_back(new ThreadsLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); + fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom(*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.indirect ) { + // find ordinal of direct reader + if ( fOptions.nameSpace() == Options::kTwoLevelNameSpace ) { + bool found = false; + for (std::map::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 ) { + 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(*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(*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(*this, nameStart, len)); + } + } + } + } + } + // add umbrella command if needed + if ( fOptions.umbrellaName() != NULL ) { + fWriterSynthesizedAtoms.push_back(new UmbrellaLoadCommandsAtom(*this, fOptions.umbrellaName())); + } + std::vector& allowableClients = fOptions.allowableClients(); + if ( allowableClients.size() != 0 ) { + for (std::vector::iterator it=allowableClients.begin(); + it != allowableClients.end(); + it++) + fWriterSynthesizedAtoms.push_back(new AllowableClientLoadCommandsAtom(*this, *it)); + } + } + break; + case Options::kStaticExecutable: + case Options::kObjectFile: + case Options::kDyld: + break; + } + + //fprintf(stderr, "ordinals table:\n"); + //for (std::map::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) { + // fprintf(stderr, "%d <== %s\n", it->second, it->first->getPath()); + //} +} + +template +Writer::~Writer() +{ + if ( fFilePath != NULL ) + free((void*)fFilePath); + if ( fSymbolTable != NULL ) + delete [] fSymbolTable; +} + + +template +ObjectFile::Atom* Writer::getUndefinedProxyAtom(const char* name) +{ + if ( (fOptions.outputKind() == Options::kObjectFile) + || (fOptions.undefinedTreatment() != Options::kUndefinedError) ) + return new UndefinedSymbolProxyAtom(*this, name); + else + return NULL; +} + +template +uint8_t Writer::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::iterator pos = fLibraryToOrdinal.find(lib); + if ( pos != fLibraryToOrdinal.end() ) + return pos->second; + + throw "can't find ordinal for imported symbol"; +} + + +template +uint64_t Writer::write(std::vector& atoms, + std::vector& stabs, + class ObjectFile::Atom* entryPointAtom, class ObjectFile::Atom* dyldHelperAtom, + bool createUUID) +{ + fAllAtoms = &atoms; + fStabs = &stabs; + fEntryPoint = entryPointAtom; + fDyldHelper = dyldHelperAtom; + + // Set for create UUID + if (createUUID) + fUUIDAtom->emit(); + + // 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(); +} + +template +void Writer::buildLinkEdit() +{ + this->collectExportedAndImportedAndLocalAtoms(); + this->buildSymbolTable(); + this->buildFixups(); + this->adjustLinkEditSections(); +} + + + +template +uint64_t Writer::getAtomLoadAddress(const ObjectFile::Atom* atom) +{ + return atom->getAddress(); +// SectionInfo* info = (SectionInfo*)atom->getSection(); +// return info->getBaseAddress() + atom->getSectionOffset(); +} + +template +void Writer::setExportNlist(const ObjectFile::Atom* atom, macho_nlist

* entry) +{ + // set n_type + 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 +void Writer::setImportNlist(const ObjectFile::Atom* atom, macho_nlist

* 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 +void Writer::setLocalNlist(const ObjectFile::Atom* atom, macho_nlist

* 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 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 +void Writer::setNlistRange(std::vector& atoms, uint32_t startIndex, uint32_t count) +{ + macho_nlist

* 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 +void Writer::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

[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 +bool Writer::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 +void Writer::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 ) { + 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 +uint64_t Writer::valueForStab(const ObjectFile::Reader::Stab& stab) +{ + switch ( stab.type ) { + case N_FUN: + if ( stab.other == 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: + return 0; + default: + return stab.value; + } +} + +template +uint32_t Writer::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 +uint8_t Writer::sectionIndexForStab(const ObjectFile::Reader::Stab& stab) +{ + if ( stab.atom != NULL ) + return stab.atom->getSection()->getIndex(); + else + return stab.other; +} + +template +void Writer::addStabs(uint32_t startIndex) +{ + macho_nlist

* entry = &fSymbolTable[startIndex]; + for(std::vector::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 +uint32_t Writer::symbolIndex(ObjectFile::Atom& atom) +{ + // search imports + int i = 0; + for(std::vector::iterator it=fImportedAtoms.begin(); it != fImportedAtoms.end(); ++it) { + if ( &atom == *it ) + return i + fSymbolTableImportStartIndex; + ++i; + } + + // search locals + i = 0; + for(std::vector::iterator it=fLocalSymbolAtoms.begin(); it != fLocalSymbolAtoms.end(); ++it) { + if ( &atom == *it ) + return i + fSymbolTableLocalStartIndex; + ++i; + } + + // search exports + i = 0; + for(std::vector::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 +void Writer::buildFixups() +{ + if ( fOptions.outputKind() == Options::kObjectFile ) { + this->buildObjectFileFixups(); + } + else { + if ( fOptions.keepRelocations() ) + this->buildObjectFileFixups(); + this->buildExecutableFixups(); + } +} + + +template <> +uint32_t Writer::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

reloc1; + macho_relocation_info

reloc2; + macho_scattered_relocation_info

* sreloc1 = (macho_scattered_relocation_info

*)&reloc1; + macho_scattered_relocation_info

* sreloc2 = (macho_scattered_relocation_info

*)&reloc2; + x86::ReferenceKinds kind = (x86::ReferenceKinds)ref->getKind(); + + switch ( kind ) { + case x86::kNoFixUp: + case x86::kFollowOn: + return 0; + + case x86::kPointer: + case x86::kPointerWeakImport: + 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::getRelocPointerSize() +{ + return 2; +} + +template <> +uint8_t Writer::getRelocPointerSize() +{ + return 3; +} + +template <> +uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) +{ + return addObjectRelocs_powerpc(atom, ref); +} + +template <> +uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) +{ + return addObjectRelocs_powerpc(atom, ref); +} + +// +// addObjectRelocs and addObjectRelocs are almost exactly the same, so +// they use a common addObjectRelocs_powerpc() method. +// +template +uint32_t Writer::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

reloc1; + macho_relocation_info

reloc2; + macho_scattered_relocation_info

* sreloc1 = (macho_scattered_relocation_info

*)&reloc1; + macho_scattered_relocation_info

* sreloc2 = (macho_scattered_relocation_info

*)&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 +void Writer::buildObjectFileFixups() +{ + uint32_t relocIndex = 0; + std::vector& segmentInfos = fSegmentInfos; + const int segCount = segmentInfos.size(); + for(int i=0; i < segCount; ++i) { + SegmentInfo* curSegment = segmentInfos[i]; + std::vector& 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& 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& 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 { + if ( curSection->fAllNonLazyPointers + && (ref->getTarget().getScope() == ObjectFile::Atom::scopeLinkageUnit) + && !fOptions.keepPrivateExterns() ) + 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

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 { + 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& 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::illegalRelocInFinalLinkedImage(uint8_t kind, bool slideable) +{ + switch ( kind ) { + case ppc::kAbsLow16: + case ppc::kAbsLow14: + case ppc::kAbsHigh16: + case ppc::kAbsHigh16AddLow: + if ( slideable ) + return true; + } + return false; +} + + +template <> +bool Writer::illegalRelocInFinalLinkedImage(uint8_t kind, bool slideable) +{ + switch ( kind ) { + case ppc::kAbsLow16: + case ppc::kAbsLow14: + case ppc::kAbsHigh16: + case ppc::kAbsHigh16AddLow: + if ( slideable ) + return true; + } + return false; +} + +template <> +bool Writer::illegalRelocInFinalLinkedImage(uint8_t kind, bool slideable) +{ + return false; +} + + + +template +typename Writer::RelocKind Writer::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 +void Writer::buildExecutableFixups() +{ + const bool slideable = (fOptions.outputKind() != Options::kDynamicExecutable) && (fOptions.outputKind() != Options::kStaticExecutable); + fIndirectTableAtom->fTable.reserve(50); // minimize reallocations + std::vector& segmentInfos = fSegmentInfos; + const int segCount = segmentInfos.size(); + for(int i=0; i < segCount; ++i) { + SegmentInfo* curSegment = segmentInfos[i]; + std::vector& 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& 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& 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*)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

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(atom->getAddress()-fOptions.baseAddress()); + 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

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(atom->getAddress()+ref->getFixUpOffset()-fOptions.baseAddress()); + 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

externalReloc; + externalReloc.set_r_address(atom->getAddress()+ref->getFixUpOffset()-fOptions.baseAddress()); + 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->getKind(), 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*)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::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::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::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 +uint64_t Writer::writeAtoms() +{ + uint32_t end = 0; + uint8_t* buffer = new uint8_t[(fLargestAtomSize+4095) & (-4096)]; + std::vector& 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& sectionInfos = curSegment->fSections; + const int sectionCount = sectionInfos.size(); + for(int j=0; j < sectionCount; ++j) { + SectionInfo* curSection = sectionInfos[j]; + std::vector& 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& references = atom->getReferences(); + for (std::vector::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\n", offset, end, atom->getAddress(), atom->getSize(), atom->getDisplayName()); + // write out + ::pwrite(fFileDescriptor, buffer, atom->getSize(), offset); + } + } + } + } + } + delete [] buffer; + return end; +} + + +template <> +void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; + switch ( (x86::ReferenceKinds)(ref->getKind()) ) { + case x86::kNoFixUp: + case x86::kFollowOn: + // do nothing + break; + case x86::kPointerWeakImport: + case x86::kPointer: + { + if ( ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kRegularDefinition ) { + // external realocation ==> pointer contains addend + LittleEndian::set32(*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::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: + int64_t 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; + } + 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::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 (ref->getKind()) { + case x86::kNoFixUp: + case x86::kFollowOn: + // do nothing + break; + case x86::kPointer: + { + 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: + 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::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + fixUpReference_powerpc(ref, inAtom, buffer, true); +} + +template <> +void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + fixUpReference_powerpc(ref, inAtom, buffer, true); +} + +template <> +void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + fixUpReference_powerpc(ref, inAtom, buffer, false); +} + +template <> +void Writer::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 +void Writer::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 ( 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::stubableReferenceKind(uint8_t kind) +{ + return (kind == ppc::kBranch24 || kind == ppc::kBranch24WeakImport); +} + +template <> +bool Writer::stubableReferenceKind(uint8_t kind) +{ + return (kind == ppc64::kBranch24 || kind == ppc64::kBranch24WeakImport); +} + +template <> +bool Writer::stubableReferenceKind(uint8_t kind) +{ + return (kind == x86::kPCRel32 || kind == x86::kPCRel32WeakImport); +} + + +template <> +bool Writer::weakImportReferenceKind(uint8_t kind) +{ + return (kind == ppc::kBranch24WeakImport || kind == ppc::kPointerWeakImport); +} + +template <> +bool Writer::weakImportReferenceKind(uint8_t kind) +{ + return (kind == ppc64::kBranch24WeakImport || kind == ppc64::kPointerWeakImport); +} + +template <> +bool Writer::weakImportReferenceKind(uint8_t kind) +{ + return (kind == x86::kPCRel32WeakImport || kind == x86::kPointerWeakImport); +} + + + +template <> +bool Writer::GOTReferenceKind(uint8_t kind) +{ + return false; +} + +template <> +bool Writer::GOTReferenceKind(uint8_t kind) +{ + return false; +} + +template <> +bool Writer::GOTReferenceKind(uint8_t kind) +{ + return false; +} + + + +template +void Writer::synthesizeStubs() +{ + switch ( fOptions.outputKind() ) { + case Options::kStaticExecutable: + case Options::kDyld: + case Options::kObjectFile: + // these output kinds never have stubs + return; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kDynamicExecutable: + // try to synthesize stubs for these + break; + } + + // walk every atom and reference + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + std::vector& references = atom->getReferences(); + for (std::vector::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::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::iterator pos = fStubsMap.find(&target); + if ( pos == fStubsMap.end() ) { + stub = new StubAtom(*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::iterator pos = fGOTMap.find(&target); + if ( pos == fGOTMap.end() ) { + nlp = new NonLazyPointerAtom(*this, target); + fGOTMap[&target] = nlp; + } + else { + nlp = pos->second; + } + // alter reference to use non lazy pointer instead + ref->setTarget(*nlp, 0); + } + } + } + + // sort stubs + + // sort lazy pointers + + // add stubs to fAllAtoms + if ( fAllSynthesizedStubs.size() != 0 ) { + std::vector* stubs = (std::vector*)&fAllSynthesizedStubs; + std::vector 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::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::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::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, 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 +void Writer::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)) ); + 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& alignmentOverrides = fOptions.sectionAlignments(); + for(std::vector::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::addBranchIslands() +{ + return this->addPPCBranchIslands(); +} + +template <> +bool Writer::addBranchIslands() +{ + return this->addPPCBranchIslands(); +} + +template <> +bool Writer::addBranchIslands() +{ + // x86 branches can reach entire 4G address space, so no need for branch islands + return false; +} + + +template <> +inline uint8_t Writer::branch24Reference() +{ + return ppc::kBranch24; +} + +template <> +inline uint8_t Writer::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 +bool Writer::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::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 AtomToIsland; + AtomToIsland regionsMap[kIslandRegionsCount]; + std::vector regionsIslands[kIslandRegionsCount]; + unsigned int islandCount = 0; + + // create islands for branch references that are out of range + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + std::vector& references = atom->getReferences(); + for (std::vector::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* island = new BranchIslandAtom(*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 newAtomList; + newAtomList.reserve(textSection->fAtoms.size()+islandCount); + uint64_t islandRegionAddr = kBetweenRegions; + int regionIndex = 0; + uint64_t sectionOffset = 0; + for (std::vector::iterator it=textSection->fAtoms.begin(); it != textSection->fAtoms.end(); it++) { + ObjectFile::Atom* atom = *it; + newAtomList.push_back(atom); + if ( atom->getAddress() > islandRegionAddr ) { + std::vector* regionIslands = ®ionsIslands[regionIndex]; + for (std::vector::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* regionIslands = ®ionsIslands[regionIndex]; + for (std::vector::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 +void Writer::adjustLoadCommandsAndPadding() +{ + fSegmentCommands->computeSize(); + + // recompute load command section offsets + uint64_t offset = 0; + std::vector& 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& 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 +void Writer::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::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& sectionInfos = curSegment->fSections; + + for (std::vector::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 ) { + fileOffset = (address - prevSection->getBaseAddress()) + prevSection->fFileOffset; + } + + // 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 + 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 { + 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); + } + } + } + } + } +} + +template +void Writer::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(); + for (unsigned int i=firstLinkEditSectionIndex; i < sectionCount; ++i) { + std::vector& 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; + } + 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 +ObjectFile::Atom::Scope MachHeaderAtom::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 +ObjectFile::Atom::SymbolTableInclusion MachHeaderAtom::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 +const char* MachHeaderAtom::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 +const char* MachHeaderAtom::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 +void MachHeaderAtom::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& 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* mh = (macho_header*)buffer; + setHeaderInfo(*mh); + mh->set_filetype(fileType); + mh->set_ncmds(commandsCount); + mh->set_sizeofcmds(commandsSize); + mh->set_flags(flags); +} + +template <> +void MachHeaderAtom::setHeaderInfo(macho_header& header) const +{ + header.set_magic(MH_MAGIC); + header.set_cputype(CPU_TYPE_POWERPC); + header.set_cpusubtype(CPU_SUBTYPE_POWERPC_ALL); +} + +template <> +void MachHeaderAtom::setHeaderInfo(macho_header& 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::setHeaderInfo(macho_header& header) const +{ + header.set_magic(MH_MAGIC); + header.set_cputype(CPU_TYPE_I386); + header.set_cpusubtype(CPU_SUBTYPE_I386_ALL); +} + + +template +CustomStackAtom::CustomStackAtom(Writer& writer) + : WriterAtom(writer, Segment::fgStackSegment) +{ + if ( stackGrowsDown() ) + Segment::fgStackSegment.setBaseAddress(writer.fOptions.customStackAddr() - writer.fOptions.customStackSize()); + else + Segment::fgStackSegment.setBaseAddress(writer.fOptions.customStackAddr()); +} + + +template <> +bool CustomStackAtom::stackGrowsDown() +{ + return true; +} + +template <> +bool CustomStackAtom::stackGrowsDown() +{ + return true; +} + +template <> +bool CustomStackAtom::stackGrowsDown() +{ + return true; +} + + +template +void SegmentLoadCommandsAtom::computeSize() +{ + uint64_t size = 0; + std::vector& segmentInfos = fWriter.fSegmentInfos; + const int segCount = segmentInfos.size(); + for(int i=0; i < segCount; ++i) { + size += sizeof(macho_segment_command

); + std::vector& 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

); + } + } + fSize = size; + fCommandCount = segCount; +} + +template <> +uint64_t LoadCommandAtom::alignedSize(uint64_t size) +{ + return ((size+3) & (-4)); // 4-byte align all load commands for 32-bit mach-o +} + +template <> +uint64_t LoadCommandAtom::alignedSize(uint64_t size) +{ + return ((size+7) & (-8)); // 8-byte align all load commands for 64-bit mach-o +} + +template <> +uint64_t LoadCommandAtom::alignedSize(uint64_t size) +{ + return ((size+3) & (-4)); // 4-byte align all load commands for 32-bit mach-o +} + + + +template +void SegmentLoadCommandsAtom::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& 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

* cmd = (macho_segment_command

*)p; + cmd->set_cmd(macho_segment_command

::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

* const sections = (macho_section

*)&p[sizeof(macho_segment_command

)]; + unsigned int sectionsEmitted = 0; + for (int j=0; j < sectionCount; ++j) { + SectionInfo* sectInfo = segInfo->fSections[j]; + if ( fWriter.fEmitVirtualSections || !sectInfo->fVirtualSection ) { + macho_section

* 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

) + 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); + 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, "__message_refs") == 0) && (strcmp(sectInfo->fSegmentName, "__OBJC") == 0) ) { + sect->set_flags(S_LITERAL_POINTERS); + } + } + } + p = &p[sizeof(macho_segment_command

) + sectionsEmitted*sizeof(macho_section

)]; + cmd->set_cmdsize(sizeof(macho_segment_command

) + sectionsEmitted*sizeof(macho_section

)); + cmd->set_nsects(sectionsEmitted); + } +} + + +template +SymbolTableLoadCommandsAtom::SymbolTableLoadCommandsAtom(Writer& writer) + : LoadCommandAtom(writer, Segment::fgTextSegment) +{ + bzero(&fSymbolTable, sizeof(macho_symtab_command

)); + bzero(&fDynamicSymbolTable, sizeof(macho_dysymtab_command

)); + writer.fSymbolTableCommands = this; +} + +template +uint64_t SymbolTableLoadCommandsAtom::getSize() const +{ + if ( fWriter.fOptions.outputKind() == Options::kStaticExecutable ) + return this->alignedSize(sizeof(macho_symtab_command

)); + else + return this->alignedSize(sizeof(macho_symtab_command

) + sizeof(macho_dysymtab_command

)); +} + +template +void SymbolTableLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + // build LC_DYSYMTAB command + macho_symtab_command

* symbolTableCmd = (macho_symtab_command

*)buffer; + bzero(symbolTableCmd, sizeof(macho_symtab_command

)); + symbolTableCmd->set_cmd(LC_SYMTAB); + symbolTableCmd->set_cmdsize(sizeof(macho_symtab_command

)); + 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

* dynamicSymbolTableCmd = (macho_dysymtab_command

*)&buffer[sizeof(macho_symtab_command

)]; + bzero(dynamicSymbolTableCmd, sizeof(macho_dysymtab_command

)); + dynamicSymbolTableCmd->set_cmd(LC_DYSYMTAB); + dynamicSymbolTableCmd->set_cmdsize(sizeof(macho_dysymtab_command

)); + 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 +unsigned int SymbolTableLoadCommandsAtom::commandCount() +{ + return (fWriter.fOptions.outputKind() == Options::kStaticExecutable) ? 1 : 2; +} + +template +uint64_t DyldLoadCommandsAtom::getSize() const +{ + return this->alignedSize(sizeof(macho_dylinker_command

) + strlen("/usr/lib/dyld") + 1); +} + +template +void DyldLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + bzero(buffer, size); + macho_dylinker_command

* cmd = (macho_dylinker_command

*)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

)], "/usr/lib/dyld"); +} + +template +uint64_t AllowableClientLoadCommandsAtom::getSize() const +{ + return this->alignedSize(sizeof(macho_sub_client_command

) + strlen(this->clientString) + 1); +} + +template +void AllowableClientLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + + bzero(buffer, size); + macho_sub_client_command

* cmd = (macho_sub_client_command

*)buffer; + cmd->set_cmd(LC_SUB_CLIENT); + cmd->set_cmdsize(size); + cmd->set_client_offset(); + strcpy((char*)&buffer[sizeof(macho_sub_client_command

)], this->clientString); + +} + +template +uint64_t DylibLoadCommandsAtom::getSize() const +{ + const char* path = fInfo.reader->getInstallPath(); + if ( fInfo.options.fInstallPathOverride != NULL ) + path = fInfo.options.fInstallPathOverride; + return this->alignedSize(sizeof(macho_dylib_command

) + strlen(path) + 1); +} + +template +void DylibLoadCommandsAtom::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

* cmd = (macho_dylib_command

*)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

)], path); +} + + + +template +uint64_t DylibIDLoadCommandsAtom::getSize() const +{ + return this->alignedSize(sizeof(macho_dylib_command

) + strlen(fWriter.fOptions.installPath()) + 1); +} + +template +void DylibIDLoadCommandsAtom::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

* cmd = (macho_dylib_command

*)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

)], fWriter.fOptions.installPath()); +} + + +template +void RoutinesLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t initAddr = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); + bzero(buffer, sizeof(macho_routines_command

)); + macho_routines_command

* cmd = (macho_routines_command

*)buffer; + cmd->set_cmd(macho_routines_command

::CMD); + cmd->set_cmdsize(this->getSize()); + cmd->set_init_address(initAddr); +} + + +template +uint64_t SubUmbrellaLoadCommandsAtom::getSize() const +{ + return this->alignedSize(sizeof(macho_sub_umbrella_command

) + strlen(fName) + 1); +} + +template +void SubUmbrellaLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + bzero(buffer, size); + macho_sub_umbrella_command

* cmd = (macho_sub_umbrella_command

*)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

)], fName); +} + +template +void UUIDLoadCommandAtom::copyRawContent(uint8_t buffer[]) const +{ + if (fEmit) { + uint64_t size = this->getSize(); + bzero(buffer, size); + macho_uuid_command

* cmd = (macho_uuid_command

*)buffer; + cmd->set_cmd(LC_UUID); + cmd->set_cmdsize(this->getSize()); + cmd->set_uuid((uint8_t*)fUUID); + } +} + +template +uint64_t SubLibraryLoadCommandsAtom::getSize() const +{ + return this->alignedSize(sizeof(macho_sub_library_command

) + fNameLength + 1); +} + +template +void SubLibraryLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + bzero(buffer, size); + macho_sub_library_command

* cmd = (macho_sub_library_command

*)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

)], fNameStart, fNameLength); + buffer[sizeof(macho_sub_library_command

)+fNameLength] = '\0'; +} + +template +uint64_t UmbrellaLoadCommandsAtom::getSize() const +{ + return this->alignedSize(sizeof(macho_sub_framework_command

) + strlen(fName) + 1); +} + +template +void UmbrellaLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + bzero(buffer, size); + macho_sub_framework_command

* cmd = (macho_sub_framework_command

*)buffer; + cmd->set_cmd(LC_SUB_FRAMEWORK); + cmd->set_cmdsize(this->getSize()); + cmd->set_umbrella_offset(); + strcpy((char*)&buffer[sizeof(macho_sub_framework_command

)], fName); +} + +template <> +uint64_t ThreadsLoadCommandsAtom::getSize() const +{ + return this->alignedSize(16 + 40*4); // base size + PPC_THREAD_STATE_COUNT * 4 +} + +template <> +uint64_t ThreadsLoadCommandsAtom::getSize() const +{ + return this->alignedSize(16 + 76*4); // base size + PPC_THREAD_STATE64_COUNT * 4 +} + +template <> +uint64_t ThreadsLoadCommandsAtom::getSize() const +{ + return this->alignedSize(16 + 16*4); // base size + i386_THREAD_STATE_COUNT * 4 +} + + +template <> +void ThreadsLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); + bzero(buffer, size); + macho_thread_command* cmd = (macho_thread_command*)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::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); + bzero(buffer, size); + macho_thread_command* cmd = (macho_thread_command*)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(6, fWriter.fOptions.customStackAddr()); // r1 +} + +template <> +void ThreadsLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); + bzero(buffer, size); + macho_thread_command* cmd = (macho_thread_command*)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 LoadCommandsPaddingAtom::copyRawContent(uint8_t buffer[]) const +{ + bzero(buffer, fSize); +} + +template +void LoadCommandsPaddingAtom::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 +uint64_t LinkEditAtom::getFileOffset() const +{ + return ((SectionInfo*)this->getSection())->fFileOffset + this->getSectionOffset(); +} + + +template +uint64_t SectionRelocationsLinkEditAtom::getSize() const +{ + return fWriter.fSectionRelocs.size() * sizeof(macho_relocation_info

); +} + +template +void SectionRelocationsLinkEditAtom::copyRawContent(uint8_t buffer[]) const +{ + memcpy(buffer, &fWriter.fSectionRelocs[0], this->getSize()); +} + + +template +uint64_t LocalRelocationsLinkEditAtom::getSize() const +{ + return fWriter.fInternalRelocs.size() * sizeof(macho_relocation_info

); +} + +template +void LocalRelocationsLinkEditAtom::copyRawContent(uint8_t buffer[]) const +{ + memcpy(buffer, &fWriter.fInternalRelocs[0], this->getSize()); +} + + + +template +uint64_t SymbolTableLinkEditAtom::getSize() const +{ + return fWriter.fSymbolTableCount * sizeof(macho_nlist

); +} + +template +void SymbolTableLinkEditAtom::copyRawContent(uint8_t buffer[]) const +{ + memcpy(buffer, fWriter.fSymbolTable, this->getSize()); +} + +template +uint64_t ExternalRelocationsLinkEditAtom::getSize() const +{ + return fWriter.fExternalRelocs.size() * sizeof(macho_relocation_info

); +} + +template +void ExternalRelocationsLinkEditAtom::copyRawContent(uint8_t buffer[]) const +{ + memcpy(buffer, &fWriter.fExternalRelocs[0], this->getSize()); +} + + + +template +uint64_t IndirectTableLinkEditAtom::getSize() const +{ + return fTable.size() * sizeof(uint32_t); +} + +template +void IndirectTableLinkEditAtom::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::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 +StringsLinkEditAtom::StringsLinkEditAtom(Writer& writer) + : LinkEditAtom(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 +uint64_t StringsLinkEditAtom::getSize() const +{ + return kBufferSize * fFullBuffers.size() + fCurrentBufferUsed; +} + +template +void StringsLinkEditAtom::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 +int32_t StringsLinkEditAtom::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 +int32_t StringsLinkEditAtom::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 +BranchIslandAtom::BranchIslandAtom(Writer& writer, const char* name, int islandRegion, ObjectFile::Atom& target, uint32_t targetOffset) + : WriterAtom(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::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::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::getSize() const +{ + return 4; +} + +template <> +uint64_t BranchIslandAtom::getSize() const +{ + return 4; +} + + +template <> +bool StubAtom::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.fOptions.zeroPageSize() > 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::pic() const +{ + return ( fWriter.fOptions.outputKind() != Options::kDynamicExecutable ); +} + + +template <> +StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target) + : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target) +{ + writer.fAllSynthesizedStubs.push_back(this); + + LazyPointerAtom* lp = new LazyPointerAtom(writer, target); + if ( pic() ) { + // picbase is 8 bytes into atom + fReferences.push_back(new WriterReference(12, ppc::kPICBaseHigh16, lp, 0, NULL, 8)); + fReferences.push_back(new WriterReference(20, ppc::kPICBaseLow16, lp, 0, NULL, 8)); + } + else { + fReferences.push_back(new WriterReference(0, ppc::kAbsHigh16AddLow, lp)); + fReferences.push_back(new WriterReference(4, ppc::kAbsLow16, lp)); + } +} + +template <> +StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target) + : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target) +{ + writer.fAllSynthesizedStubs.push_back(this); + + LazyPointerAtom* lp = new LazyPointerAtom(writer, target); + if ( pic() ) { + // picbase is 8 bytes into atom + fReferences.push_back(new WriterReference(12, ppc64::kPICBaseHigh16, lp, 0, NULL, 8)); + fReferences.push_back(new WriterReference(20, ppc64::kPICBaseLow14, lp, 0, NULL, 8)); + } + else { + fReferences.push_back(new WriterReference(0, ppc64::kAbsHigh16AddLow, lp)); + fReferences.push_back(new WriterReference(4, ppc64::kAbsLow14, lp)); + } +} + +// specialize to put x86 fast stub in __IMPORT segment with no lazy pointer +template <> +StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target) + : WriterAtom(writer, Segment::fgImportSegment), fName(stubName(target.getName())), fTarget(target) +{ + writer.fAllSynthesizedStubs.push_back(this); +} + + +template +const char* StubAtom::stubName(const char* name) +{ + char* buf; + asprintf(&buf, "%s$stub", name); + return buf; +} + +template <> +uint64_t StubAtom::getSize() const +{ + return ( pic() ? 32 : 16 ); +} + +template <> +uint64_t StubAtom::getSize() const +{ + return ( pic() ? 32 : 16 ); +} + +template <> +uint64_t StubAtom::getSize() const +{ + return 5; +} + + +template <> +uint8_t StubAtom::getAlignment() const +{ + // special case x86 fast stubs to be byte aligned + return 0; +} + +template <> +void StubAtom::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::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::copyRawContent(uint8_t buffer[]) const +{ + buffer[0] = 0xF4; + buffer[1] = 0xF4; + buffer[2] = 0xF4; + buffer[3] = 0xF4; + buffer[4] = 0xF4; +} + + +template <> +const char* StubAtom::getSectionName() const +{ + return ( pic() ? "__picsymbolstub1" : "__symbol_stub1"); +} + +template <> +const char* StubAtom::getSectionName() const +{ + return ( pic() ? "__picsymbolstub1" : "__symbol_stub1"); +} + +template <> +const char* StubAtom::getSectionName() const +{ + return "__jump_table"; +} + + + + + +template +LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Atom& target) + : WriterAtom(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target) +{ + writer.fAllSynthesizedLazyPointers.push_back(this); + + fReferences.push_back(new WriterReference(0, A::kPointer, &target)); +} + + + +template +const char* LazyPointerAtom::lazyPointerName(const char* name) +{ + char* buf; + asprintf(&buf, "%s$lazy_pointer", name); + return buf; +} + +template +void LazyPointerAtom::copyRawContent(uint8_t buffer[]) const +{ + bzero(buffer, getSize()); +} + + +template +NonLazyPointerAtom::NonLazyPointerAtom(Writer& writer, ObjectFile::Atom& target) + : WriterAtom(writer, Segment::fgDataSegment), fName(nonlazyPointerName(target.getName())), fTarget(target) +{ + writer.fAllSynthesizedNonLazyPointers.push_back(this); + + fReferences.push_back(new WriterReference(0, A::kPointer, &target)); +} + +template +const char* NonLazyPointerAtom::nonlazyPointerName(const char* name) +{ + char* buf; + asprintf(&buf, "%s$non_lazy_pointer", name); + return buf; +} + +template +void NonLazyPointerAtom::copyRawContent(uint8_t buffer[]) const +{ + bzero(buffer, getSize()); +} + + + +}; // namespace executable +}; // namespace mach_o + + +#endif // __EXECUTABLE_MACH_O__