+/*
+ * Copyright (c) 2005 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@
+ */
+
+
+
+namespace ExecutableFileMachO {
+
+class Writer : public ExecutableFile::Writer
+{
+public:
+ Writer(const char* path, Options& options, std::vector<ExecutableFile::DyLibUsed>& dynamicLibraries);
+ virtual ~Writer();
+
+ virtual const char* getPath();
+ virtual std::vector<class ObjectFile::Atom*>& getAtoms();
+ virtual std::vector<class ObjectFile::Atom*>* getJustInTimeAtomsFor(const char* name);
+ virtual std::vector<ObjectFile::StabsInfo>* getStabsDebugInfo();
+
+ virtual class ObjectFile::Atom* getUndefinedProxyAtom(const char* name);
+ virtual void write(std::vector<class ObjectFile::Atom*>& atoms, class ObjectFile::Atom* entryPointAtom);
+
+private:
+ void assignFileOffsets();
+ void partitionIntoSections();
+ void adjustLoadCommandsAndPadding();
+ void createDynamicLinkerCommand();
+ void createDylibCommands();
+ void buildLinkEdit();
+ void writeAtoms();
+ void collectExportedAndImportedAndLocalAtoms();
+ void setNlistRange(std::vector<class ObjectFile::Atom*>& atoms, uint32_t startIndex, uint32_t count);
+ void buildSymbolTable();
+ void setExportNlist(const ObjectFile::Atom* atom, macho_nlist* 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(ObjectFile::Atom& atom);
+ void buildFixups();
+ void adjustLinkEditSections();
+ void buildObjectFileFixups();
+ void buildExecutableFixups();
+ uint32_t symbolIndex(ObjectFile::Atom& atom);
+ uint32_t addRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref);
+ unsigned int collectStabs();
+ macho_uintptr_t valueForStab(const ObjectFile::StabsInfo& stab, const ObjectFile::Atom* atom);
+ void addStabs(uint32_t startIndex, uint32_t count);
+
+
+ class SectionInfo : public ObjectFile::Section {
+ public:
+ SectionInfo();
+ void setIndex(unsigned int index) { fIndex=index; }
+ std::vector<ObjectFile::Atom*> fAtoms;
+ char fSegmentName[20];
+ char fSectionName[20];
+ uint64_t fFileOffset;
+ uint64_t fSize;
+ uint32_t fRelocCount;
+ uint32_t fRelocOffset;
+ uint32_t fIndirectSymbolOffset;
+ uint8_t fAlignment;
+ bool fAllLazyPointers;
+ bool fAllNonLazyPointers;
+ bool fAllZeroFill;
+ bool fVirtualSection;
+ };
+
+ class SegmentInfo
+ {
+ public:
+ SegmentInfo();
+ std::vector<class SectionInfo*> fSections;
+ char fName[20];
+ uint32_t fInitProtection;
+ uint32_t fMaxProtection;
+ uint64_t fFileOffset;
+ uint64_t fFileSize;
+ uint64_t fBaseAddress;
+ uint64_t fSize;
+ };
+
+
+ struct DirectLibrary {
+ class ObjectFile::Reader* fLibrary;
+ bool fWeak;
+ bool fReExport;
+ };
+
+ struct IndirectEntry {
+ uint32_t indirectIndex;
+ uint32_t symbolIndex;
+ };
+
+ struct StabChunks {
+ ObjectFile::Atom* fAtom;
+ ObjectFile::Reader* fReader;
+ unsigned int fOrderInReader;
+ std::vector<ObjectFile::StabsInfo>* fStabs;
+ };
+
+ static bool stabChunkCompare(const StabChunks& lhs, const StabChunks& rhs);
+
+ 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 LinkEditAtom;
+ friend class LocalRelocationsLinkEditAtom;
+ friend class ExternalRelocationsLinkEditAtom;
+ friend class SymbolTableLinkEditAtom;
+ friend class IndirectTableLinkEditAtom;
+ friend class StringsLinkEditAtom;
+
+ const char* fFilePath;
+ Options& fOptions;
+ int fFileDescriptor;
+ std::vector<class ObjectFile::Atom*>* fAllAtoms;
+ class SectionInfo* fLoadCommandsSection;
+ class SegmentInfo* fLoadCommandsSegment;
+ class SegmentLoadCommandsAtom* fSegmentCommands;
+ class SymbolTableLoadCommandsAtom* fSymbolTableCommands;
+ class LoadCommandsPaddingAtom* fHeaderPadding;
+ std::vector<class ObjectFile::Atom*> fWriterSynthesizedAtoms;
+ std::vector<SegmentInfo*> fSegmentInfos;
+ class ObjectFile::Atom* fEntryPoint;
+ std::vector<DirectLibrary> fDirectLibraries;
+ std::map<class ObjectFile::Reader*, uint32_t> fLibraryToOrdinal;
+ std::vector<StabChunks> fStabChunks;
+ std::vector<class ObjectFile::Atom*> fExportedAtoms;
+ std::vector<class ObjectFile::Atom*> fImportedAtoms;
+ std::vector<class ObjectFile::Atom*> fLocalSymbolAtoms;
+ LocalRelocationsLinkEditAtom* fLocalRelocationsAtom;
+ ExternalRelocationsLinkEditAtom* fExternalRelocationsAtom;
+ SymbolTableLinkEditAtom* fSymbolTableAtom;
+ IndirectTableLinkEditAtom* fIndirectTableAtom;
+ StringsLinkEditAtom* fStringsAtom;
+ macho_nlist* fSymbolTable;
+ //char* fStringPool;
+ //uint32_t fStringPoolUsed;
+ //uint32_t fStringPoolSize;
+ std::vector<macho_relocation_info> fInternalRelocs;
+ std::vector<macho_relocation_info> fExternalRelocs;
+ std::vector<IndirectEntry> fIndirectSymbolTable;
+ 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;
+ bool fEmitVirtualSections;
+ bool fHasWeakExports;
+ bool fReferencesWeakImports;
+};
+
+
+class WriterAtom : public ObjectFile::Atom
+{
+protected:
+ class Segment;
+public:
+ enum Kind { zeropage, machHeaderApp, machHeaderDylib, machHeaderBundle, machHeaderObject, loadCommands, undefinedProxy };
+ WriterAtom(Writer& writer, class WriterAtom::Segment& segment) : fWriter(writer), fSegment(segment) {}
+
+ virtual ObjectFile::Reader* getFile() const { return &fWriter; }
+ virtual const char* getName() const { return NULL; }
+ virtual const char* getDisplayName() const { return this->getName(); }
+ virtual Scope getScope() const { return ObjectFile::Atom::scopeTranslationUnit; }
+ virtual bool isTentativeDefinition() const { return false; }
+ virtual bool isWeakDefinition() const { return false; }
+ virtual bool isCoalesableByName() const { return false; }
+ virtual bool isCoalesableByValue() const { return false; }
+ virtual bool isZeroFill() const { return false; }
+ virtual bool dontDeadStrip() const { return true; }
+ virtual bool dontStripName() const { return false; }
+ virtual bool isImportProxy() const { return false; }
+ virtual std::vector<ObjectFile::Reference*>& getReferences() const { return fgEmptyReferenceList; }
+ virtual bool mustRemainInSection() const { return true; }
+ virtual ObjectFile::Segment& getSegment() const { return fSegment; }
+ virtual bool requiresFollowOnAtom() const { return false; }
+ virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); }
+ virtual std::vector<ObjectFile::StabsInfo>* getStabsDebugInfo() const { return NULL; }
+ virtual uint8_t getAlignment() const { return 2; }
+ virtual WeakImportSetting getImportWeakness() const { return Atom::kWeakUnset; }
+ virtual void copyRawContent(uint8_t buffer[]) const { throw "don't use copyRawContent"; }
+ virtual void setScope(Scope) { }
+ virtual void setImportWeakness(bool weakImport) { }
+
+
+protected:
+ virtual ~WriterAtom() {}
+
+ class Segment : public ObjectFile::Segment
+ {
+ public:
+ Segment(const char* name, bool readable, bool writable, bool executable)
+ : fName(name), fReadable(readable), fWritable(writable), fExecutable(executable) {}
+ 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; }
+ private:
+ const char* fName;
+ const bool fReadable;
+ const bool fWritable;
+ const bool fExecutable;
+ };
+
+ static std::vector<ObjectFile::Reference*> fgEmptyReferenceList;
+ static Segment fgTextSegment;
+ static Segment fgPageZeroSegment;
+ static Segment fgLinkEditSegment;
+ static Segment fgStackSegment;
+
+
+ Writer& fWriter;
+ Segment& fSegment;
+};
+
+
+WriterAtom::Segment WriterAtom::fgPageZeroSegment("__PAGEZERO", false, false, false);
+WriterAtom::Segment WriterAtom::fgTextSegment("__TEXT", true, false, true);
+WriterAtom::Segment WriterAtom::fgLinkEditSegment("__LINKEDIT", true, false, false);
+WriterAtom::Segment WriterAtom::fgStackSegment("__UNIXSTACK", true, true, false);
+std::vector<ObjectFile::Reference*> WriterAtom::fgEmptyReferenceList;
+
+class PageZeroAtom : public WriterAtom
+{
+public:
+ PageZeroAtom(Writer& writer) : WriterAtom(writer, 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; }
+ virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const {}
+};
+
+class MachHeaderAtom : public WriterAtom
+{
+public:
+ MachHeaderAtom(Writer& writer) : WriterAtom(writer, fgTextSegment) {}
+ virtual const char* getName() const;
+ virtual const char* getDisplayName() const;
+ virtual Scope getScope() const;
+ virtual bool dontStripName() const;
+ virtual uint64_t getSize() const;
+ virtual uint8_t getAlignment() const { return 12; }
+ virtual const char* getSectionName() const { return "._mach_header"; }
+ virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
+};
+
+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; }
+ virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const {}
+};
+
+class SegmentLoadCommandsAtom : public WriterAtom
+{
+public:
+ SegmentLoadCommandsAtom(Writer& writer) : WriterAtom(writer, 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 writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
+
+ void computeSize();
+ void setup();
+ unsigned int commandCount() { return fCommandCount; }
+ void assignFileOffsets();
+private:
+ unsigned int fCommandCount;
+ uint32_t fSize;
+};
+
+class SymbolTableLoadCommandsAtom : public WriterAtom
+{
+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 writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
+
+private:
+ macho_symtab_command fSymbolTable;
+ macho_dysymtab_command fDynamicSymbolTable;
+};
+
+class ThreadsLoadCommandsAtom : public WriterAtom
+{
+public:
+ ThreadsLoadCommandsAtom(Writer& writer) : WriterAtom(writer, 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 writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
+private:
+ uint8_t* fBuffer;
+ uint32_t fBufferSize;
+};
+
+class DyldLoadCommandsAtom : public WriterAtom
+{
+public:
+ DyldLoadCommandsAtom(Writer& writer) : WriterAtom(writer, 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 writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
+};
+
+class DylibLoadCommandsAtom : public WriterAtom
+{
+public:
+ DylibLoadCommandsAtom(Writer& writer, ExecutableFile::DyLibUsed& info) : WriterAtom(writer, 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 writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
+private:
+ ExecutableFile::DyLibUsed& fInfo;
+};
+
+class DylibIDLoadCommandsAtom : public WriterAtom
+{
+public:
+ DylibIDLoadCommandsAtom(Writer& writer) : WriterAtom(writer, 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 writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
+};
+
+class RoutinesLoadCommandsAtom : public WriterAtom
+{
+public:
+ RoutinesLoadCommandsAtom(Writer& writer) : WriterAtom(writer, fgTextSegment) {}
+ virtual const char* getDisplayName() const { return "routines load command"; }
+ virtual uint64_t getSize() const;
+ virtual uint8_t getAlignment() const { return 2; }
+ virtual const char* getSectionName() const { return "._load_commands"; }
+ virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
+};
+
+class SubUmbrellaLoadCommandsAtom : public WriterAtom
+{
+public:
+ SubUmbrellaLoadCommandsAtom(Writer& writer, const char* name) : WriterAtom(writer, 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 writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
+private:
+ const char* fName;
+};
+
+class SubLibraryLoadCommandsAtom : public WriterAtom
+{
+public:
+ SubLibraryLoadCommandsAtom(Writer& writer, const char* nameStart, int nameLen)
+ : WriterAtom(writer, 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 writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
+private:
+ const char* fNameStart;
+ int fNameLength;
+};
+
+class UmbrellaLoadCommandsAtom : public WriterAtom
+{
+public:
+ UmbrellaLoadCommandsAtom(Writer& writer, const char* name)
+ : WriterAtom(writer, 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 writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
+private:
+ const char* fName;
+};
+
+class LoadCommandsPaddingAtom : public WriterAtom
+{
+public:
+ LoadCommandsPaddingAtom(Writer& writer)
+ : WriterAtom(writer, fgTextSegment), fSize(0) {}
+ virtual const char* getDisplayName() const { return "header padding"; }
+ virtual uint64_t getSize() const;
+ virtual uint8_t getAlignment() const { return 2; }
+ virtual const char* getSectionName() const { return "._load_cmds_pad"; }
+ virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
+
+ void setSize(uint64_t newSize) { fSize = newSize; }
+private:
+ uint64_t fSize;
+};
+
+class LinkEditAtom : public WriterAtom
+{
+public:
+ LinkEditAtom(Writer& writer) : WriterAtom(writer, fgLinkEditSegment) {}
+ uint64_t getFileOffset() const;
+};
+
+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 writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
+};
+
+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 writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
+};
+
+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 writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
+};
+
+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 writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
+};
+
+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 writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
+
+ int32_t add(const char* name);
+ int32_t emptyString();
+
+private:
+ enum { kBufferSize = 0x01000000 };
+
+ std::vector<char*> fFullBuffers;
+ char* fCurrentBuffer;
+ uint32_t fCurrentBufferUsed;
+};
+
+
+
+class UndefinedSymbolProxyAtom : public WriterAtom
+{
+public:
+ UndefinedSymbolProxyAtom(Writer& writer, const char* name) : WriterAtom(writer, fgLinkEditSegment), fName(name), fWeakImportSetting(Atom::kWeakUnset) {}
+ virtual const char* getName() const { return fName; }
+ virtual Scope getScope() const { return ObjectFile::Atom::scopeGlobal; }
+ virtual uint64_t getSize() const { return 0; }
+ virtual bool isWeakDefinition() const { return true; }
+ virtual bool isImportProxy() const { return true; }
+ virtual const char* getSectionName() const { return "._imports"; }
+ virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const {}
+ virtual WeakImportSetting getImportWeakness() const { return fWeakImportSetting; }
+ virtual void setImportWeakness(bool weakImport) { fWeakImportSetting = weakImport ? kWeakImport : kNonWeakImport; }
+private:
+ const char* fName;
+ WeakImportSetting fWeakImportSetting;
+};
+
+
+
+struct ExportSorter
+{
+ bool operator()(ObjectFile::Atom* left, ObjectFile::Atom* right)
+ {
+ return (strcmp(left->getName(), right->getName()) < 0);
+ }
+};
+
+
+ExecutableFile::Writer* MakeWriter(const char* path, Options& options, std::vector<ExecutableFile::DyLibUsed>& dynamicLibraries)
+{
+ return new Writer(path, options, dynamicLibraries);
+}
+
+Writer::SectionInfo::SectionInfo()
+ : fFileOffset(0), fSize(0), fRelocCount(0), fRelocOffset(0), fIndirectSymbolOffset(0), fAlignment(0),
+ fAllLazyPointers(false), fAllNonLazyPointers(false), fAllZeroFill(false), fVirtualSection(false)
+{
+ fSegmentName[0] = '\0';
+ fSectionName[0] = '\0';
+}
+
+Writer::SegmentInfo::SegmentInfo()
+ : fInitProtection(0), fMaxProtection(0), fFileOffset(0), fFileSize(0), fBaseAddress(0), fSize(0)
+{
+ fName[0] = '\0';
+}
+
+
+Writer::Writer(const char* path, Options& options, std::vector<ExecutableFile::DyLibUsed>& dynamicLibraries)
+ : ExecutableFile::Writer(dynamicLibraries), fFilePath(strdup(path)), fOptions(options), fLoadCommandsSection(NULL),
+ fLoadCommandsSegment(NULL),
+ //fStringPool(NULL), fStringPoolUsed(0), fStringPoolSize(0),
+ fEmitVirtualSections(false), fHasWeakExports(false), fReferencesWeakImports(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));
+ 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(new ThreadsLoadCommandsAtom(*this));
+ if ( fOptions.hasCustomStack() )
+ fWriterSynthesizedAtoms.push_back(new CustomStackAtom(*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;
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ 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(new SymbolTableLoadCommandsAtom(*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;
+ case Options::kDyld:
+ 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(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<class ObjectFile::Reader*, uint32_t>::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) {
+ if ( it->first == dylibInfo.directReader ) {
+ //fprintf(stderr, "ordinal %d for indirect %s\n", it->second, dylibInfo.reader->getPath());
+ fLibraryToOrdinal[dylibInfo.reader] = it->second;
+ found = true;
+ break;
+ }
+ }
+ if ( ! found )
+ fprintf(stderr, "ld64 warning: ordinal not found for %s, parent %s\n", dylibInfo.reader->getPath(), dylibInfo.directReader != NULL ? dylibInfo.directReader->getPath() : NULL);
+ }
+ }
+ else {
+ // see if a DylibLoadCommandsAtom has already been created for this install path
+ bool newDylib = true;
+ const char* dylibInstallPath = dylibInfo.reader->getInstallPath();
+ if ( dylibInfo.options.fInstallPathOverride != NULL )
+ dylibInstallPath = dylibInfo.options.fInstallPathOverride;
+ for (unsigned int seenLib=0; seenLib < i; ++seenLib) {
+ ExecutableFile::DyLibUsed& seenDylibInfo = dynamicLibraries[seenLib];
+ if ( !seenDylibInfo.indirect ) {
+ 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()));
+ }
+ }
+ break;
+ case Options::kStaticExecutable:
+ case Options::kObjectFile:
+ case Options::kDyld:
+ break;
+ }
+
+ //fprintf(stderr, "ordinals table:\n");
+ //for (std::map<class ObjectFile::Reader*, uint32_t>::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) {
+ // fprintf(stderr, "%d <== %s\n", it->second, it->first->getPath());
+ //}
+}
+
+Writer::~Writer()
+{
+ if ( fFilePath != NULL )
+ free((void*)fFilePath);
+ if ( fSymbolTable != NULL )
+ delete [] fSymbolTable;
+ //if ( fStringPool != NULL )
+ // delete [] fStringPool;
+}
+
+const char* Writer::getPath()
+{
+ return fFilePath;
+}
+
+
+std::vector<class ObjectFile::Atom*>& Writer::getAtoms()
+{
+ return fWriterSynthesizedAtoms;
+}
+
+std::vector<class ObjectFile::Atom*>* Writer::getJustInTimeAtomsFor(const char* name)
+{
+ return NULL;
+}
+
+std::vector<ObjectFile::StabsInfo>* Writer::getStabsDebugInfo()
+{
+ return NULL;
+}
+
+ObjectFile::Atom* Writer::getUndefinedProxyAtom(const char* name)
+{
+ if ( (fOptions.outputKind() == Options::kObjectFile)
+ || (fOptions.undefinedTreatment() != Options::kUndefinedError) )
+ return new UndefinedSymbolProxyAtom(*this, name);
+ else
+ return NULL;
+}
+
+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<class ObjectFile::Reader*, uint32_t>::iterator pos = fLibraryToOrdinal.find(lib);
+ if ( pos != fLibraryToOrdinal.end() )
+ return pos->second;
+
+ throw "can't find ordinal for imported symbol";
+}
+
+
+void Writer::write(std::vector<class ObjectFile::Atom*>& atoms, class ObjectFile::Atom* entryPointAtom)
+{
+ fAllAtoms = &atoms;
+ fEntryPoint = entryPointAtom;
+
+ // 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();
+
+ // build symbol table and relocations
+ buildLinkEdit();
+
+ // write everything
+ writeAtoms();
+}
+
+void Writer::buildLinkEdit()
+{
+ this->collectExportedAndImportedAndLocalAtoms();
+ this->buildSymbolTable();
+ this->buildFixups();
+ this->adjustLinkEditSections();
+}
+
+
+
+uint64_t Writer::getAtomLoadAddress(const ObjectFile::Atom* atom)
+{
+ return atom->getAddress();
+// SectionInfo* info = (SectionInfo*)atom->getSection();
+// return info->getBaseAddress() + atom->getSectionOffset();
+}
+
+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.keepPrivateExterns() && (fOptions.outputKind() == Options::kObjectFile) )
+ 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->dontStripName())
+ entry->set_n_type(N_EXT | N_ABS);
+
+ // set n_desc
+ uint16_t desc = 0;
+ if ( atom->dontStripName() )
+ desc |= REFERENCED_DYNAMICALLY;
+ if ( atom->isWeakDefinition() && (strcmp(atom->getSectionName(), "__common") != 0) ) {
+ 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));
+}
+
+void Writer::setImportNlist(const ObjectFile::Atom* atom, macho_nlist* entry)
+{
+ // set n_type
+ entry->set_n_type(N_UNDF | N_EXT);
+
+ // 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->dontStripName() )
+ desc |= REFERENCED_DYNAMICALLY;
+ // an import proxy is always weak (overridden by definition in .o files)
+ // so we ask its reader if the exported symbol in its dylib is weak
+ if ( ( fOptions.outputKind() != Options::kObjectFile) && atom->getFile()->isDefinitionWeak(*atom) ) {
+ desc |= N_REF_TO_WEAK;
+ fReferencesWeakImports = true;
+ }
+ // set weak_import attribute
+ if ( atom->getImportWeakness() == ObjectFile::Atom::kWeakImport )
+ 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());
+}
+
+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 <mach-o/ldsyms.h> synthesized lable for mach_header needs special section number...
+ if ( strcmp(atom->getSectionName(), "._mach_header") == 0 )
+ sectIndex = 1;
+ }
+ entry->set_n_sect(sectIndex);
+
+ // set n_desc
+ uint16_t desc = 0;
+ if ( atom->isWeakDefinition() && (strcmp(atom->getSectionName(), "__common") != 0) ) // commons on not weak
+ 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));
+}
+
+
+void Writer::setNlistRange(std::vector<class ObjectFile::Atom*>& 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);
+ }
+ }
+}
+
+void Writer::buildSymbolTable()
+{
+ fSymbolTableStabsStartIndex = 0;
+ fSymbolTableStabsCount = this->collectStabs();
+ 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, fSymbolTableStabsCount);
+}
+
+
+
+bool Writer::shouldExport(ObjectFile::Atom& atom)
+{
+ switch ( atom.getScope() ) {
+ case ObjectFile::Atom::scopeGlobal:
+ return true;
+ case ObjectFile::Atom::scopeLinkageUnit:
+ return ( fOptions.keepPrivateExterns() && (fOptions.outputKind() == Options::kObjectFile) );
+ default:
+ return false;
+ }
+}
+
+void Writer::collectExportedAndImportedAndLocalAtoms()
+{
+ const int atomCount = fAllAtoms->size();
+ 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());
+ if ( atom->isImportProxy() || ((fOptions.outputKind() == Options::kObjectFile) && (strcmp(atom->getSectionName(), "__common") == 0)) )
+ fImportedAtoms.push_back(atom);
+ else if ( this->shouldExport(*atom) )
+ fExportedAtoms.push_back(atom);
+ else if ( !fOptions.stripLocalSymbols() )
+ fLocalSymbolAtoms.push_back(atom);
+ }
+ }
+
+ // sort exported atoms by name
+ std::sort(fExportedAtoms.begin(), fExportedAtoms.end(), ExportSorter());
+}
+
+
+bool Writer::stabChunkCompare(const struct StabChunks& lhs, const struct StabChunks& rhs)
+{
+ if ( lhs.fReader != rhs.fReader ) {
+ return lhs.fReader < rhs.fReader;
+ }
+ return lhs.fOrderInReader < rhs.fOrderInReader;
+}
+
+unsigned int Writer::collectStabs()
+{
+ unsigned int count = 0;
+
+ // collect all stabs chunks
+ std::set<ObjectFile::Reader*> seenReaders;
+ const int atomCount = fAllAtoms->size();
+ for (int i=0; i < atomCount; ++i) {
+ ObjectFile::Atom* atom = (*fAllAtoms)[i];
+ ObjectFile::Reader* atomsReader = atom->getFile();
+ if ( (atomsReader != NULL) && (seenReaders.count(atomsReader) == 0) ) {
+ seenReaders.insert(atomsReader);
+ std::vector<ObjectFile::StabsInfo>* readerStabs = atomsReader->getStabsDebugInfo();
+ if ( readerStabs != NULL ) {
+ StabChunks chunk;
+ chunk.fAtom = NULL;
+ chunk.fReader = atomsReader;
+ chunk.fOrderInReader = 0;
+ chunk.fStabs = readerStabs;
+ fStabChunks.push_back(chunk);
+ count += readerStabs->size() + 1; // extra one is for trailing N_SO
+ }
+ }
+ std::vector<ObjectFile::StabsInfo>* atomStabs = atom->getStabsDebugInfo();
+ if ( atomStabs != NULL ) {
+ StabChunks chunk;
+ chunk.fAtom = atom;
+ chunk.fReader = atomsReader;
+ chunk.fOrderInReader = atom->getSortOrder();
+ chunk.fStabs = atomStabs;
+ fStabChunks.push_back(chunk);
+ count += atomStabs->size();
+ }
+ }
+
+ // sort by order in original .o file
+ std::sort(fStabChunks.begin(), fStabChunks.end(), stabChunkCompare);
+
+ return count;
+}
+
+macho_uintptr_t Writer::valueForStab(const ObjectFile::StabsInfo& stab, const ObjectFile::Atom* atom)
+{
+ switch ( stab.type ) {
+ case N_FUN:
+ if ( stab.other == 0 )
+ break;
+ // end of function N_FUN has size (not address) so should not be adjusted
+ // fall through
+ case N_BNSYM:
+ case N_ENSYM:
+ case N_LBRAC:
+ case N_RBRAC:
+ case N_SLINE:
+ case N_STSYM:
+ case N_LCSYM:
+ // all these stab types need their value changed from an offset in the atom to an address
+ if ( atom != NULL )
+ return getAtomLoadAddress(atom) + stab.atomOffset;
+ }
+ return stab.atomOffset;
+}
+
+
+void Writer::addStabs(uint32_t startIndex, uint32_t count)
+{
+ macho_nlist* entry = &fSymbolTable[startIndex];
+ const int chunkCount = fStabChunks.size();
+ for (int i=0; i < chunkCount; ++i ) {
+ const StabChunks& chunk = fStabChunks[i];
+ const int stabCount = chunk.fStabs->size();
+ for (int j=0; j < stabCount; ++j ) {
+ const ObjectFile::StabsInfo& stab = (*chunk.fStabs)[j];
+ entry->set_n_type(stab.type);
+ entry->set_n_sect(stab.other);
+ entry->set_n_desc(stab.desc);
+ entry->set_n_value(valueForStab(stab, chunk.fAtom));
+ entry->set_n_strx(this->fStringsAtom->add(stab.string));
+ ++entry;
+ }
+ if ( (i == chunkCount-1) || (fStabChunks[i+1].fReader != chunk.fReader) ) {
+ // need to add empty SO at end of each file
+ entry->set_n_type(N_SO);
+ entry->set_n_sect(1);
+ entry->set_n_desc(0);
+ entry->set_n_value(0);
+ entry->set_n_strx(this->fStringsAtom->emptyString());
+ ++entry;
+ }
+ }
+}
+
+
+
+
+uint32_t Writer::symbolIndex(ObjectFile::Atom& atom)
+{
+ // search imports
+ const int importCount = fImportedAtoms.size();
+ for (int i=0; i < importCount; ++i) {
+ if ( &atom == fImportedAtoms[i] )
+ return i + fSymbolTableImportStartIndex;
+ }
+
+ // search locals
+ const int localCount = fLocalSymbolAtoms.size();
+ for (int i=0; i < localCount; ++i) {
+ if ( &atom == fLocalSymbolAtoms[i] )
+ return i + fSymbolTableLocalStartIndex;
+ }
+
+ // search exports
+ const int exportCount = fExportedAtoms.size();
+ for (int i=0; i < exportCount; ++i) {
+ if ( &atom == fExportedAtoms[i] )
+ return i + fSymbolTableExportStartIndex;
+ }
+
+ fprintf(stderr, "symbolIndex(%s)\n", atom.getDisplayName());
+ fprintf(stderr, "from %s\n", atom.getFile()->getPath());
+ throw "atom not found";
+}
+
+
+void Writer::buildFixups()
+{
+ if ( fOptions.outputKind() == Options::kObjectFile )
+ this->buildObjectFileFixups();
+ else
+ this->buildExecutableFixups();
+}
+
+uint32_t Writer::addRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref)
+{
+ ObjectFile::Atom& target = ref->getTarget();
+ bool isExtern = target.isImportProxy() || ( strcmp(target.getSectionName(), "__common") == 0 );
+ 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;
+
+ switch ( ref->getKind() ) {
+ case ObjectFile::Reference::noFixUp:
+ return 0;
+
+ case ObjectFile::Reference::pointer:
+ 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(macho_relocation_info::pointer_length);
+ reloc1.set_r_extern(isExtern);
+ reloc1.set_r_type(GENERIC_RELOC_VANILLA);
+ fInternalRelocs.insert(fInternalRelocs.begin(), reloc1);
+ return 1;
+
+ case ObjectFile::Reference::ppcFixupBranch24:
+ 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());
+ }
+ fInternalRelocs.insert(fInternalRelocs.begin(), reloc1);
+ return 1;
+
+ case ObjectFile::Reference::ppcFixupBranch14:
+ reloc1.set_r_address(address);
+ reloc1.set_r_symbolnum(sectionNum);
+ reloc1.set_r_pcrel(true);
+ reloc1.set_r_length(2);
+ reloc1.set_r_extern(false);
+ reloc1.set_r_type(PPC_RELOC_BR14);
+ fInternalRelocs.insert(fInternalRelocs.begin(), reloc1);
+ return 1;
+
+ case ObjectFile::Reference::ppcFixupPicBaseLow14:
+ case ObjectFile::Reference::ppcFixupPicBaseLow16:
+ {
+ macho_uintptr_t fromAddr = atom->getAddress() + ref->getFromTargetOffset();
+ macho_uintptr_t toAddr = target.getAddress() + ref->getTargetOffset();
+ uint32_t overflow = 0;
+ if ( ((toAddr-fromAddr) & 0x00008000) != 0 )
+ overflow = 1;
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(2);
+ if ( ref->getKind() == ObjectFile::Reference::ppcFixupPicBaseLow16 )
+ sreloc1->set_r_type(PPC_RELOC_LO16_SECTDIFF);
+ else
+ sreloc1->set_r_type(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);
+ fInternalRelocs.insert(fInternalRelocs.begin(), reloc2);
+ fInternalRelocs.insert(fInternalRelocs.begin(), reloc1);
+ return 2;
+ }
+
+ case ObjectFile::Reference::ppcFixupPicBaseHigh16:
+ {
+ macho_uintptr_t fromAddr = atom->getAddress() + ref->getFromTargetOffset();
+ macho_uintptr_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);
+ fInternalRelocs.insert(fInternalRelocs.begin(), reloc2);
+ fInternalRelocs.insert(fInternalRelocs.begin(), reloc1);
+ return 2;
+ }
+
+ case ObjectFile::Reference::ppcFixupAbsLow14:
+ case ObjectFile::Reference::ppcFixupAbsLow16:
+ {
+ macho_uintptr_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);
+ if ( ref->getKind() == ObjectFile::Reference::ppcFixupAbsLow16 )
+ reloc1.set_r_type(PPC_RELOC_LO16);
+ else
+ reloc1.set_r_type(PPC_RELOC_LO14);
+ }
+ else {
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(2);
+ if ( ref->getKind() == ObjectFile::Reference::ppcFixupAbsLow16 )
+ sreloc1->set_r_type(PPC_RELOC_LO16);
+ else
+ sreloc1->set_r_type(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);
+ fInternalRelocs.insert(fInternalRelocs.begin(), reloc2);
+ fInternalRelocs.insert(fInternalRelocs.begin(), reloc1);
+ return 2;
+ }
+
+ case ObjectFile::Reference::ppcFixupAbsHigh16:
+ {
+ macho_uintptr_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);
+ fInternalRelocs.insert(fInternalRelocs.begin(), reloc2);
+ fInternalRelocs.insert(fInternalRelocs.begin(), reloc1);
+ return 2;
+ }
+
+ case ObjectFile::Reference::ppcFixupAbsHigh16AddLow:
+ {
+ macho_uintptr_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);
+ fInternalRelocs.insert(fInternalRelocs.begin(), reloc2);
+ fInternalRelocs.insert(fInternalRelocs.begin(), reloc1);
+ return 2;
+ }
+
+ case ObjectFile::Reference::pointer32Difference:
+ case ObjectFile::Reference::pointer64Difference:
+ {
+ macho_uintptr_t toAddr = target.getAddress() + ref->getTargetOffset();
+ macho_uintptr_t fromAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset();
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ if ( ref->getKind() == ObjectFile::Reference::pointer64Difference )
+ sreloc1->set_r_length(3);
+ else
+ sreloc1->set_r_length(2);
+ if ( ref->getTargetOffset() != 0 )
+ sreloc1->set_r_type(PPC_RELOC_LOCAL_SECTDIFF);
+ else
+ sreloc1->set_r_type(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(macho_relocation_info::pointer_length);
+ sreloc2->set_r_type(PPC_RELOC_PAIR);
+ sreloc2->set_r_address(0);
+ sreloc2->set_r_value(fromAddr);
+ fInternalRelocs.insert(fInternalRelocs.begin(), reloc2);
+ fInternalRelocs.insert(fInternalRelocs.begin(), reloc1);
+ return 2;
+ }
+
+ case ObjectFile::Reference::x86FixupBranch32:
+ reloc1.set_r_address(address);
+ reloc1.set_r_symbolnum(sectionNum);
+ reloc1.set_r_pcrel(true);
+ reloc1.set_r_length(2);
+ reloc1.set_r_extern(false);
+ reloc1.set_r_type(GENERIC_RELOC_VANILLA);
+ fInternalRelocs.insert(fInternalRelocs.begin(), reloc1);
+ return 1;
+
+ }
+ return 0;
+}
+
+
+void Writer::buildObjectFileFixups()
+{
+ uint32_t relocIndex = 0;
+ std::vector<SegmentInfo*>& segmentInfos = fSegmentInfos;
+ const int segCount = segmentInfos.size();
+ for(int i=0; i < segCount; ++i) {
+ SegmentInfo* curSegment = segmentInfos[i];
+ std::vector<SectionInfo*>& sectionInfos = curSegment->fSections;
+ const int sectionCount = sectionInfos.size();
+ for(int j=0; j < sectionCount; ++j) {
+ SectionInfo* curSection = sectionInfos[j];
+ std::vector<ObjectFile::Atom*>& sectionAtoms = curSection->fAtoms;
+ if ( ! curSection->fAllZeroFill ) {
+ if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers )
+ curSection->fIndirectSymbolOffset = fIndirectSymbolTable.size();
+ curSection->fRelocOffset = relocIndex;
+ const int atomCount = sectionAtoms.size();
+ for (int k=0; k < atomCount; ++k) {
+ ObjectFile::Atom* atom = sectionAtoms[k];
+ std::vector<ObjectFile::Reference*>& refs = atom->getReferences();
+ const int refCount = refs.size();
+ for (int l=0; l < refCount; ++l) {
+ ObjectFile::Reference* ref = refs[l];
+ relocIndex += this->addRelocs(atom, ref);
+ if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers ) {
+ uint32_t offsetInSection = atom->getSectionOffset();
+ uint32_t indexInSection = offsetInSection / sizeof(macho_uintptr_t);
+ uint32_t undefinedSymbolIndex = this->symbolIndex(ref->getTarget());
+ uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset;
+ IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex };
+ //printf("fIndirectSymbolTable.add(%d-%d => 0x%X-%s), size=%lld\n", indexInSection, indirectTableIndex, undefinedSymbolIndex, ref->getTarget().getName(), atom->getSize());
+ fIndirectSymbolTable.push_back(entry);
+ }
+ }
+ }
+ curSection->fRelocCount = relocIndex - curSection->fRelocOffset;
+ }
+ }
+ }
+
+ // now reverse reloc entries
+ for(int i=0; i < segCount; ++i) {
+ SegmentInfo* curSegment = segmentInfos[i];
+ std::vector<SectionInfo*>& sectionInfos = curSegment->fSections;
+ const int sectionCount = sectionInfos.size();
+ for(int j=0; j < sectionCount; ++j) {
+ SectionInfo* curSection = sectionInfos[j];
+ curSection->fRelocOffset = relocIndex - curSection->fRelocOffset - curSection->fRelocCount;
+ }
+ }
+
+}
+
+
+void Writer::buildExecutableFixups()
+{
+ const bool slideable = (fOptions.outputKind() != Options::kDynamicExecutable) && (fOptions.outputKind() != Options::kStaticExecutable);
+ std::vector<SegmentInfo*>& segmentInfos = fSegmentInfos;
+ const int segCount = segmentInfos.size();
+ for(int i=0; i < segCount; ++i) {
+ SegmentInfo* curSegment = segmentInfos[i];
+ std::vector<SectionInfo*>& sectionInfos = curSegment->fSections;
+ const int sectionCount = sectionInfos.size();
+ for(int j=0; j < sectionCount; ++j) {
+ SectionInfo* curSection = sectionInfos[j];
+ std::vector<ObjectFile::Atom*>& sectionAtoms = curSection->fAtoms;
+ if ( ! curSection->fAllZeroFill ) {
+ if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers )
+ curSection->fIndirectSymbolOffset = fIndirectSymbolTable.size();
+ const int atomCount = sectionAtoms.size();
+ for (int k=0; k < atomCount; ++k) {
+ ObjectFile::Atom* atom = sectionAtoms[k];
+ std::vector<ObjectFile::Reference*>& refs = atom->getReferences();
+ const int refCount = refs.size();
+ //printf("atom %s has %d references\n", atom->getDisplayName(), refCount);
+
+ for (int l=0; l < refCount; ++l) {
+ ObjectFile::Reference* ref = refs[l];
+ // only care about references that need dyld fixups
+ if ( ref->requiresRuntimeFixUp() ) {
+ if ( ! atom->getSegment().isContentWritable() )
+ throwf("relocations in read-only segments not supported. %s in %s reference to %s", atom->getDisplayName(), atom->getFile()->getPath(), ref->getTarget().getDisplayName());
+ if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers ) {
+ // if atom is in (non)lazy_pointer section, this is encoded as an indirect symbol
+ if ( atom->getSize() != sizeof(macho_uintptr_t) ) {
+ printf("oversize pointer atom %s from file %s\n", atom->getDisplayName(), atom->getFile()->getPath());
+ }
+ uint32_t offsetInSection = atom->getSectionOffset();
+ uint32_t indexInSection = offsetInSection / sizeof(macho_uintptr_t);
+ uint32_t undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL;
+ //printf("indirect pointer atom %s section offset = %d\n", atom->getDisplayName(), offsetInSection);
+ if ( ref->getTarget().isImportProxy()
+ || ref->getTarget().isWeakDefinition()
+ || (fOptions.interposable() && fOptions.shouldExport(ref->getTarget().getName()))
+ || (fOptions.nameSpace() == Options::kFlatNameSpace)
+ || (fOptions.nameSpace() == Options::kForceFlatNameSpace) ) {
+ undefinedSymbolIndex = this->symbolIndex(ref->getTarget());
+ }
+ uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset;
+ IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex };
+ //printf("fIndirectSymbolTable.add(%d-%d => 0x%X-%s), size=%lld\n", indexInSection, indirectTableIndex, undefinedSymbolIndex, ref->getTarget().getName(), atom->getSize());
+ fIndirectSymbolTable.push_back(entry);
+ if ( slideable && curSection->fAllLazyPointers ) {
+ // if this is a dylib/bundle, need PBLAPTR internal relocation to fix up binding handler if image slides
+ macho_relocation_info pblaReloc;
+ macho_scattered_relocation_info* pblaSReloc = (macho_scattered_relocation_info*)&pblaReloc;
+ pblaSReloc->set_r_scattered(true);
+ pblaSReloc->set_r_pcrel(false);
+ pblaSReloc->set_r_length(macho_relocation_info::pointer_length);
+ pblaSReloc->set_r_type(PPC_RELOC_PB_LA_PTR);
+ pblaSReloc->set_r_address(atom->getAddress()-fOptions.baseAddress());
+ pblaSReloc->set_r_value(ref->getFromTarget().getAddress()); // helper is stored in "from" address
+ fInternalRelocs.push_back(pblaReloc);
+ }
+ }
+ else if ( ref->getTarget().isImportProxy() ) {
+ // if import is to antoher dylib, this is encoded as an external relocation
+ 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(macho_relocation_info::pointer_length);
+ externalReloc.set_r_extern(true);
+ externalReloc.set_r_type(GENERIC_RELOC_VANILLA);
+ fExternalRelocs.push_back(externalReloc);
+ }
+ else if ( slideable ) {
+ // if this is a dylib/bundle, need fix-up encoded as an internal relocation
+ 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(macho_relocation_info::pointer_length);
+ internalReloc.set_r_extern(false);
+ internalReloc.set_r_type(GENERIC_RELOC_VANILLA);
+ fInternalRelocs.push_back(internalReloc);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+class ContentWriter : public ObjectFile::ContentWriter
+{
+public:
+ ContentWriter(int fd, uint64_t fileOffset) : fFileDescriptor(fd), fFileOffset(fileOffset) {}
+ virtual void write(uint64_t atomOffset, const void* buffer, uint64_t size) {
+ ::pwrite(fFileDescriptor, buffer, size, fFileOffset+atomOffset);
+ }
+private:
+ int fFileDescriptor;
+ uint64_t fFileOffset;
+};
+
+
+void Writer::writeAtoms()
+{
+ const bool requireAllFixUps = (fOptions.outputKind() != Options::kObjectFile);
+
+ std::vector<SegmentInfo*>& segmentInfos = fSegmentInfos;
+ const int segCount = segmentInfos.size();
+ for(int i=0; i < segCount; ++i) {
+ SegmentInfo* curSegment = segmentInfos[i];
+ bool isText = ((curSegment->fInitProtection & VM_PROT_EXECUTE) != 0);
+ std::vector<SectionInfo*>& sectionInfos = curSegment->fSections;
+ const int sectionCount = sectionInfos.size();
+ for(int j=0; j < sectionCount; ++j) {
+ SectionInfo* curSection = sectionInfos[j];
+ std::vector<ObjectFile::Atom*>& sectionAtoms = curSection->fAtoms;
+ //printf("writing %d atoms for section %s\n", (int)sectionAtoms.size(), curSection->fSectionName);
+ if ( ! curSection->fAllZeroFill ) {
+ const int atomCount = sectionAtoms.size();
+ uint32_t end = curSection->fFileOffset;
+ for (int k=0; k < atomCount; ++k) {
+ ObjectFile::Atom* atom = sectionAtoms[k];
+ if ( !atom->isImportProxy() ) {
+ uint32_t offset = curSection->fFileOffset + atom->getSectionOffset();
+ if ( isText && (offset != end) ) {
+ // fill gaps with no-ops
+ #if defined(ARCH_PPC) || defined(ARCH_PPC64)
+ uint32_t ppcNop;
+ OSWriteBigInt32(&ppcNop, 0, 0x60000000);
+ for (uint32_t p=end; p < offset; p += 4)
+ ::pwrite(fFileDescriptor, &ppcNop, 4, p);
+ #else defined(ARCH_I386)
+ uint8_t x86Nop = 0x90;
+ for (uint32_t p=end; p < offset; ++p)
+ ::pwrite(fFileDescriptor, &x86Nop, 1, p);
+ #endif
+ }
+ ContentWriter writer(fFileDescriptor, offset);
+ atom->writeContent(requireAllFixUps, writer);
+ end = offset+atom->getSize();
+ //printf("wrote 0x%08X -> 0x%08X, atom %s\n", offset, end, atom->getDisplayName());
+ }
+ }
+ }
+ }
+ }
+}
+
+
+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;
+ for (unsigned int i=0; i < fAllAtoms->size(); ++i) {
+ ObjectFile::Atom* atom = (*fAllAtoms)[i];
+ if ( atom->getSection() != curSection ) {
+ 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] == '.');
+ 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();
+ this->fSegmentInfos.push_back(currentSegmentInfo);
+ }
+ currentSectionInfo = new SectionInfo();
+ strcpy(currentSectionInfo->fSectionName, atom->getSectionName());
+ strcpy(currentSectionInfo->fSegmentName, atom->getSegment().getName());
+ currentSectionInfo->fAlignment = atom->getAlignment();
+ // check for -sectalign override
+ std::vector<Options::SectionAlignment>& alignmentOverrides = fOptions.sectionAlignments();
+ for(std::vector<Options::SectionAlignment>::iterator it=alignmentOverrides.begin(); it != alignmentOverrides.end(); ++it) {
+ if ( (strcmp(it->segmentName, currentSectionInfo->fSegmentName) == 0) && (strcmp(it->sectionName, currentSectionInfo->fSectionName) == 0) )
+ currentSectionInfo->fAlignment = it->alignment;
+ }
+ currentSectionInfo->fAllZeroFill = atom->isZeroFill();
+ currentSectionInfo->fVirtualSection = ( currentSectionInfo->fSectionName[0] == '.');
+ if ( !currentSectionInfo->fVirtualSection || fEmitVirtualSections )
+ currentSectionInfo->setIndex(sectionIndex++);
+ currentSegmentInfo->fSections.push_back(currentSectionInfo);
+ }
+ if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "._load_commands") == 0) ) {
+ fLoadCommandsSection = currentSectionInfo;
+ fLoadCommandsSegment = currentSegmentInfo;
+ }
+ if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__la_symbol_ptr") == 0) )
+ currentSectionInfo->fAllLazyPointers = true;
+ if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__nl_symbol_ptr") == 0) )
+ currentSectionInfo->fAllNonLazyPointers = 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);
+ currentSectionInfo->fSize = offset + atom->getSize();
+ // add atom to section vector
+ currentSectionInfo->fAtoms.push_back(atom);
+ }
+}
+
+
+void Writer::adjustLoadCommandsAndPadding()
+{
+ fSegmentCommands->computeSize();
+
+ // recompute load command section offsets
+ uint64_t offset = 0;
+ std::vector<class ObjectFile::Atom*>& loadCommandAtoms = fLoadCommandsSection->fAtoms;
+ const unsigned int atomCount = loadCommandAtoms.size();
+ for (unsigned int i=0; i < atomCount; ++i) {
+ ObjectFile::Atom* atom = loadCommandAtoms[i];
+ uint64_t alignment = 1 << atom->getAlignment();
+ offset = ( (offset+alignment-1) & (-alignment) );
+ atom->setSectionOffset(offset);
+ offset += atom->getSize();
+ fLoadCommandsSection->fSize = offset;
+ }
+
+ std::vector<SectionInfo*>& sectionInfos = fLoadCommandsSegment->fSections;
+ const int sectionCount = sectionInfos.size();
+ uint64_t paddingSize = 0;
+ if ( fOptions.outputKind() == Options::kDyld ) {
+ // dyld itself has special padding requirements. We want the beginning __text section to start at a stable address
+ uint32_t totalSizeOfHeaderAndLoadCommands = 0;
+ for(int j=0; j < sectionCount; ++j) {
+ SectionInfo* curSection = sectionInfos[j];
+ totalSizeOfHeaderAndLoadCommands += curSection->fSize;
+ if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 )
+ break;
+ }
+ paddingSize = 4096 - (totalSizeOfHeaderAndLoadCommands % 4096);
+ }
+ else {
+ // 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;
+
+ // 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
+void Writer::assignFileOffsets()
+{
+ bool haveFixedSegments = false;
+ uint64_t fileOffset = 0;
+ uint64_t nextContiguousAddress = fOptions.baseAddress();
+ std::vector<SegmentInfo*>& segmentInfos = fSegmentInfos;
+ const int segCount = segmentInfos.size();
+ for(int i=0; i < segCount; ++i) {
+ SegmentInfo* curSegment = segmentInfos[i];
+ fileOffset = (fileOffset+4095) & (-4096);
+ curSegment->fFileOffset = fileOffset;
+ if ( curSegment->fBaseAddress == 0 ) {
+ // segment has uses next address
+ curSegment->fBaseAddress = nextContiguousAddress;
+ }
+ else {
+ // segment has fixed address
+ haveFixedSegments = true;
+ }
+ uint64_t address = curSegment->fBaseAddress;
+ std::vector<SectionInfo*>& sectionInfos = curSegment->fSections;
+ const int sectionCount = sectionInfos.size();
+ for(int j=0; j < sectionCount; ++j) {
+ SectionInfo* curSection = sectionInfos[j];
+ uint64_t alignment = 1 << curSection->fAlignment;
+ fileOffset = ( (fileOffset+alignment-1) & (-alignment) );
+ address = ( (address+alignment-1) & (-alignment) );
+ curSection->fFileOffset = fileOffset;
+ curSection->setBaseAddress(address);
+ //printf("assignFileOffsets(): setBaseAddress(%s, 0x%08llX)\n", curSection->fSectionName, address);
+ curSegment->fSize = curSection->getBaseAddress() + curSection->fSize - curSegment->fBaseAddress;
+ if ( (fOptions.outputKind() != Options::kObjectFile) || ! curSection->fVirtualSection )
+ address += curSection->fSize;
+ if ( !curSection->fAllZeroFill ) {
+ curSegment->fFileSize = curSegment->fSize;
+ fileOffset += curSection->fSize;
+ }
+ }
+ if ( curSegment->fBaseAddress == nextContiguousAddress )
+ nextContiguousAddress = (curSegment->fBaseAddress+curSegment->fSize+4095) & (-4096);
+ }
+
+ // check for segment overlaps
+ if ( haveFixedSegments ) {
+ for(int i=0; i < segCount; ++i) {
+ SegmentInfo* segment1 = segmentInfos[i];
+ for(int j=0; j < segCount; ++j) {
+ if ( i != j ) {
+ SegmentInfo* segment2 = segmentInfos[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);
+ }
+ }
+ }
+ }
+ }
+}
+
+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<class ObjectFile::Atom*>& atoms = lastSeg->fSections[i]->fAtoms;
+ const unsigned int atomCount = atoms.size();
+ uint64_t sectionOffset = 0;
+ lastSeg->fSections[i]->fFileOffset = fileOffset;
+ lastSeg->fSections[i]->setBaseAddress(address);
+ for (unsigned int j=0; j < atomCount; ++j) {
+ ObjectFile::Atom* atom = atoms[j];
+ uint64_t alignment = 1 << atom->getAlignment();
+ sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) );
+ atom->setSectionOffset(sectionOffset);
+ sectionOffset += atom->getSize();
+ }
+ 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;
+ }
+}
+
+
+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";
+}
+
+bool MachHeaderAtom::dontStripName() const
+{
+ switch ( fWriter.fOptions.outputKind() ) {
+ case Options::kDynamicExecutable:
+ case Options::kStaticExecutable:
+ return true;
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ case Options::kDyld:
+ case Options::kObjectFile:
+ return false;
+ }
+ throw "unknown header type";
+}
+
+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";
+}
+
+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";
+}
+
+uint64_t MachHeaderAtom::getSize() const
+{
+ return macho_header::size;
+}
+
+void MachHeaderAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
+{
+ macho_header mh;
+
+ // 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 ) {
+ flags = MH_SUBSECTIONS_VIA_SYMBOLS;
+ }
+ 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;
+ }
+
+ // get commands info
+ uint32_t commandsSize = 0;
+ uint32_t commandsCount = 0;
+
+ std::vector<class ObjectFile::Atom*>& loadCommandAtoms = fWriter.fLoadCommandsSection->fAtoms;
+ const unsigned int atomCount = loadCommandAtoms.size();
+ for (unsigned int i=0; i < atomCount; ++i) {
+ ObjectFile::Atom* atom = loadCommandAtoms[i];
+ commandsSize += atom->getSize();
+ // segment and symbol table atoms can contain more than one load command
+ if ( atom == fWriter.fSegmentCommands )
+ commandsCount += fWriter.fSegmentCommands->commandCount();
+ else if ( atom == fWriter.fSymbolTableCommands )
+ commandsCount += 2;
+ else
+ ++commandsCount;
+ }
+
+ // fill out mach_header
+ mh.set_magic(macho_header::magic_value);
+ mh.set_cputype(fWriter.fOptions.architecture());
+ mh.set_cpusubtype(0);
+ mh.set_filetype(fileType);
+ mh.set_ncmds(commandsCount);
+ mh.set_sizeofcmds(commandsSize);
+ mh.set_flags(flags);
+ mh.set_reserved();
+
+ // write it
+ writer.write(0, &mh, macho_header::size);
+}
+
+
+CustomStackAtom::CustomStackAtom(Writer& writer)
+ : WriterAtom(writer, fgStackSegment)
+{
+#if defined(ARCH_PPC) || defined(ARCH_PPC64) || defined(ARCH_I386)
+ // stack grows down for these architectures
+ fgStackSegment.setBaseAddress(writer.fOptions.customStackAddr() - writer.fOptions.customStackSize());
+#else
+ #error unknown architecture
+#endif
+}
+
+
+void SegmentLoadCommandsAtom::computeSize()
+{
+ uint64_t size = 0;
+ std::vector<Writer::SegmentInfo*>& segmentInfos = fWriter.fSegmentInfos;
+ const int segCount = segmentInfos.size();
+ for(int i=0; i < segCount; ++i) {
+ size += macho_segment_command::size;
+ std::vector<Writer::SectionInfo*>& sectionInfos = segmentInfos[i]->fSections;
+ const int sectionCount = sectionInfos.size();
+ for(int j=0; j < sectionCount; ++j) {
+ if ( fWriter.fEmitVirtualSections || ! sectionInfos[j]->fVirtualSection )
+ size += macho_section::content_size;
+ }
+ }
+ fSize = size;
+ fCommandCount = segCount;
+}
+
+
+
+void SegmentLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
+{
+ uint64_t size = this->getSize();
+ uint8_t buffer[size];
+ const bool oneSegment =( fWriter.fOptions.outputKind() == Options::kObjectFile );
+ bzero(buffer, fSize);
+ uint8_t* p = buffer;
+ std::vector<Writer::SegmentInfo*>& segmentInfos = fWriter.fSegmentInfos;
+ const int segCount = segmentInfos.size();
+ for(int i=0; i < segCount; ++i) {
+ Writer::SegmentInfo* segInfo = segmentInfos[i];
+ const int sectionCount = segInfo->fSections.size();
+ macho_segment_command* cmd = (macho_segment_command*)p;
+ cmd->set_cmd(macho_segment_command::command);
+ 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[macho_segment_command::size];
+ unsigned int sectionsEmitted = 0;
+ for (int j=0; j < sectionCount; ++j) {
+ Writer::SectionInfo* sectInfo = segInfo->fSections[j];
+ if ( fWriter.fEmitVirtualSections || !sectInfo->fVirtualSection ) {
+ macho_section* sect = §ions[sectionsEmitted++];
+ if ( oneSegment ) {
+ // .o files have weird segment range
+ if ( sectionsEmitted == 1 ) {
+ cmd->set_vmaddr(sectInfo->getBaseAddress());
+ cmd->set_fileoff(sectInfo->fFileOffset);
+ cmd->set_filesize(segInfo->fFileSize-sectInfo->fFileOffset);
+ }
+ 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 * macho_relocation_info::size + fWriter.fLocalRelocationsAtom->getFileOffset());
+ sect->set_nreloc(sectInfo->fRelocCount);
+ }
+ if ( sectInfo->fAllZeroFill ) {
+ sect->set_flags(S_ZEROFILL);
+ }
+ 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 ( (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);
+ }
+ }
+ }
+ p = &p[macho_segment_command::size + sectionsEmitted*macho_section::content_size];
+ cmd->set_cmdsize(macho_segment_command::size + sectionsEmitted*macho_section::content_size);
+ cmd->set_nsects(sectionsEmitted);
+ }
+ writer.write(0, buffer, size);
+}
+
+
+SymbolTableLoadCommandsAtom::SymbolTableLoadCommandsAtom(Writer& writer)
+ : WriterAtom(writer, fgTextSegment)
+{
+ bzero(&fSymbolTable, macho_symtab_command::size);
+ bzero(&fDynamicSymbolTable, macho_dysymtab_command::size);
+ writer.fSymbolTableCommands = this;
+}
+
+uint64_t SymbolTableLoadCommandsAtom::getSize() const
+{
+ return macho_symtab_command::size + macho_dysymtab_command::size;
+}
+
+void SymbolTableLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
+{
+ // build LC_DYSYMTAB command
+ macho_symtab_command symbolTableCmd;
+ bzero(&symbolTableCmd, macho_symtab_command::size);
+ symbolTableCmd.set_cmd(LC_SYMTAB);
+ symbolTableCmd.set_cmdsize(macho_symtab_command::size);
+ symbolTableCmd.set_nsyms(fWriter.fSymbolTableCount);
+ symbolTableCmd.set_symoff(fWriter.fSymbolTableAtom->getFileOffset());
+ symbolTableCmd.set_stroff(fWriter.fStringsAtom->getFileOffset());
+ symbolTableCmd.set_strsize(fWriter.fStringsAtom->getSize());
+ writer.write(0, &symbolTableCmd, macho_symtab_command::size);
+
+ // build LC_DYSYMTAB command
+ macho_dysymtab_command dynamicSymbolTableCmd;
+ bzero(&dynamicSymbolTableCmd, macho_dysymtab_command::size);
+ dynamicSymbolTableCmd.set_cmd(LC_DYSYMTAB);
+ dynamicSymbolTableCmd.set_cmdsize(macho_dysymtab_command::size);
+ 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.fIndirectSymbolTable.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());
+ }
+ writer.write(macho_symtab_command::size, &dynamicSymbolTableCmd, macho_dysymtab_command::size);
+}
+
+uint64_t DyldLoadCommandsAtom::getSize() const
+{
+ uint32_t len = macho_dylinker_command::name_offset + strlen("/usr/lib/dyld");
+ len = (len+7) & (-8); // 8-byte align
+ return len;
+}
+
+void DyldLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
+{
+ uint64_t size = this->getSize();
+ uint8_t 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[macho_dylinker_command::name_offset], "/usr/lib/dyld");
+ writer.write(0, buffer, size);
+}
+
+
+
+uint64_t DylibLoadCommandsAtom::getSize() const
+{
+ const char* path = fInfo.reader->getInstallPath();
+ if ( fInfo.options.fInstallPathOverride != NULL )
+ path = fInfo.options.fInstallPathOverride;
+ uint32_t len = macho_dylib_command::name_offset + strlen(path);
+ len = (len+7) & (-8); // 8-byte align
+ return len;
+}
+
+void DylibLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
+{
+ uint64_t size = this->getSize();
+ uint8_t buffer[size];
+ 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[macho_dylib_command::name_offset], path);
+ writer.write(0, buffer, size);
+}
+
+
+
+uint64_t DylibIDLoadCommandsAtom::getSize() const
+{
+ uint32_t len = macho_dylib_command::name_offset + strlen(fWriter.fOptions.installPath());
+ len = (len+7) & (-8); // 8-byte align
+ return len;
+}
+
+void DylibIDLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
+{
+ struct timeval currentTime = { 0 , 0 };
+ gettimeofday(¤tTime, NULL);
+ time_t timestamp = currentTime.tv_sec;
+ uint64_t size = this->getSize();
+ uint8_t buffer[size];
+ 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[macho_dylib_command::name_offset], fWriter.fOptions.installPath());
+ writer.write(0, buffer, size);
+}
+
+
+uint64_t RoutinesLoadCommandsAtom::getSize() const
+{
+ return macho_routines_command::size;
+}
+
+void RoutinesLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
+{
+ uint64_t initAddr = fWriter.getAtomLoadAddress(fWriter.fEntryPoint);
+ uint8_t buffer[macho_routines_command::size];
+ bzero(buffer, macho_routines_command::size);
+ macho_routines_command* cmd = (macho_routines_command*)buffer;
+ cmd->set_cmd(macho_routines_command::command);
+ cmd->set_cmdsize(this->getSize());
+ cmd->set_init_address(initAddr);
+ writer.write(0, buffer, macho_routines_command::size);
+}
+
+
+uint64_t SubUmbrellaLoadCommandsAtom::getSize() const
+{
+ uint32_t len = macho_sub_umbrella_command::name_offset + strlen(fName);
+ len = (len+7) & (-8); // 8-byte align
+ return len;
+}
+
+void SubUmbrellaLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
+{
+ uint64_t size = this->getSize();
+ uint8_t buffer[size];
+ 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_name_offset();
+ strcpy((char*)&buffer[macho_sub_umbrella_command::name_offset], fName);
+ writer.write(0, buffer, size);
+}
+
+
+uint64_t SubLibraryLoadCommandsAtom::getSize() const
+{
+ uint32_t len = macho_sub_library_command::name_offset + fNameLength + 1;
+ len = (len+7) & (-8); // 8-byte align
+ return len;
+}
+
+void SubLibraryLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
+{
+ uint64_t size = this->getSize();
+ uint8_t buffer[size];
+ 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_name_offset();
+ strncpy((char*)&buffer[macho_sub_library_command::name_offset], fNameStart, fNameLength);
+ buffer[macho_sub_library_command::name_offset+fNameLength] = '\0';
+ writer.write(0, buffer, size);
+}
+
+uint64_t UmbrellaLoadCommandsAtom::getSize() const
+{
+ uint32_t len = macho_sub_framework_command::name_offset + strlen(fName);
+ len = (len+7) & (-8); // 8-byte align
+ return len;
+}
+
+void UmbrellaLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
+{
+ uint64_t size = this->getSize();
+ uint8_t buffer[size];
+ 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_name_offset();
+ strcpy((char*)&buffer[macho_sub_framework_command::name_offset], fName);
+ writer.write(0, buffer, size);
+}
+
+uint64_t ThreadsLoadCommandsAtom::getSize() const
+{
+#if defined(ARCH_PPC)
+ uint32_t stateSize = 40; // PPC_THREAD_STATE_COUNT;
+#elif defined(ARCH_PPC64)
+ uint32_t stateSize = 76; // PPC_THREAD_STATE64_COUNT;
+#elif defined(ARCH_I386)
+ uint32_t stateSize = 16; // i386_THREAD_STATE_COUNT;
+#else
+ #error unknown architecture
+#endif
+ return macho_thread_command::size + stateSize*4;
+}
+
+void ThreadsLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
+{
+ uint64_t size = this->getSize();
+ uint8_t buffer[size];
+ 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);
+#if defined(ARCH_PPC)
+ cmd->set_flavor(1); // PPC_THREAD_STATE
+ cmd->set_count(40); // PPC_THREAD_STATE_COUNT;
+ cmd->set_threadState32(0, start);
+ if ( fWriter.fOptions.hasCustomStack() )
+ cmd->set_threadState32(3, fWriter.fOptions.customStackAddr()); // r1
+#elif defined(ARCH_PPC64)
+ cmd->set_flavor(5); // PPC_THREAD_STATE64
+ cmd->set_count(76); // PPC_THREAD_STATE64_COUNT;
+ cmd->set_threadState64(0, start);
+ if ( fWriter.fOptions.hasCustomStack() )
+ cmd->set_threadState64(6, fWriter.fOptions.customStackAddr()); // r1
+#elif defined(ARCH_I386)
+ cmd->set_flavor(0xFFFFFFFF); // i386_THREAD_STATE
+ cmd->set_count(16); // i386_THREAD_STATE_COUNT;
+ cmd->set_threadState32(0, start);
+ if ( fWriter.fOptions.hasCustomStack() )
+ cmd->set_threadState32(15, fWriter.fOptions.customStackAddr()); // uesp
+#else
+ #error unknown architecture
+#endif
+ writer.write(0, buffer, size);
+}
+
+
+
+uint64_t LoadCommandsPaddingAtom::getSize() const
+{
+ return fSize;
+}
+
+void LoadCommandsPaddingAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
+{
+ uint8_t buffer[fSize];
+ bzero(buffer, fSize);
+ writer.write(0, buffer, fSize);
+}
+
+
+uint64_t LinkEditAtom::getFileOffset() const
+{
+ return ((Writer::SectionInfo*)this->getSection())->fFileOffset + this->getSectionOffset();
+}
+
+
+uint64_t LocalRelocationsLinkEditAtom::getSize() const
+{
+ return fWriter.fInternalRelocs.size() * macho_relocation_info::size;
+}
+
+void LocalRelocationsLinkEditAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
+{
+ writer.write(0, &fWriter.fInternalRelocs[0], this->getSize());
+}
+
+
+
+uint64_t SymbolTableLinkEditAtom::getSize() const
+{
+ return fWriter.fSymbolTableCount * macho_nlist::size;
+}
+
+void SymbolTableLinkEditAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
+{
+ writer.write(0, fWriter.fSymbolTable, this->getSize());
+}
+
+uint64_t ExternalRelocationsLinkEditAtom::getSize() const
+{
+ return fWriter.fExternalRelocs.size() * macho_relocation_info::size;
+}
+
+void ExternalRelocationsLinkEditAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
+{
+ writer.write(0, &fWriter.fExternalRelocs[0], this->getSize());
+}
+
+
+
+uint64_t IndirectTableLinkEditAtom::getSize() const
+{
+ return fWriter.fIndirectSymbolTable.size() * sizeof(uint32_t);
+}
+
+void IndirectTableLinkEditAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
+{
+ uint64_t size = this->getSize();
+ uint8_t buffer[size];
+ bzero(buffer, size);
+ const uint32_t indirectTableSize = fWriter.fIndirectSymbolTable.size();
+ uint32_t* indirectTable = (uint32_t*)buffer;
+ for (uint32_t i=0; i < indirectTableSize; ++i) {
+ Writer::IndirectEntry& entry = fWriter.fIndirectSymbolTable[i];
+ if ( entry.indirectIndex < indirectTableSize ) {
+ ENDIAN_WRITE32(indirectTable[entry.indirectIndex], entry.symbolIndex);
+ }
+ else {
+ throw "malformed indirect table";
+ }
+ }
+ writer.write(0, buffer, size);
+}
+
+
+
+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';
+}
+
+uint64_t StringsLinkEditAtom::getSize() const
+{
+ return kBufferSize * fFullBuffers.size() + fCurrentBufferUsed;
+}
+
+void StringsLinkEditAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
+{
+ uint64_t offset = 0;
+ for (unsigned int i=0; i < fFullBuffers.size(); ++i) {
+ writer.write(offset, fFullBuffers[i], kBufferSize);
+ offset += kBufferSize;
+ }
+ writer.write(offset, fCurrentBuffer, fCurrentBufferUsed);
+}
+
+int32_t StringsLinkEditAtom::add(const char* name)
+{
+ int lenNeeded = strlen(name)+1;
+ while ( lenNeeded + fCurrentBufferUsed >= kBufferSize ) {
+ // first part of string fits in current buffer
+ int firstLen = kBufferSize - fCurrentBufferUsed;
+ memcpy(&fCurrentBuffer[fCurrentBufferUsed], name, firstLen);
+ // alloc next buffer
+ fFullBuffers.push_back(fCurrentBuffer);
+ fCurrentBuffer = new char[kBufferSize];
+ fCurrentBufferUsed = 0;
+ // advance name to second part
+ name += firstLen;
+ lenNeeded -= firstLen;
+ }
+ //fprintf(stderr, "StringsLinkEditAtom::add(): lenNeeded=%d, fCurrentBuffer=%d, fCurrentBufferUsed=%d\n", lenNeeded, fCurrentBuffer, fCurrentBufferUsed);
+ // string all fits in current buffer
+ strcpy(&fCurrentBuffer[fCurrentBufferUsed], name);
+ int32_t offset = kBufferSize * fFullBuffers.size() + fCurrentBufferUsed;
+ fCurrentBufferUsed += lenNeeded;
+ return offset;
+}
+
+// returns the index of an empty string
+int32_t StringsLinkEditAtom::emptyString()
+{
+ return 1;
+}
+
+
+
+};
+
+
+