/* * 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& dynamicLibraries); virtual ~Writer(); virtual const char* getPath(); virtual std::vector& getAtoms(); virtual std::vector* getJustInTimeAtomsFor(const char* name); virtual std::vector* getStabsDebugInfo(); virtual class ObjectFile::Atom* getUndefinedProxyAtom(const char* name); virtual void write(std::vector& 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& 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 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 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* 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* fAllAtoms; class SectionInfo* fLoadCommandsSection; class SegmentInfo* fLoadCommandsSegment; class SegmentLoadCommandsAtom* fSegmentCommands; class SymbolTableLoadCommandsAtom* fSymbolTableCommands; class LoadCommandsPaddingAtom* fHeaderPadding; std::vector fWriterSynthesizedAtoms; std::vector fSegmentInfos; class ObjectFile::Atom* fEntryPoint; std::vector fDirectLibraries; std::map fLibraryToOrdinal; std::vector fStabChunks; std::vector fExportedAtoms; std::vector fImportedAtoms; std::vector fLocalSymbolAtoms; LocalRelocationsLinkEditAtom* fLocalRelocationsAtom; ExternalRelocationsLinkEditAtom* fExternalRelocationsAtom; SymbolTableLinkEditAtom* fSymbolTableAtom; IndirectTableLinkEditAtom* fIndirectTableAtom; StringsLinkEditAtom* fStringsAtom; macho_nlist* fSymbolTable; //char* fStringPool; //uint32_t fStringPoolUsed; //uint32_t fStringPoolSize; std::vector fInternalRelocs; std::vector fExternalRelocs; std::vector 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& 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* 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 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 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 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& 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& 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::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::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& Writer::getAtoms() { return fWriterSynthesizedAtoms; } std::vector* Writer::getJustInTimeAtomsFor(const char* name) { return NULL; } std::vector* 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::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& 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 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& 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 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* 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* 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& segmentInfos = fSegmentInfos; const int segCount = segmentInfos.size(); for(int i=0; i < segCount; ++i) { SegmentInfo* curSegment = segmentInfos[i]; std::vector& sectionInfos = curSegment->fSections; const int sectionCount = sectionInfos.size(); for(int j=0; j < sectionCount; ++j) { SectionInfo* curSection = sectionInfos[j]; std::vector& 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& 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& 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& segmentInfos = fSegmentInfos; const int segCount = segmentInfos.size(); for(int i=0; i < segCount; ++i) { SegmentInfo* curSegment = segmentInfos[i]; std::vector& sectionInfos = curSegment->fSections; const int sectionCount = sectionInfos.size(); for(int j=0; j < sectionCount; ++j) { SectionInfo* curSection = sectionInfos[j]; std::vector& 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& 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& 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& sectionInfos = curSegment->fSections; const int sectionCount = sectionInfos.size(); for(int j=0; j < sectionCount; ++j) { SectionInfo* curSection = sectionInfos[j]; std::vector& sectionAtoms = curSection->fAtoms; //printf("writing %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& alignmentOverrides = fOptions.sectionAlignments(); for(std::vector::iterator it=alignmentOverrides.begin(); it != alignmentOverrides.end(); ++it) { if ( (strcmp(it->segmentName, currentSectionInfo->fSegmentName) == 0) && (strcmp(it->sectionName, currentSectionInfo->fSectionName) == 0) ) currentSectionInfo->fAlignment = it->alignment; } currentSectionInfo->fAllZeroFill = atom->isZeroFill(); currentSectionInfo->fVirtualSection = ( currentSectionInfo->fSectionName[0] == '.'); if ( !currentSectionInfo->fVirtualSection || fEmitVirtualSections ) currentSectionInfo->setIndex(sectionIndex++); currentSegmentInfo->fSections.push_back(currentSectionInfo); } if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "._load_commands") == 0) ) { fLoadCommandsSection = currentSectionInfo; fLoadCommandsSegment = currentSegmentInfo; } if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__la_symbol_ptr") == 0) ) currentSectionInfo->fAllLazyPointers = true; if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__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& 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& 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& 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& 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& 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& 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& segmentInfos = fWriter.fSegmentInfos; const int segCount = segmentInfos.size(); for(int i=0; i < segCount; ++i) { size += macho_segment_command::size; std::vector& sectionInfos = segmentInfos[i]->fSections; const int sectionCount = sectionInfos.size(); for(int j=0; j < sectionCount; ++j) { if ( fWriter.fEmitVirtualSections || ! sectionInfos[j]->fVirtualSection ) size += 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& 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; } };