From: Apple Date: Thu, 1 Jun 2006 20:14:06 +0000 (+0000) Subject: ld64-47.2.tar.gz X-Git-Tag: developer-tools-23^0 X-Git-Url: https://git.saurik.com/apple/ld64.git/commitdiff_plain/d696c285d331ab577dcabd00419d8c30336673da?hp=c7f24d34adf9643c842ab6c44f8e530661cffcc7 ld64-47.2.tar.gz --- diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..d028e47 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,691 @@ + +----- Tagged ld64-47 + + +----- Tagged ld64-46 + +2006-03-10 Nick Kledzik + + ld64 should figure out architecture from .o files + * unit-tests/test-cases/auto-arch: added + * src/ld.cpp: added Linker::inferArchitecture() to scan .o files are infer architecture to link + * src/MachOReaderArchive.hpp: enhanced validFile() to look deeper into archive and really valdate + * src/MachOWriterExecutable.hpp: stop using fOptions.architecture() + * src/Options.cpp: stop defaulting to ppc64 + + +2006-03-09 Nick Kledzik + + Need "intentionally left blank" dylib stubs + * unit-tests/include/common.makefile: add VALID_ARCHS + * unit-tests/run-all-unit-tests: set up VALID_ARCHS + * unit-tests/test-cases/blank-stubs: add test case + * src/ld.cpp: in addDylib(), detect and ignore blank stubs + * src/MachOReaderDylib.hpp: in constructor, handle blank stubs + +2006-03-09 Nick Kledzik + + crash in stub with 2GB pagezero + * src/MachOWriterExecutable.hpp: StubAtom can't be no-pic if a large zero-page is used + +2006-03-06 Nick Kledzik + + * src/Options.cpp: addSectionAlignment, warn if -sectalign alignment is not a power of two + +----- Tagged ld64-45 + + +2006-03-06 Nick Kledzik + + LP64/9A122: ld64: hang when trying to link DiscRecording framework + * src/Options.cpp: addSectionAlignment, warn on zero. Use log2() for alignment conversion + + +----- Tagged ld64-44 + +2006-03-04 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: fix again test for detection of anonymous non-lazy-pointer. + Error out if .o file contains old __DWARFA style dwarf. + +2006-03-02 Nick Kledzik + + * src/ld.cpp: only re-map page aligned sub-parts of a fat file. A conformat mmap() requires alignment. + +----- Tagged ld64-43 + +2006-03-02 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: tighten detection of anonymous non-lazy-pointer + +----- Tagged ld64-42 + +2006-02-28 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: fix x86 __IMPORT permissions for class Segment + +2006-02-28 Nick Kledzik + + SWB: ld64-37 (can't resolve symbol ___dso_handle) + * src/MachOWriterExecutable.hpp: add class DsoHandleAtom + +2006-02-28 Nick Kledzik + + * unit-tests/test-cases/literals-coalesce-alignment: added test case + * src/ld.cpp: when coalescing strings pick one with greater alignment + ld64: CG link failed because lo14 reference to anonymous non-lazy-pointer not aligned + * unit-tests/test-cases/relocs-c/test.c: tweak to fail like 4458660 + * src/MachOReaderRelocatable.hpp: detect anonymous non-lazy-pointer and transform into real non-lazy-pointers + +----- Tagged ld64-41 + +2006-02-24 Nick Kledzik + + * src/Options.cpp: Warning about -no_dead_strip_inits_and_terms and -i options. + Fix -weak-l option. + +----- Tagged ld64-40 + +2006-02-24 Nick Kledzik + + Leopard9A113: ppc64 libstdc++.dylib initializer crashes in pthread_once + * unit-tests/test-cases/multiple-entry-points: added + * src/MachOReaderRelocatable.hpp: make sure that if there are multiple symbols with the same + address, that we properly make zero length atoms for all but last symbol + +2006-02-24 Nick Kledzik + + * src/Options.cpp: ld64 doesn't realpath(3) B&I tracing paths + +2006-02-24 Nick Kledzik + + * src/Options.cpp: 9A110: ld64 can't deal with section names >16 chars + +2006-02-23 Nick Kledzik + + * src/MachOWriterExecutable.hpp: use vector.reserve() to minimize re-allocations + * src/Options.cpp: use vector.reserve() to minimize re-allocations + * src/MachOReaderRelocatable.hpp: use vector.reserve() to minimize re-allocations + * src/MachOReaderDylib.hpp: use vector.reserve() to minimize re-allocations + * src/ld.cpp: use vector.reserve() to minimize re-allocations + +2006-02-23 Nick Kledzik + + ld64 creates corrupt executables (and has malloc errors) with -headerpad option + * src/MachOWriterExecutable.hpp: Change LoadCommandsPaddingAtom::setSize() to update fLargestAtomSize + * unit-tests/test-cases/header-pad: added + +2006-02-23 Nick Kledzik + + ld64 creates invalid static executables + * src/MachOWriterExecutable.hpp: Change MachHeaderAtom::copyRawContent() to create correct header + for static executables. Change SymbolTableLoadCommandsAtom to skip LC_DYSYMTAB for static executables + * src/machochecker.cpp: Add tests that static executables are well formed + * unit-tests/test-cases/static-executable: added + +2006-02-22 Nick Kledzik + + * src/Options.cpp: chnage printf on unknown arg to a throw + +----- Tagged ld64-39 + +2006-02-20 Nick Kledzik + + * unit-tests/test-cases/read-only-relocs: added new test case + * src/MachOWriterExecutable.hpp: detect and error on relocs in read-only sections + * src/MachOReaderRelocatable.hpp: fix parsing of i386 absolute addressing relocs + +2006-02-20 Nick Kledzik + + * unit-tests/test-cases/stabs-coalesce: added new test case + * src/ld.cpp.hpp: in collectStabs removed unused stabs + +----- Tagged ld64-38 + +2006-02-17 Nick Kledzik + + * src/MachOWriterExecutable.hpp: set correct n_sect field of stabs + +2006-02-15 Nick Kledzik + + * src/MachOReaderArchive.hpp: with -all_load skip over both kinds of SYMDEFs + * unit-tests/test-cases/archive-basic/Makefile: add -all_load test case + +----- Tagged ld64-37 + +2006-02-13 Eric Christopher + + * src/MachOWriterExecutable.hpp (assignFileOffsets): Simplify. Add comments. + Adjust whitespace. + +2006-02-13 Nick Kledzik + + * src/MachOWriterExecutable.hpp: in Writer::fixUpReferenceRelocatable() fix kPCRel32 for external case + +2006-02-13 Nick Kledzik + + * unit-tests/test-cases/zero-fill: added + * src/machochecker.cpp: check that S_ZEROFILL have no file offset + * src/MachOWriterExecutable.hpp: rework assignFileOffsets() to fix rdar://problem/4441145 + +2006-02-12 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: fix use of first zero-length c-string in .o file + +2006-02-12 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: fix uninitialized fAlignment + +2006-02-12 Nick Kledzik + + * unit-tests/test-cases/relocs-asm/relocs-asm.s: add pointer-diff cases + * src/Architectures.hpp: make size explicit in ppc/ppc64 kPointerDiff + * src/MachOReaderRelocatable.hpp: don't allow kPointerDiff64 for ppc (just ppc64) + * src/MachOWriterExecutable.cpp: set proper r_length for ld -r of kPointerDiff + +----- Tagged ld64-36 + +2006-02-08 Nick Kledzik + + * src/MachOReaderRelocatable.cpp: rdar://problem/4438677 Handle when a .o file dwarf line info entries but no functions + +2006-02-08 Nick Kledzik + + * src/MachOWriterExecutable.cpp: Properly set address of first TEXT section + Keep S_COALESCED attribute for __eh_frame + +2006-02-08 Nick Kledzik + + * src/ld.cpp: Temporarily turn allowable client errors into warnings + * unit-tests/test-cases/allowable-clientMakefile: Temporarily let warnings be ok for above + * src/MachOWriterExecutable.hpp: fix ld -r to not use external relocations for symbols make static + +2006-02-08 Nick Kledzik + + * src/ld.cpp: A sibling in an umbrella can always link with its other siblings + * unit-tests/test-cases/allowable-client: add test case for above + +2006-02-08 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: support LOCAL non-lazy pointers to hidden symbols + * src/machochecker.cpp: verify indirect symbol table + * unit-tests/test-cases/private-non-lazy: added test case + +2006-02-07 Nick Kledzik + + * src/MachOWriterExecutable.hpp: fix calculation of file offsets in ld -r mode + * src/machochecker.cpp: verify segment file offsets are within file + +----- Tagged ld64-35 + +2006-02-06 Nick Kledzik + + * ld.cpp: allow parent of sub-framework to link + * unit-tests/test-cases/allowable-client/Makefile: added cases for parent and clients of parent + +2006-02-04 Nick Kledzik + + * unit-tests/test-cases/relocs-c/test.c: added some array cases + * src/MachOReaderRelocatable.hpp: factor out makeReferenceToEH() + * src/MachOWriterExecutable.hpp: add initial support for non-lazy pointer synthesis + +----- Tagged ld64-34 + +2006-02-04 Nick Kledzik + + * src/ld.cpp: fix -no_arch_warnings + fix -undefined warning + Do BINCL/EINCL optimization for gfull stabs + Implement "essential symbols" for stabs (-Sp) + Fix allowable clients to only test on direct libraries + * src/MachOReaderRelocatable.hpp: support BINCL/EINCL stabs + +2006-02-03 Nick Kledzik + + * src/machochecker.cpp: add code to check load command alignment + * src/MachOWriterExecutable.hpp: make load command alignment depend on architecture + +2006-02-03 Nick Kledzik + + * unit-tests/test-cases/literals-coalesce: added + * src/MachOReaderRelocatable.hpp: assure all targets of low14 ppc relocs are at least 4-byte alignmented + +----- Tagged ld64-33 + +2006-02-02 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: properly coalesce 8-byte literals + * src/MachOWriterExecutable.hpp: support ppc64::kPointerDiff32 + +----- Tagged ld64-32 + +2006-02-02 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: support anonymous zero fill atoms + +2006-02-02 Nick Kledzik + + * src/ld.cpp: A weak definition is good enough, do not search archives for a non-weak one + * unit-tests/test-cases/archive-weak: add test case for above + * src/MachOReaderRelocatable.hpp: an atom should never have a by-name reference to itself + * src/Options.cpp: prevent .eh symbols from being exported via a -exported_symbols_list + +2006-02-01 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: Support -macosx_version_min 10.5 + +2006-02-01 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: don't try to parse debug_line dwarf if no symboled atoms + +----- Tagged ld64-31 + +2006-02-01 Eric Christopher + + * unit-tests/test-cases/allow-stack-execute/Makefile: Move otool handling... + * unit-tests/include/common.makefile: ... here. + * unit-tests/bin/fail-if-stdin.pl: New. + * unit-tests/test-cases/no-uuid: Ditto. + * src/ld.cpp (Linker::) Add fCreateUUID. + (::Linker): Initialize. + (::collectStabs): Use. Set if dwarf or we have a UUID already. + (::writeOutput): Pass as argument to Writer::write along with option. + * src/Options.h (Option::emitUUID): Declare. + (Option::fEmitUUID): Ditto. + * src/Options.cpp (Option::emitUUID): New. + (parse): Handle -no_uuid. + * src/MachOReaderRelocatable (Reader::Reader): Handle LC_UUID. + * src/ExecutableFile.h (Writer::Write): Add createUUID boolean. + * src/MachOWriterExecutable: Add UUID forward declaration. + (fUUIDAtom): New. + (UUIDLoadCommandAtom): Emit LC_UUID if fEmit. New function emit. Size + to zero at start. + (Writer::writer): Add handle for LC_UUID. If createUUID emit LC_UUID. + (MachHeaderAtom::copyRawContent): Don't count a load command if its size is + 0. + (UUIDLoadCommandAtom::copyRawContent): Depend on fEmit. + + +2006-01-31 Nick Kledzik + + * unit-tests/test-cases/dwarf-debug-notes : Added + * src/ld.cpp: don't generate debug note for .eh symbols + * src/MachOReaderRelocatable.hpp: make dwarf line info to atom matching faster and better + +2006-01-31 Nick Kledzik + + * ld64.xcodeproj/project.pbxproj : Make buildable on Leopard + * src/MachOFileAbstraction.hpp: make buildable without latest cctools headers + +2006-01-31 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: better error message for bad relocs + * src/ObjectDump.cpp: add emacs tab settings + * src/SectCreate.h: ditto + * src/SectCreate.cpp: ditto + * src/machochecker.cpp: ditto + * src/ExecutableFile.h: ditto + +2006-01-30 Eric Christopher + + * src/ExecutableFile.h: Indent. + +2006-01-30 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: performance improvements + * src/ld.cpp: now that stubs are synthesized in write, don't need to special case anymore + +2006-01-30 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: fix parsing of pcc relocs + * unit-tests/test-cases/relocs-asm/relocs-asm.s: add test case for above + +2006-01-29 Nick Kledzik + + * unit-tests/test-cases/weak_import: added test case + * src/ld.cpp: move code for weak_import mismatch to writer + * src/ObjectFile.h: remove ImportWeakness methods + * src/MachOReaderDylib.hpp: ditto + * src/SectCreate.cpp: ditto + * src/Architectures.hpp: add new ReferenceKinds for weak_imports + * src/MachOReaderRelocatable.hpp: implement new ReferenceKinds + * src/MachOWriterExecutable.hpp: handle new ReferenceKinds and weak_import mismatches + +2006-01-29 Nick Kledzik + + * src/Options.cpp: verify -allow_stack_execute is only used on main executables + +2006-01-29 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: sync with latest dwarf reader from Geoff + * src/debugline.c: sync with latest dwarf reader from Geoff + +2006-01-27 Eric Christopher + + * src/ld.cpp (Linker::syntesizeStabs): Correct spelling. Update all uses. + +2006-01-27 Eric Christopher + + * src/Options.h (Options): Add hasExecutableStack, fExecutableStack. + * src/Options.cpp (Options::hasExecutableStack): New. + (Options::parse): Parse -allow_stack_execute. + * src/MachOWriterExecutable.hpp (MachHeaderAtom::copyRawContent): + Implement MH_ALLOW_STACK_EXECUTION. + * unit-tests/include/common.makefile (FAIL_IF_EMPTY): New. + * unit-tests/bin/fail-if-no-stdin.pl: New file. + * unit-tests/test-cases/allow-stack-execute: New directory. + +2006-01-27 Nick Kledzik + + * src/MachOFileAbstraction.hpp: rely on latest system headers + * src/MachOWriterExecutable.hpp: fix ppc stubs. + wrote new relocationNeededInFinalLinkedImage() to replace common code + +2006-01-27 Eric Christopher + + * src/ld.cpp (logTraceInfo): New. + (Linker::addArchive): Use. + (Linker::addDylib): Ditto. + * src/ObjectFile (ReaderOptions::fTraceOutputFile): New. + * src/MachOReaderArchive.hpp (Reader::Reader): Move trace + logging to Linker::addArchive. + * src/Options.cpp (parsePreCommandLineEnvironment): Check + LD_PRINT_FILE if tracing dylibs or archives. + +2006-01-26 Nick Kledzik + + * src/MachOWriterExecutable.hpp: handle NULL strings in SO debug notes + +2006-01-26 Nick Kledzik + + * src/MachOWriterExecutable.hpp: fix header padding calculation and thread state + +2006-01-26 Nick Kledzik + + Rewrite all stabs processing. + Move sythesize of debug notes into ld.cpp + +2006-01-26 Nick Kledzik + + * src/MachOWriterExecutable.hpp: fix ppc and ppc64 stub relocs + +2006-01-25 Nick Kledzik + + * ld64.xcodeproj/project.pbxproj: special case building in Curry + +2006-01-25 Nick Kledzik + + * src/MachOWriterExecutable.hpp: fix bugs in stub/lazy-pointer synthesis + +2006-01-24 Eric Christopher + + * src/ld.cpp (Linker::createReaders): Change logging title to XBS. + (Linker::addDylib): Ditto. + * src/MachOReaderArchive.hpp (Reader::Reader): Ditto. + * src/Options.h (fPrintOptions): New. + * src/Options.cpp (Options::Options): Initialize above. + (Options::checkForFile): Change logging title to XBS. + (Options::findFramework): Ditto. + (Options::parse): Add log for options. + (Options::parsePreCommandLineEnvironmentSettings): Add LD_TRACE_ARCHIVES, + LD_TRACE_DYLIBS, and LD_PRINT_OPTIONS. + +2006-01-24 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: better C++ eh parsing + +2006-01-23 Eric Christopher + + * unit-tests/bin/fail-if-exit-zero.pl: New. + * unit-tests/include/common.makefile (FAIL_IF_SUCCESS): Use. + * unit-tests/allowable-client: New test. + * src/ld.cpp (Linker::addDylib): Check allowable clients before adding dylib. + * src/Options.h (allowableClients): New. + (clientName): Ditto. + (fAllowableClients): Ditto. + (fClientName): Ditto. + * src/Options.cpp: Implement above. + (parse): Handle -allowable_client and -client_name. + * src/MachOReaderDylib.hpp (getAllowableClients): New. + (fAllowableClients): Ditto. + (Reader): Process LC_SUB_CLIENT load command. + * src/ObjectFile.h (parentUmbrella): New. + (getAllowableClients): New. + * src/MachOWriterExecutable.hpp (AllowableClientLoadCommandsAtom): New. + +2006-01-23 Nick Kledzik + + * unit-tests/test-cases/archive-basic: added + * src/ld.cpp: fix shadowed local variable + * src/FileAbstraction.hpp: ld64 shouldn't inline when building debug + +2006-01-23 Nick Kledzik + + * src/ld.cpp: fix symbol not found error message + * src/MachOReaderDylib.hpp: add logging to hash table + * src/MachOReaderRelocatable.hpp: enable stabs processing. Handle static functions with stubs + handle labeled cstrings. + * src/MachOWriterExecutable.hpp: properly suppress atoms not in symbol table. fix low14 error check. + add StubAtomHelper. + * unit-tests/test-cases/relocs-literals/test.c: add more interesting edge cases + +2006-01-17 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: tweaks to synthesizing debug notes + +2006-01-16 Nick Kledzik + + * src/debugline.{sh}: added + * src/MachOReaderRelocatable.hpp: synthesize debug notes SOL from dwarf + * src/MachOWriterExecutable.hpp: fix lazy pointer section + * src/ObjectDump.hpp: Fix conditionalization + * unit-tests/test-cases/dwarf-strip: added + +2006-01-11 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: support Tiger crt1.o build with old ld64 + * src/ObjectDump.hpp: Support -arch option + +2006-01-10 Nick Kledzik + + * src/MachOWriterExecutable.hpp: fix stubs for ppc64 + * src/MachOFileAbstraction.hpp: fix typo for macho_routines + * ld64.xcodeproj/project.pbxproj: add machochecker target + * src/machochecker.cpp: new skeleton for checking mach-o file bit + * unit-tests/: Add support for running machochecker + +2006-01-10 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: warn if dwarf can't be parsed + * src/MachOReaderArchive.hpp: modTime for OSO stabs from archives is .a modTime + +2006-01-09 Nick Kledzik + + * track modification time of .o files so that sythesized OSO stab will have it + +2006-01-09 Nick Kledzik + + * src/MachOFileAbstraction.hpp: add macho_uuid_command + * src/MachOWriterExecutable.cpp: add UUID load command to generated files + +2006-01-09 Nick Kledzik + + * src/MachOReaderDylib.hpp: no longer keep dylib memory mapped + * src/ld.cpp: don't track dylib sizes because they are not longer memory mapped + +2006-01-05 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: support new relocations + +2006-01-05 Nick Kledzik + + * src/MachOReaderDylib.hpp: support MH_DYLIB_STUB + * src/MachOReaderRelocatable.hpp: Add Geoff's comp unit extractor + +2006-01-05 Nick Kledzik + + refactor: transform Atom::dontStripName() to getSymbolTableInclusion() + * src/ld.cpp: pass dyld_stub_binding_helper to writer + * src/MachOReaderRelocatable.hpp: update synthesized stabs + Ignore stubs and lazy pointers in .o files + Support initializers and terminators + * src/MachOWriterExecutable.hpp: synthesize stubs and lazy pointers as needed + * ld64.xcodeproj/project.pbxproj: change Release target to build with dwarf + +2006-01-03 Eric Christopher + + * src/Options.h (multipleDefinitionsInDylibs): Declare. + (overridingDefinitionInDependentDylib): Ditto. + (warnOnMultipleDefinitionsInObjectFiles): Ditto. + (multiplyDefined): Remove. + (multiplyDefinedUnused): Ditto. + (fMultiplyDefined): Ditto. + (fWarnOnMultiplyDefined): New. + (fMultiplyDefinedDynamic): Ditto. + * src/Options.cpp (Options::Options): Initialize above. + (overridingDefinitionInDependentDylib): New. + (multipleDefinitionsInDylibs): Ditto. + (warnOnMultipleDefinitionsInObjectFiles): Ditto. + (parse): Update comments. Fix parsing of -y option. + Update error message for -dead_strip. Parse above + options. + +2006-01-02 Nick Kledzik + + * Refactor: move Atom::writeContent() to Writer + +2005-12-23 Nick Kledzik + + * Reworked, simplify, and document test harness + * unit-tests/README: Added + +2005-12-23 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: fixes for Objective-C + * unit-tests/test-cases/relocs-objc: Added + +2005-12-22 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: fix check that next reloc is pair + * src/MachOReaderRelocatable.hpp: Add code to synthesize essential stabs from dwarf + +2005-12-21 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: Fix parsing of literal sections + * src/MachOWriterExecutable.hpp: Fix writing of literal sections + * unit-tests/test-cases/relocs-literals: Added + +2005-12-15 Eric Christopher + + * src/Options.h (enum Treatment): New. + (enum PICTreatment): Delete. + (enum VersionMin): New. + (prebind): Declare. + (macosxVersionMin): Ditto. + (multiplyDefined): Ditto. + (multiplyDefinedUnused): Ditto. + (setVersionMin): Ditto. + (setPICTreatment): Delete. + (setReadOnlyRelocTreatment): Ditto. + (picTreatment): Adjust return type. + (parseTreatment): New. + (fPrebind): Ditto. + (fVersionMin): Ditto. + (fPICTreatment): Change type. + (fMultiplyDefined): New. + (fMultiplyDefinedUnused): Ditto. + (fLimitUndefinedSymbols): Ditto. + + * src/Options.cpp: Fix whitespace. Add comments on options. + (Options::Options): Add initializers for new variables. + (Options::prebind): New. + (Options::macosxVersionMin): Ditto. + (Options::parseTreatment): Ditto. + (Options::setVersionMin): Ditto. + (Options::setReadOnlyRelocTreatment): Delete. + (Options::setPICTreatment): Ditto. + (Options::Parse): Update for above. Add comments. + +2005-12-15 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: Add comments about dwarf + +2005-12-14 Nick Kledzik + + * src/ELFFileAbstraction.hpp: Added + * src/ELFReaderRelocatable.hpp: Added + * Lot of fixes for new architecture + +2005-12-13 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: check for S_ATTR_DEBUG and ignore those sections + * unit-tests/test-cases/dwarf-ignore: added + +2005-12-12 Nick Kledzik + + * Added test harness and three initial tests: + relocs-asm, relocs-c, and hello-world + +2005-12-12 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: Massive refactoring: + Now there are three Atom classes, Chopping into Atoms + is done on label boundaries or by knowledge of special + sections, Share lots of ppc/ppc64 code. + Stabs process code is temporarily disabled. + +2005-12-12 Nick Kledzik + + * src/ObjectDump.cpp: Add command line options: -no_content, -stabs, -no_sort + +2005-12-11 Eric Christopher + + * src/Options.cpp: Reformat. + * src/Options.h: Ditto. + +2005-12-07 Eric Christopher + + * src/MachOReaderRelocatable.hpp (Atom::getAlignment): + When calculating alignment of an Atom, take into account + the alignment from which we pulled the Atom. + +2005-12-06 Nick Kledzik + + * src/Options.cpp src/Options.h: Add design comments + +2005-12-05 Eric Christopher + + * src/ld.cpp (Linker::createWriter): Uncomment ppc64 and + i386 linkers. + +2005-12-05 Eric Christopher + + * ChangeLog: New file. + +2005-12-02 Nick Kledzik + + * src/ObjectFile.h: Add design comments + +2005-11-28 Nick Kledzik + + * Refactor Atom to use getDefinitionKind() + +2005-11-21 Nick Kledzik + + * src/MachOWriterExecutable.hpp: don't generate section for commons in -r mode + +2005-11-18 Nick Kledzik + + * x86 tweaks + +2005-11-18 Nick Kledzik + + * src/ObjectDump.cpp: make work with command line arguments + +2005-11-18 Nick Kledzik + + * Massive rework to remove preprocessor conditionals and use templates + +2005-11-14 Nick Kledzik + + * Created new Subversion repository for ld64 from cvs tag ld64-27.2 diff --git a/ld64.xcode/project.pbxproj b/ld64.xcode/project.pbxproj deleted file mode 100644 index 1831cbe..0000000 --- a/ld64.xcode/project.pbxproj +++ /dev/null @@ -1,441 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 39; - objects = { - F9023C2C06D5A227001BBF46 = { - children = ( - F971EED706D5AD240041D381, - F9023C3F06D5A254001BBF46, - F9C0D48A06DD1E1B001C7193, - F9C0D48B06DD1E1B001C7193, - F9023C3E06D5A254001BBF46, - F9023C4006D5A254001BBF46, - F9023C4106D5A254001BBF46, - F97288E607D277570031794D, - F972890007D27FD00031794D, - F9023C4206D5A254001BBF46, - F9023C4806D5A254001BBF46, - F97F5028070D0BB200B9FCD7, - F9023C3A06D5A23E001BBF46, - ); - isa = PBXGroup; - refType = 4; - sourceTree = ""; - }; - F9023C2E06D5A227001BBF46 = { - buildSettings = { - COPY_PHASE_STRIP = NO; - GCC_OPTIMIZATION_LEVEL = 0; - }; - isa = PBXBuildStyle; - name = Development; - }; - F9023C2F06D5A227001BBF46 = { - buildSettings = { - COPY_PHASE_STRIP = YES; - }; - isa = PBXBuildStyle; - name = Deployment; - }; - F9023C3006D5A227001BBF46 = { - buildSettings = { - }; - buildStyles = ( - F9023C2E06D5A227001BBF46, - F9023C2F06D5A227001BBF46, - ); - hasScannedForEncodings = 0; - isa = PBXProject; - mainGroup = F9023C2C06D5A227001BBF46; - productRefGroup = F9023C3A06D5A23E001BBF46; - projectDirPath = ""; - targets = ( - F9023C3806D5A23E001BBF46, - F971EED206D5ACF60041D381, - ); - }; - F9023C3606D5A23E001BBF46 = { - buildActionMask = 2147483647; - files = ( - F9023C4E06D5A272001BBF46, - F9C0D4BD06DD28D2001C7193, - F9023C4F06D5A272001BBF46, - F9023C5006D5A272001BBF46, - F97288E707D277570031794D, - ); - isa = PBXSourcesBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - F9023C3706D5A23E001BBF46 = { - buildActionMask = 2147483647; - files = ( - ); - isa = PBXFrameworksBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - F9023C3806D5A23E001BBF46 = { - buildPhases = ( - F9023C3606D5A23E001BBF46, - F9023C3706D5A23E001BBF46, - F97F5025070D0B6300B9FCD7, - ); - buildRules = ( - F9E8D4BE07FCAF2A00FD5801, - F9E8D4BD07FCAF2000FD5801, - ); - buildSettings = { - CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; - GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = 3; - INSTALL_PATH = /usr/bin; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PREBINDING = NO; - PRODUCT_NAME = ld64; - SECTORDER_FLAGS = ""; - VERSIONING_SYSTEM = "apple-generic"; - WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas"; - }; - dependencies = ( - ); - isa = PBXNativeTarget; - name = ld64; - productName = ld64; - productReference = F9023C3906D5A23E001BBF46; - productType = "com.apple.product-type.tool"; - }; - F9023C3906D5A23E001BBF46 = { - explicitFileType = "compiled.mach-o.executable"; - includeInIndex = 0; - isa = PBXFileReference; - path = ld64; - refType = 3; - sourceTree = BUILT_PRODUCTS_DIR; - }; - F9023C3A06D5A23E001BBF46 = { - children = ( - F9023C3906D5A23E001BBF46, - F971EED306D5ACF60041D381, - ); - isa = PBXGroup; - name = Products; - refType = 4; - sourceTree = ""; - }; - F9023C3E06D5A254001BBF46 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = ExecutableFile.h; - path = src/ExecutableFile.h; - refType = 4; - sourceTree = ""; - }; - F9023C3F06D5A254001BBF46 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = ld.cpp; - path = src/ld.cpp; - refType = 4; - sourceTree = ""; - }; - F9023C4006D5A254001BBF46 = { - fileEncoding = 30; - indentWidth = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = MachOAbstraction.h; - path = src/MachOAbstraction.h; - refType = 4; - sourceTree = ""; - tabWidth = 4; - usesTabs = 1; - }; - F9023C4106D5A254001BBF46 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = ObjectFile.h; - path = src/ObjectFile.h; - refType = 4; - sourceTree = ""; - }; - F9023C4206D5A254001BBF46 = { - children = ( - F9023C4706D5A254001BBF46, - F9023C4406D5A254001BBF46, - F95DD3B106EE395A007CAFEB, - F9023C4506D5A254001BBF46, - F9023C4606D5A254001BBF46, - ); - isa = PBXGroup; - name = Readers; - path = src/Readers; - refType = 4; - sourceTree = ""; - }; - F9023C4406D5A254001BBF46 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - path = ObjectFileDylibMachO.cpp; - refType = 4; - sourceTree = ""; - }; - F9023C4506D5A254001BBF46 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - path = "ObjectFileMachO-all.cpp"; - refType = 4; - sourceTree = ""; - }; - F9023C4606D5A254001BBF46 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - path = "ObjectFileMachO-all.h"; - refType = 4; - sourceTree = ""; - }; - F9023C4706D5A254001BBF46 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - path = ObjectFileMachO.cpp; - refType = 4; - sourceTree = ""; - }; - F9023C4806D5A254001BBF46 = { - children = ( - F9023C4906D5A254001BBF46, - F9023C4A06D5A254001BBF46, - F9023C4B06D5A254001BBF46, - ); - isa = PBXGroup; - name = Writers; - path = src/Writers; - refType = 4; - sourceTree = ""; - }; - F9023C4906D5A254001BBF46 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - path = "ExecutableFileMachO-all.cpp"; - refType = 4; - sourceTree = ""; - }; - F9023C4A06D5A254001BBF46 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - path = "ExecutableFileMachO-all.h"; - refType = 4; - sourceTree = ""; - }; - F9023C4B06D5A254001BBF46 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - path = ExecutableFileMachO.cpp; - refType = 4; - sourceTree = ""; - }; - F9023C4E06D5A272001BBF46 = { - fileRef = F9023C3F06D5A254001BBF46; - isa = PBXBuildFile; - settings = { - }; - }; - F9023C4F06D5A272001BBF46 = { - fileRef = F9023C4506D5A254001BBF46; - isa = PBXBuildFile; - settings = { - }; - }; - F9023C5006D5A272001BBF46 = { - fileRef = F9023C4906D5A254001BBF46; - isa = PBXBuildFile; - settings = { - }; - }; - F95DD3B106EE395A007CAFEB = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - path = ObjectFileArchiveMachO.cpp; - refType = 4; - sourceTree = ""; - }; - F971EED006D5ACF60041D381 = { - buildActionMask = 2147483647; - files = ( - F971EED806D5AD240041D381, - F971EEDA06D5AD450041D381, - ); - isa = PBXSourcesBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - F971EED106D5ACF60041D381 = { - buildActionMask = 2147483647; - files = ( - ); - isa = PBXFrameworksBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - F971EED206D5ACF60041D381 = { - buildPhases = ( - F971EED006D5ACF60041D381, - F971EED106D5ACF60041D381, - ); - buildRules = ( - ); - buildSettings = { - GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = 0; - INSTALL_PATH = "$(HOME)/bin"; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PREBINDING = NO; - PRODUCT_NAME = ObjectDump; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas"; - }; - dependencies = ( - ); - isa = PBXNativeTarget; - name = ObjectDump; - productName = ObjectDump; - productReference = F971EED306D5ACF60041D381; - productType = "com.apple.product-type.tool"; - }; - F971EED306D5ACF60041D381 = { - explicitFileType = "compiled.mach-o.executable"; - includeInIndex = 0; - isa = PBXFileReference; - path = ObjectDump; - refType = 3; - sourceTree = BUILT_PRODUCTS_DIR; - }; - F971EED706D5AD240041D381 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = ObjDump.cpp; - path = src/ObjDump.cpp; - refType = 4; - sourceTree = ""; - }; - F971EED806D5AD240041D381 = { - fileRef = F971EED706D5AD240041D381; - isa = PBXBuildFile; - settings = { - }; - }; - F971EEDA06D5AD450041D381 = { - fileRef = F9023C4506D5A254001BBF46; - isa = PBXBuildFile; - settings = { - }; - }; - F97288E607D277570031794D = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = SectCreate.cpp; - path = src/SectCreate.cpp; - refType = 4; - sourceTree = ""; - }; - F97288E707D277570031794D = { - fileRef = F97288E607D277570031794D; - isa = PBXBuildFile; - settings = { - }; - }; - F972890007D27FD00031794D = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = SectCreate.h; - path = src/SectCreate.h; - refType = 4; - sourceTree = ""; - }; - F97F5025070D0B6300B9FCD7 = { - buildActionMask = 8; - dstPath = /usr/share/man/man1; - dstSubfolderSpec = 0; - files = ( - F97F5029070D0BB200B9FCD7, - ); - isa = PBXCopyFilesBuildPhase; - runOnlyForDeploymentPostprocessing = 1; - }; - F97F5028070D0BB200B9FCD7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = text.man; - name = ld64.1; - path = doc/man/man1/ld64.1; - refType = 4; - sourceTree = ""; - }; - F97F5029070D0BB200B9FCD7 = { - fileRef = F97F5028070D0BB200B9FCD7; - isa = PBXBuildFile; - settings = { - }; - }; - F9C0D48A06DD1E1B001C7193 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = Options.cpp; - path = src/Options.cpp; - refType = 4; - sourceTree = ""; - }; - F9C0D48B06DD1E1B001C7193 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = Options.h; - path = src/Options.h; - refType = 4; - sourceTree = ""; - }; - F9C0D4BD06DD28D2001C7193 = { - fileRef = F9C0D48A06DD1E1B001C7193; - isa = PBXBuildFile; - settings = { - }; - }; - F9E8D4BD07FCAF2000FD5801 = { - compilerSpec = com.apple.compilers.gcc.4_0; - fileType = sourcecode.c; - isEditable = 1; - isa = PBXBuildRule; - outputFiles = ( - ); - }; - F9E8D4BE07FCAF2A00FD5801 = { - compilerSpec = com.apple.compilers.gcc.4_0; - fileType = sourcecode.cpp; - isEditable = 1; - isa = PBXBuildRule; - outputFiles = ( - ); - }; - }; - rootObject = F9023C3006D5A227001BBF46; -} diff --git a/ld64.xcodeproj/project.pbxproj b/ld64.xcodeproj/project.pbxproj new file mode 100644 index 0000000..1ce18dd --- /dev/null +++ b/ld64.xcodeproj/project.pbxproj @@ -0,0 +1,602 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 42; + objects = { + +/* Begin PBXAggregateTarget section */ + F96D5368094A2754008E9EE8 /* unit-tests */ = { + isa = PBXAggregateTarget; + buildConfigurationList = F96D536D094A2773008E9EE8 /* Build configuration list for PBXAggregateTarget "unit-tests" */; + buildPhases = ( + F96D5367094A2754008E9EE8 /* ShellScript */, + ); + buildSettings = { + PRODUCT_NAME = "unit-tests"; + }; + dependencies = ( + F96D536A094A275D008E9EE8 /* PBXTargetDependency */, + F96D536C094A275F008E9EE8 /* PBXTargetDependency */, + F9EA73970974999B008B4F1D /* PBXTargetDependency */, + ); + name = "unit-tests"; + productName = "unit-tests"; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + F9023C4E06D5A272001BBF46 /* ld.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9023C3F06D5A254001BBF46 /* ld.cpp */; }; + F933E3D9092E855B0083EAC8 /* ObjectDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F971EED706D5AD240041D381 /* ObjectDump.cpp */; }; + F97288E707D277570031794D /* SectCreate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F97288E607D277570031794D /* SectCreate.cpp */; }; + F97F5029070D0BB200B9FCD7 /* ld64.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = F97F5028070D0BB200B9FCD7 /* ld64.1 */; }; + F9C0D4BD06DD28D2001C7193 /* Options.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9C0D48A06DD1E1B001C7193 /* Options.cpp */; }; + F9EA72D5097454FF008B4F1D /* machochecker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9EA72D4097454FF008B4F1D /* machochecker.cpp */; }; + F9EA7584097882F3008B4F1D /* debugline.c in Sources */ = {isa = PBXBuildFile; fileRef = F9EA7582097882F3008B4F1D /* debugline.c */; }; + F9EA75BC09788857008B4F1D /* debugline.c in Sources */ = {isa = PBXBuildFile; fileRef = F9EA7582097882F3008B4F1D /* debugline.c */; }; +/* End PBXBuildFile section */ + +/* Begin PBXBuildRule section */ + F9E8D4BD07FCAF2000FD5801 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc.4_0; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + }; + F9E8D4BE07FCAF2A00FD5801 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc.4_0; + fileType = sourcecode.cpp; + isEditable = 1; + outputFiles = ( + ); + }; +/* End PBXBuildRule section */ + +/* Begin PBXBuildStyle section */ + F933D92F09291D070083EAC8 /* Development */ = { + isa = PBXBuildStyle; + buildSettings = { + COPY_PHASE_STRIP = NO; + }; + name = Development; + }; + F933D93009291D070083EAC8 /* Deployment */ = { + isa = PBXBuildStyle; + buildSettings = { + COPY_PHASE_STRIP = YES; + }; + name = Deployment; + }; +/* End PBXBuildStyle section */ + +/* Begin PBXContainerItemProxy section */ + F96D5369094A275D008E9EE8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9023C3006D5A227001BBF46 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9023C3806D5A23E001BBF46; + remoteInfo = ld; + }; + F96D536B094A275F008E9EE8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9023C3006D5A227001BBF46 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F971EED206D5ACF60041D381; + remoteInfo = ObjectDump; + }; + F9EA73960974999B008B4F1D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9023C3006D5A227001BBF46 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9EA72CA097454A6008B4F1D; + remoteInfo = machocheck; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + F97F5025070D0B6300B9FCD7 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man1; + dstSubfolderSpec = 0; + files = ( + F97F5029070D0BB200B9FCD7 /* ld64.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + C02A29DE0953B26E001FB8C1 /* ChangeLog */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = ChangeLog; sourceTree = ""; }; + F9023C3906D5A23E001BBF46 /* ld64 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ld64; sourceTree = BUILT_PRODUCTS_DIR; }; + F9023C3E06D5A254001BBF46 /* ExecutableFile.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ExecutableFile.h; path = src/ExecutableFile.h; sourceTree = ""; }; + F9023C3F06D5A254001BBF46 /* ld.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ld.cpp; path = src/ld.cpp; sourceTree = ""; }; + F9023C4106D5A254001BBF46 /* ObjectFile.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ObjectFile.h; path = src/ObjectFile.h; sourceTree = ""; }; + F933D9460929277C0083EAC8 /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = FileAbstraction.hpp; path = src/FileAbstraction.hpp; sourceTree = ""; }; + F933D9470929277C0083EAC8 /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOFileAbstraction.hpp; path = src/MachOFileAbstraction.hpp; sourceTree = ""; }; + F933DC37092A82480083EAC8 /* Architectures.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = Architectures.hpp; path = src/Architectures.hpp; sourceTree = ""; }; + F933E3CB092E84250083EAC8 /* MachOReaderArchive.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOReaderArchive.hpp; path = src/MachOReaderArchive.hpp; sourceTree = ""; }; + F933E3CC092E84250083EAC8 /* MachOReaderDylib.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOReaderDylib.hpp; path = src/MachOReaderDylib.hpp; sourceTree = ""; }; + F933E3CD092E84250083EAC8 /* MachOReaderRelocatable.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOReaderRelocatable.hpp; path = src/MachOReaderRelocatable.hpp; sourceTree = ""; }; + F933E3CE092E84250083EAC8 /* MachOWriterExecutable.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOWriterExecutable.hpp; path = src/MachOWriterExecutable.hpp; sourceTree = ""; }; + F971EED306D5ACF60041D381 /* ObjectDump */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ObjectDump; sourceTree = BUILT_PRODUCTS_DIR; }; + F971EED706D5AD240041D381 /* ObjectDump.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ObjectDump.cpp; path = src/ObjectDump.cpp; sourceTree = ""; }; + F97288E607D277570031794D /* SectCreate.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = SectCreate.cpp; path = src/SectCreate.cpp; sourceTree = ""; }; + F972890007D27FD00031794D /* SectCreate.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = SectCreate.h; path = src/SectCreate.h; sourceTree = ""; }; + F97F5028070D0BB200B9FCD7 /* ld64.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = ld64.1; path = doc/man/man1/ld64.1; sourceTree = ""; }; + F9C0D48A06DD1E1B001C7193 /* Options.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Options.cpp; path = src/Options.cpp; sourceTree = ""; }; + F9C0D48B06DD1E1B001C7193 /* Options.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Options.h; path = src/Options.h; sourceTree = ""; }; + F9EA72CB097454A6008B4F1D /* machocheck */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = machocheck; sourceTree = BUILT_PRODUCTS_DIR; }; + F9EA72D4097454FF008B4F1D /* machochecker.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = machochecker.cpp; path = src/machochecker.cpp; sourceTree = ""; }; + F9EA7582097882F3008B4F1D /* debugline.c */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 2; lastKnownFileType = sourcecode.c.c; name = debugline.c; path = src/debugline.c; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9EA7583097882F3008B4F1D /* debugline.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = debugline.h; path = src/debugline.h; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + F9023C3706D5A23E001BBF46 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F971EED106D5ACF60041D381 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9EA72C9097454A6008B4F1D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + F9023C2C06D5A227001BBF46 = { + isa = PBXGroup; + children = ( + C02A29DE0953B26E001FB8C1 /* ChangeLog */, + F933DC37092A82480083EAC8 /* Architectures.hpp */, + F933D9460929277C0083EAC8 /* FileAbstraction.hpp */, + F933D9470929277C0083EAC8 /* MachOFileAbstraction.hpp */, + F933E3CD092E84250083EAC8 /* MachOReaderRelocatable.hpp */, + F933E3CB092E84250083EAC8 /* MachOReaderArchive.hpp */, + F933E3CC092E84250083EAC8 /* MachOReaderDylib.hpp */, + F933E3CE092E84250083EAC8 /* MachOWriterExecutable.hpp */, + F9023C3E06D5A254001BBF46 /* ExecutableFile.h */, + F9023C4106D5A254001BBF46 /* ObjectFile.h */, + F9023C3F06D5A254001BBF46 /* ld.cpp */, + F9C0D48A06DD1E1B001C7193 /* Options.cpp */, + F9C0D48B06DD1E1B001C7193 /* Options.h */, + F97288E607D277570031794D /* SectCreate.cpp */, + F972890007D27FD00031794D /* SectCreate.h */, + F9EA7582097882F3008B4F1D /* debugline.c */, + F9EA7583097882F3008B4F1D /* debugline.h */, + F9EA72D4097454FF008B4F1D /* machochecker.cpp */, + F971EED706D5AD240041D381 /* ObjectDump.cpp */, + F97F5028070D0BB200B9FCD7 /* ld64.1 */, + F9023C3A06D5A23E001BBF46 /* Products */, + ); + sourceTree = ""; + }; + F9023C3A06D5A23E001BBF46 /* Products */ = { + isa = PBXGroup; + children = ( + F9023C3906D5A23E001BBF46 /* ld64 */, + F971EED306D5ACF60041D381 /* ObjectDump */, + F9EA72CB097454A6008B4F1D /* machocheck */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + F9023C3806D5A23E001BBF46 /* ld */ = { + isa = PBXNativeTarget; + buildConfigurationList = F933D91B09291AC90083EAC8 /* Build configuration list for PBXNativeTarget "ld" */; + buildPhases = ( + F9023C3606D5A23E001BBF46 /* Sources */, + F9023C3706D5A23E001BBF46 /* Frameworks */, + F97F5025070D0B6300B9FCD7 /* CopyFiles */, + ); + buildRules = ( + F9E8D4BE07FCAF2A00FD5801 /* PBXBuildRule */, + F9E8D4BD07FCAF2000FD5801 /* PBXBuildRule */, + ); + buildSettings = { + PRODUCT_NAME = ld64; + }; + dependencies = ( + ); + name = ld; + productName = ld64; + productReference = F9023C3906D5A23E001BBF46 /* ld64 */; + productType = "com.apple.product-type.tool"; + }; + F971EED206D5ACF60041D381 /* ObjectDump */ = { + isa = PBXNativeTarget; + buildConfigurationList = F933D91F09291AC90083EAC8 /* Build configuration list for PBXNativeTarget "ObjectDump" */; + buildPhases = ( + F971EED006D5ACF60041D381 /* Sources */, + F971EED106D5ACF60041D381 /* Frameworks */, + ); + buildRules = ( + ); + buildSettings = { + PRODUCT_NAME = ObjectDump; + }; + dependencies = ( + ); + name = ObjectDump; + productName = ObjectDump; + productReference = F971EED306D5ACF60041D381 /* ObjectDump */; + productType = "com.apple.product-type.tool"; + }; + F9EA72CA097454A6008B4F1D /* machocheck */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9EA72CF097454D5008B4F1D /* Build configuration list for PBXNativeTarget "machocheck" */; + buildPhases = ( + F9EA72C8097454A6008B4F1D /* Sources */, + F9EA72C9097454A6008B4F1D /* Frameworks */, + ); + buildRules = ( + ); + buildSettings = { + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = "$(HOME)/bin"; + PREBINDING = NO; + PRODUCT_NAME = machocheck; + }; + dependencies = ( + ); + name = machocheck; + productName = machocheck; + productReference = F9EA72CB097454A6008B4F1D /* machocheck */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + F9023C3006D5A227001BBF46 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = F933D92309291AC90083EAC8 /* Build configuration list for PBXProject "ld64" */; + buildSettings = { + }; + buildStyles = ( + F933D92F09291D070083EAC8 /* Development */, + F933D93009291D070083EAC8 /* Deployment */, + ); + hasScannedForEncodings = 0; + mainGroup = F9023C2C06D5A227001BBF46; + productRefGroup = F9023C3A06D5A23E001BBF46 /* Products */; + projectDirPath = ""; + targets = ( + F9023C3806D5A23E001BBF46 /* ld */, + F971EED206D5ACF60041D381 /* ObjectDump */, + F9EA72CA097454A6008B4F1D /* machocheck */, + F96D5368094A2754008E9EE8 /* unit-tests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXShellScriptBuildPhase section */ + F96D5367094A2754008E9EE8 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/csh; + shellScript = "# Let tests set MACOSX_DEPLOYMENT_TARGET as they need\nunsetenv MACOSX_DEPLOYMENT_TARGET\n\n# make an symlink to ld64 called ld, so that gcc will use this linker for all linking\nrm -rf $BUILT_PRODUCTS_DIR/ld\nln -s $BUILT_PRODUCTS_DIR/ld64 $BUILT_PRODUCTS_DIR/ld\n\n# run full test suite\n$SRCROOT/unit-tests/run-all-unit-tests\n\nexit 0"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + F9023C3606D5A23E001BBF46 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9023C4E06D5A272001BBF46 /* ld.cpp in Sources */, + F9C0D4BD06DD28D2001C7193 /* Options.cpp in Sources */, + F97288E707D277570031794D /* SectCreate.cpp in Sources */, + F9EA7584097882F3008B4F1D /* debugline.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F971EED006D5ACF60041D381 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F933E3D9092E855B0083EAC8 /* ObjectDump.cpp in Sources */, + F9EA75BC09788857008B4F1D /* debugline.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9EA72C8097454A6008B4F1D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9EA72D5097454FF008B4F1D /* machochecker.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + F96D536A094A275D008E9EE8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9023C3806D5A23E001BBF46 /* ld */; + targetProxy = F96D5369094A275D008E9EE8 /* PBXContainerItemProxy */; + }; + F96D536C094A275F008E9EE8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F971EED206D5ACF60041D381 /* ObjectDump */; + targetProxy = F96D536B094A275F008E9EE8 /* PBXContainerItemProxy */; + }; + F9EA73970974999B008B4F1D /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9EA72CA097454A6008B4F1D /* machocheck */; + targetProxy = F9EA73960974999B008B4F1D /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + F933D91C09291AC90083EAC8 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + GCC_DYNAMIC_NO_PIC = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; + GCC_WARN_ABOUT_MISSING_NEWLINE = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_POINTER_SIGNEDNESS = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; + GCC_WARN_EFFECTIVE_CPLUSPLUS_VIOLATIONS = NO; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; + GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; + GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; + GCC_WARN_MISSING_PARENTHESES = YES; + GCC_WARN_NON_VIRTUAL_DESTRUCTOR = NO; + GCC_WARN_PEDANTIC = NO; + GCC_WARN_SHADOW = NO; + GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + GCC_WARN_UNINITIALIZED_AUTOS = NO; + GCC_WARN_UNKNOWN_PRAGMAS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_PARAMETER = NO; + GCC_WARN_UNUSED_VALUE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INSTALL_PATH = /usr/bin; + OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)"; + OTHER_LDFLAGS = ""; + PREBINDING = NO; + PRODUCT_NAME = ld64; + SECTORDER_FLAGS = ""; + VERSIONING_SYSTEM = "apple-generic"; + WARNING_CFLAGS = "-Wall"; + }; + name = Debug; + }; + F933D91D09291AC90083EAC8 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + GCC_DYNAMIC_NO_PIC = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 3; + GCC_PREPROCESSOR_DEFINITIONS = "$(GCC_PREPROCESSOR_DEFINITIONS_$(RC_RELEASE))"; + GCC_PREPROCESSOR_DEFINITIONS_Curry = __OPEN_SOURCE__; + GCC_PREPROCESSOR_DEFINITIONS_CurryWeed = __OPEN_SOURCE__; + GCC_PREPROCESSOR_DEFINITIONS_Leopard = __OPEN_SOURCE__; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; + GCC_WARN_ABOUT_MISSING_NEWLINE = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_POINTER_SIGNEDNESS = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; + GCC_WARN_EFFECTIVE_CPLUSPLUS_VIOLATIONS = NO; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; + GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; + GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; + GCC_WARN_MISSING_PARENTHESES = YES; + GCC_WARN_NON_VIRTUAL_DESTRUCTOR = NO; + GCC_WARN_PEDANTIC = NO; + GCC_WARN_SHADOW = NO; + GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + GCC_WARN_UNINITIALIZED_AUTOS = NO; + GCC_WARN_UNKNOWN_PRAGMAS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_PARAMETER = NO; + GCC_WARN_UNUSED_VALUE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INSTALL_PATH = /usr/bin; + OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)"; + OTHER_LDFLAGS = ""; + PREBINDING = NO; + PRODUCT_NAME = ld64; + SECTORDER_FLAGS = ""; + VERSIONING_SYSTEM = "apple-generic"; + WARNING_CFLAGS = "-Wall"; + }; + name = Release; + }; + F933D92009291AC90083EAC8 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = "$(HOME)/bin"; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PREBINDING = NO; + PRODUCT_NAME = ObjectDump; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + }; + name = Debug; + }; + F933D92109291AC90083EAC8 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = "$(HOME)/bin"; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PREBINDING = NO; + PRODUCT_NAME = ObjectDump; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + }; + name = Release; + }; + F933D92409291AC90083EAC8 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_DYNAMIC_NO_PIC = NO; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + }; + name = Debug; + }; + F933D92509291AC90083EAC8 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_DYNAMIC_NO_PIC = NO; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + }; + name = Release; + }; + F96D536E094A2773008E9EE8 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + PRODUCT_NAME = "unit-tests"; + }; + name = Debug; + }; + F96D536F094A2773008E9EE8 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + PRODUCT_NAME = "unit-tests"; + }; + name = Release; + }; + F9EA72D0097454D5008B4F1D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = "$(HOME)/bin"; + PREBINDING = NO; + PRODUCT_NAME = machocheck; + }; + name = Debug; + }; + F9EA72D1097454D5008B4F1D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = "$(HOME)/bin"; + PREBINDING = NO; + PRODUCT_NAME = machocheck; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + F933D91B09291AC90083EAC8 /* Build configuration list for PBXNativeTarget "ld" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F933D91C09291AC90083EAC8 /* Debug */, + F933D91D09291AC90083EAC8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F933D91F09291AC90083EAC8 /* Build configuration list for PBXNativeTarget "ObjectDump" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F933D92009291AC90083EAC8 /* Debug */, + F933D92109291AC90083EAC8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F933D92309291AC90083EAC8 /* Build configuration list for PBXProject "ld64" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F933D92409291AC90083EAC8 /* Debug */, + F933D92509291AC90083EAC8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F96D536D094A2773008E9EE8 /* Build configuration list for PBXAggregateTarget "unit-tests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F96D536E094A2773008E9EE8 /* Debug */, + F96D536F094A2773008E9EE8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9EA72CF097454D5008B4F1D /* Build configuration list for PBXNativeTarget "machocheck" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9EA72D0097454D5008B4F1D /* Debug */, + F9EA72D1097454D5008B4F1D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = F9023C3006D5A227001BBF46 /* Project object */; +} diff --git a/src/Architectures.hpp b/src/Architectures.hpp new file mode 100644 index 0000000..71c10c4 --- /dev/null +++ b/src/Architectures.hpp @@ -0,0 +1,70 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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@ + */ + +#ifndef __ARCHITECTURES__ +#define __ARCHITECTURES__ + +#include "FileAbstraction.hpp" + + +// +// Architectures +// +struct ppc +{ + typedef Pointer32 P; + + enum ReferenceKinds { kNoFixUp, kFollowOn, kPointer, kPointerWeakImport, kPointerDiff32, kPointerDiff64, + kBranch24, kBranch24WeakImport, kBranch14, + kPICBaseLow16, kPICBaseLow14, kPICBaseHigh16, + kAbsLow16, kAbsLow14, kAbsHigh16, kAbsHigh16AddLow }; +}; + +struct ppc64 +{ + typedef Pointer64 P; + + enum ReferenceKinds { kNoFixUp, kFollowOn, kPointer, kPointerWeakImport, kPointerDiff32, kPointerDiff64, + kBranch24, kBranch24WeakImport, kBranch14, + kPICBaseLow16, kPICBaseLow14, kPICBaseHigh16, + kAbsLow16, kAbsLow14, kAbsHigh16, kAbsHigh16AddLow }; +}; + +struct x86 +{ + typedef Pointer32 P; + + enum ReferenceKinds { kNoFixUp, kFollowOn, kPointer, kPointerWeakImport, kPointerDiff, + kPCRel32, kPCRel32WeakImport }; +}; + + + + + + + +#endif // __ARCHITECTURES__ + + diff --git a/src/ExecutableFile.h b/src/ExecutableFile.h index e2e1e8c..576554c 100644 --- a/src/ExecutableFile.h +++ b/src/ExecutableFile.h @@ -1,15 +1,16 @@ -/* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ - * + * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. - * + * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -17,11 +18,10 @@ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. - * + * * @APPLE_LICENSE_HEADER_END@ */ - #ifndef __EXECUTABLEFILE__ #define __EXECUTABLEFILE__ @@ -34,39 +34,34 @@ namespace ExecutableFile { - struct DyLibUsed + struct DyLibUsed { ObjectFile::Reader* reader; DynamicLibraryOptions options; - bool indirect; // library found indirect. Do not make load command + bool indirect; // library found indirect. Do not make load command ObjectFile::Reader* directReader; // direct library which re-exports this library }; - + class Writer : public ObjectFile::Reader { - public: - virtual ~Writer() {}; - + public: + virtual ~Writer() {}; + virtual const char* getPath() = 0; virtual std::vector& getAtoms() = 0; virtual std::vector* getJustInTimeAtomsFor(const char* name) = 0; - virtual std::vector* getStabsDebugInfo() = 0; virtual class ObjectFile::Atom* getUndefinedProxyAtom(const char* name) = 0; - virtual void write(std::vector& atoms, class ObjectFile::Atom* entryPointAtom) = 0; - - + virtual uint64_t write(std::vector& atoms, + std::vector& stabs, + class ObjectFile::Atom* entryPointAtom, + class ObjectFile::Atom* dyldHelperAtom, + bool createUUID) = 0; protected: - Writer(std::vector&) {}; + Writer(std::vector&) {}; }; }; - - - #endif // __EXECUTABLEFILE__ - - - diff --git a/src/FileAbstraction.hpp b/src/FileAbstraction.hpp new file mode 100644 index 0000000..1f7a629 --- /dev/null +++ b/src/FileAbstraction.hpp @@ -0,0 +1,145 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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@ + */ +#ifndef __FILE_ABSTRACTION__ +#define __FILE_ABSTRACTION__ + + +#include +#include +#include + +#ifdef __OPTIMIZE__ +#define INLINE __attribute__((always_inline)) +#else +#define INLINE +#endif + +// +// This abstraction layer is for use with file formats that have 64-bit/32-bit and Big-Endian/Little-Endian variants +// +// For example: to make a utility that handles 32-bit little enidan files use: Pointer32 +// +// +// get16() read a 16-bit number from an E endian struct +// set16() write a 16-bit number to an E endian struct +// get32() read a 32-bit number from an E endian struct +// set32() write a 32-bit number to an E endian struct +// get64() read a 64-bit number from an E endian struct +// set64() write a 64-bit number to an E endian struct +// +// getBits() read a bit field from an E endian struct (bitCount=number of bits in field, firstBit=bit index of field) +// setBits() write a bit field to an E endian struct (bitCount=number of bits in field, firstBit=bit index of field) +// +// getBitsRaw() read a bit field from a struct with native endianness +// setBitsRaw() write a bit field from a struct with native endianness +// + +class BigEndian +{ +public: + static uint16_t get16(const uint16_t& from) INLINE { return OSReadBigInt16(&from, 0); } + static void set16(uint16_t& into, uint16_t value) INLINE { OSWriteBigInt16(&into, 0, value); } + + static uint32_t get32(const uint32_t& from) INLINE { return OSReadBigInt32(&from, 0); } + static void set32(uint32_t& into, uint32_t value) INLINE { OSWriteBigInt32(&into, 0, value); } + + static uint64_t get64(const uint64_t& from) INLINE { return OSReadBigInt64(&from, 0); } + static void set64(uint64_t& into, uint64_t value) INLINE { OSWriteBigInt64(&into, 0, value); } + + static uint32_t getBits(const uint32_t& from, + uint8_t firstBit, uint8_t bitCount) INLINE { return getBitsRaw(get32(from), firstBit, bitCount); } + static void setBits(uint32_t& into, uint32_t value, + uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = get32(into); setBitsRaw(temp, value, firstBit, bitCount); set32(into, temp); } + + static uint32_t getBitsRaw(const uint32_t& from, + uint8_t firstBit, uint8_t bitCount) INLINE { return ((from >> (32-firstBit-bitCount)) & ((1<> firstBit) & ((1< +class Pointer32 +{ +public: + typedef uint32_t uint_t; + typedef _E E; + + static uint64_t getP(const uint_t& from) INLINE { return _E::get32(from); } + static void setP(uint_t& into, uint64_t value) INLINE { _E::set32(into, value); } +}; + + +template +class Pointer64 +{ +public: + typedef uint64_t uint_t; + typedef _E E; + + static uint64_t getP(const uint_t& from) INLINE { return _E::get64(from); } + static void setP(uint_t& into, uint64_t value) INLINE { _E::set64(into, value); } +}; + + + + + +#endif // __FILE_ABSTRACTION__ + + diff --git a/src/MachOAbstraction.h b/src/MachOAbstraction.h deleted file mode 100644 index fe4b01e..0000000 --- a/src/MachOAbstraction.h +++ /dev/null @@ -1,2009 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * 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@ - */ -#include -#include -#include -#include -#include - - -#undef ENDIAN_READ16 -#undef ENDIAN_WRITE16 -#undef ENDIAN_READ32 -#undef ENDIAN_WRITE32 -#undef ENDIAN_SWAP64 -#undef ENDIAN_SWAP_POINTER - -#if defined(MACHO_32_SAME_ENDIAN) || defined(MACHO_64_SAME_ENDIAN) - #define ENDIAN_READ16(x) (x) - #define ENDIAN_WRITE16(into, value) into = (value); - - #define ENDIAN_READ32(x) (x) - #define ENDIAN_WRITE32(into, value) into = (value); - - #define ENDIAN_SWAP64(x) (x) - -#elif defined(MACHO_32_OPPOSITE_ENDIAN) || defined(MACHO_64_OPPOSITE_ENDIAN) - #define ENDIAN_READ16(x) OSReadSwapInt16((uint16_t*)&(x), 0) - #define ENDIAN_WRITE16(into, value) OSWriteSwapInt16(&(into), 0, value); - - #define ENDIAN_READ32(x) OSReadSwapInt32((uint32_t*)&(x), 0) - #define ENDIAN_WRITE32(into, value) OSWriteSwapInt32(&(into), 0, value); - - #define ENDIAN_SWAP64(x) OSSwapInt64(x) - -#else - #error file format undefined -#endif - - -#if defined(MACHO_64_SAME_ENDIAN) || defined(MACHO_64_OPPOSITE_ENDIAN) - typedef uint64_t macho_uintptr_t; - typedef int64_t macho_intptr_t; -#else - typedef uint32_t macho_uintptr_t; - typedef int32_t macho_intptr_t; -#endif - -#if defined(MACHO_32_SAME_ENDIAN) - #define ENDIAN_SWAP_POINTER(x) (x) -#elif defined(MACHO_64_SAME_ENDIAN) - #define ENDIAN_SWAP_POINTER(x) (x) -#elif defined(MACHO_32_OPPOSITE_ENDIAN) - #define ENDIAN_SWAP_POINTER(x) OSSwapInt32(x) -#elif defined(MACHO_64_OPPOSITE_ENDIAN) - #define ENDIAN_SWAP_POINTER(x) OSSwapInt64(x) -#else - #error file format undefined -#endif - - -#undef mach_header -#undef mach_header_64 -class macho_header { -public: - uint32_t magic() const; - void set_magic(uint32_t); - - cpu_type_t cputype() const; - void set_cputype(cpu_type_t); - - cpu_subtype_t cpusubtype() const; - void set_cpusubtype(cpu_subtype_t); - - uint32_t filetype() const; - void set_filetype(uint32_t); - - uint32_t ncmds() const; - void set_ncmds(uint32_t); - - uint32_t sizeofcmds() const; - void set_sizeofcmds(uint32_t); - - uint32_t flags() const; - void set_flags(uint32_t); - - void set_reserved(); - -#if defined(MACHO_64_SAME_ENDIAN) - enum { size = sizeof(mach_header_64) }; - enum { magic_value = MH_MAGIC_64 }; -#elif defined(MACHO_64_OPPOSITE_ENDIAN) - enum { size = sizeof(mach_header_64) }; - enum { magic_value = MH_MAGIC_64 }; -#elif defined(MACHO_32_SAME_ENDIAN) - enum { size = sizeof(mach_header) }; - enum { magic_value = MH_MAGIC }; -#elif defined(MACHO_32_OPPOSITE_ENDIAN) - enum { size = sizeof(mach_header) }; - enum { magic_value = MH_MAGIC }; -#endif - -private: -#if defined(MACHO_64_SAME_ENDIAN) || defined(MACHO_64_OPPOSITE_ENDIAN) - struct mach_header_64 content; -#else - struct mach_header content; -#endif -}; -#define mach_header __my_bad -#define mach_header_64 __my_bad - -inline __attribute__((always_inline)) -uint32_t macho_header::magic() const { - return ENDIAN_READ32(content.magic); -} - -inline __attribute__((always_inline)) -void macho_header::set_magic(uint32_t _value) { - ENDIAN_WRITE32(content.magic, _value); -} - -inline __attribute__((always_inline)) -cpu_type_t macho_header::cputype() const { - return ENDIAN_READ32(content.cputype); -} - -inline __attribute__((always_inline)) -void macho_header::set_cputype(cpu_type_t _value) { - ENDIAN_WRITE32(content.cputype, _value); -} - -inline __attribute__((always_inline)) -cpu_subtype_t macho_header::cpusubtype() const { - return ENDIAN_READ32(content.cpusubtype); -} - -inline __attribute__((always_inline)) -void macho_header::set_cpusubtype(cpu_subtype_t _value) { - ENDIAN_WRITE32(content.cpusubtype, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_header::filetype() const { - return ENDIAN_READ32(content.filetype); -} - -inline __attribute__((always_inline)) -void macho_header::set_filetype(uint32_t _value) { - ENDIAN_WRITE32(content.filetype, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_header::ncmds() const { - return ENDIAN_READ32(content.ncmds); -} - -inline __attribute__((always_inline)) -void macho_header::set_ncmds(uint32_t _value) { - ENDIAN_WRITE32(content.ncmds, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_header::sizeofcmds() const { - return ENDIAN_READ32(content.sizeofcmds); -} - -inline __attribute__((always_inline)) -void macho_header::set_sizeofcmds(uint32_t _value) { - ENDIAN_WRITE32(content.sizeofcmds, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_header::flags() const { - return ENDIAN_READ32(content.flags); -} - -inline __attribute__((always_inline)) -void macho_header::set_flags(uint32_t _value) { - ENDIAN_WRITE32(content.flags, _value); -} - -inline __attribute__((always_inline)) -void macho_header::set_reserved() { -#if defined(MACHO_64_SAME_ENDIAN) || defined(MACHO_64_OPPOSITE_ENDIAN) - content.reserved = 0; -#endif -} - - -#undef load_command -class macho_load_command { -public: - uint32_t cmd() const; - void set_cmd(uint32_t); - - uint32_t cmdsize() const; - void set_cmdsize(uint32_t); - -private: - struct load_command content; -}; - -inline __attribute__((always_inline)) -uint32_t macho_load_command::cmd() const { - return ENDIAN_READ32(content.cmd); -} - -inline __attribute__((always_inline)) -void macho_load_command::set_cmd(uint32_t _value) { - ENDIAN_WRITE32(content.cmd, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_load_command::cmdsize() const { - return ENDIAN_READ32(content.cmdsize); -} - -inline __attribute__((always_inline)) -void macho_load_command::set_cmdsize(uint32_t _value) { - ENDIAN_WRITE32(content.cmdsize, _value); -} -#define load_command __my_bad - - -#undef segment_command -#undef segment_command_64 -class macho_segment_command { -public: - uint32_t cmd() const; - void set_cmd(uint32_t); - - uint32_t cmdsize() const; - void set_cmdsize(uint32_t); - - const char* segname() const; - void set_segname(const char*); - - uint64_t vmaddr() const; - void set_vmaddr(uint64_t); - - uint64_t vmsize() const; - void set_vmsize(uint64_t); - - uint64_t fileoff() const; - void set_fileoff(uint64_t); - - uint64_t filesize() const; - void set_filesize(uint64_t); - - vm_prot_t maxprot() const; - void set_maxprot(vm_prot_t); - - vm_prot_t initprot() const; - void set_initprot(vm_prot_t); - - uint32_t nsects() const; - void set_nsects(uint32_t); - - uint32_t flags() const; - void set_flags(uint32_t); - - enum { size = -#if defined(MACHO_64_SAME_ENDIAN) || defined(MACHO_64_OPPOSITE_ENDIAN) - sizeof(segment_command_64) }; -#else - sizeof(segment_command) }; -#endif - - enum { command = -#if defined(MACHO_64_SAME_ENDIAN) || defined(MACHO_64_OPPOSITE_ENDIAN) - LC_SEGMENT_64 -#else - LC_SEGMENT -#endif - }; - -private: -#if defined(MACHO_64_SAME_ENDIAN) || defined(MACHO_64_OPPOSITE_ENDIAN) - struct segment_command_64 content; -#else - struct segment_command content; -#endif -}; -#define segment_command __my_bad -#define segment_command_64 __my_bad - -inline __attribute__((always_inline)) -uint32_t macho_segment_command::cmd() const { - return ENDIAN_READ32(content.cmd); -} - -inline __attribute__((always_inline)) -void macho_segment_command::set_cmd(uint32_t _value) { - ENDIAN_WRITE32(content.cmd, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_segment_command::cmdsize() const { - return ENDIAN_READ32(content.cmdsize); -} - -inline __attribute__((always_inline)) -void macho_segment_command::set_cmdsize(uint32_t _value) { - ENDIAN_WRITE32(content.cmdsize, _value); -} - -inline __attribute__((always_inline)) -const char* macho_segment_command::segname() const { - return content.segname; -} - -inline __attribute__((always_inline)) -void macho_segment_command::set_segname(const char* _value) { - strncpy(content.segname, _value, 16); -} - -inline __attribute__((always_inline)) -uint64_t macho_segment_command::vmaddr() const { -#if defined(ARCH_PPC64) - return ENDIAN_SWAP64(content.vmaddr); -#elif defined(ARCH_PPC) || defined(ARCH_I386) - return ENDIAN_READ32(content.vmaddr); -#else - #error unknown architecture -#endif -} - -inline __attribute__((always_inline)) -void macho_segment_command::set_vmaddr(uint64_t _value) { -#if defined(ARCH_PPC64) - content.vmaddr = ENDIAN_SWAP64(_value); -#elif defined(ARCH_PPC) || defined(ARCH_I386) - ENDIAN_WRITE32(content.vmaddr, _value); -#else - #error unknown architecture -#endif -} - -inline __attribute__((always_inline)) -uint64_t macho_segment_command::vmsize() const { -#if defined(ARCH_PPC64) - return ENDIAN_SWAP64(content.vmsize); -#elif defined(ARCH_PPC) || defined(ARCH_I386) - return ENDIAN_READ32(content.vmsize); -#else - #error unknown architecture -#endif -} - -inline __attribute__((always_inline)) -void macho_segment_command::set_vmsize(uint64_t _value) { -#if defined(ARCH_PPC64) - content.vmsize = ENDIAN_SWAP64(_value); -#elif defined(ARCH_PPC) || defined(ARCH_I386) - ENDIAN_WRITE32(content.vmsize, _value); -#else - #error unknown architecture -#endif -} - -inline __attribute__((always_inline)) -uint64_t macho_segment_command::fileoff() const { -#if defined(ARCH_PPC64) - return ENDIAN_SWAP64(content.fileoff); -#elif defined(ARCH_PPC) || defined(ARCH_I386) - return ENDIAN_READ32(content.fileoff); -#else - #error unknown architecture -#endif -} - -inline __attribute__((always_inline)) -void macho_segment_command::set_fileoff(uint64_t _value) { -#if defined(ARCH_PPC64) - content.fileoff = ENDIAN_SWAP64(_value); -#elif defined(ARCH_PPC) || defined(ARCH_I386) - ENDIAN_WRITE32(content.fileoff, _value); -#else - #error unknown architecture -#endif -} - -inline __attribute__((always_inline)) -uint64_t macho_segment_command::filesize() const { -#if defined(ARCH_PPC64) - return ENDIAN_SWAP64(content.filesize); -#elif defined(ARCH_PPC) || defined(ARCH_I386) - return ENDIAN_READ32(content.filesize); -#else - #error unknown architecture -#endif -} - -inline __attribute__((always_inline)) -void macho_segment_command::set_filesize(uint64_t _value) { -#if defined(ARCH_PPC64) - content.filesize = ENDIAN_SWAP64(_value); -#elif defined(ARCH_PPC) || defined(ARCH_I386) - ENDIAN_WRITE32(content.filesize, _value); -#else - #error unknown architecture -#endif -} - -inline __attribute__((always_inline)) -vm_prot_t macho_segment_command::maxprot() const { - return ENDIAN_READ32(content.maxprot); -} - -inline __attribute__((always_inline)) -void macho_segment_command::set_maxprot(vm_prot_t _value) { - ENDIAN_WRITE32(content.maxprot, _value); -} - -inline __attribute__((always_inline)) -vm_prot_t macho_segment_command::initprot() const { - return ENDIAN_READ32(content.initprot); -} - -inline __attribute__((always_inline)) -void macho_segment_command::set_initprot(vm_prot_t _value) { - ENDIAN_WRITE32(content.initprot, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_segment_command::nsects() const { - return ENDIAN_READ32(content.nsects); -} - -inline __attribute__((always_inline)) -void macho_segment_command::set_nsects(uint32_t _value) { - ENDIAN_WRITE32(content.nsects, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_segment_command::flags() const { - return ENDIAN_READ32(content.flags); -} - -inline __attribute__((always_inline)) -void macho_segment_command::set_flags(uint32_t _value) { - ENDIAN_WRITE32(content.flags, _value); -} - -#undef section -#undef section_64 -class macho_section { -public: - const char* sectname() const; - void set_sectname(const char*); - - const char* segname() const; - void set_segname(const char*); - - uint64_t addr() const; - void set_addr(uint64_t); - - uint64_t size() const; - void set_size(uint64_t); - - uint32_t offset() const; - void set_offset(uint32_t); - - uint32_t align() const; - void set_align(uint32_t); - - uint32_t reloff() const; - void set_reloff(uint32_t); - - uint32_t nreloc() const; - void set_nreloc(uint32_t); - - uint32_t flags() const; - void set_flags(uint32_t); - - uint32_t reserved1() const; - void set_reserved1(uint32_t); - - uint32_t reserved2() const; - void set_reserved2(uint32_t); - - enum { content_size = -#if defined(MACHO_64_SAME_ENDIAN) || defined(MACHO_64_OPPOSITE_ENDIAN) - sizeof(section_64) }; -#else - sizeof(section) }; -#endif - -private: -#if defined(MACHO_64_SAME_ENDIAN) || defined(MACHO_64_OPPOSITE_ENDIAN) - struct section_64 content; -#else - struct section content; -#endif -}; -#define section __my_bad -#define section_64 __my_bad - -inline __attribute__((always_inline)) -const char* macho_section::sectname() const { - return content.sectname; -} - -inline __attribute__((always_inline)) -void macho_section::set_sectname(const char* _value) { - strncpy(content.sectname, _value, 16); -} - -inline __attribute__((always_inline)) -const char* macho_section::segname() const { - return content.segname; -} - -inline __attribute__((always_inline)) -void macho_section::set_segname(const char* _value) { - strncpy(content.segname, _value, 16); -} - -inline __attribute__((always_inline)) -uint64_t macho_section::addr() const { -#if defined(ARCH_PPC64) - return ENDIAN_SWAP64(content.addr); -#elif defined(ARCH_PPC) || defined(ARCH_I386) - return ENDIAN_READ32(content.addr); -#else - #error unknown architecture -#endif -} - -inline __attribute__((always_inline)) -void macho_section::set_addr(uint64_t _value) { -#if defined(ARCH_PPC64) - content.addr = ENDIAN_SWAP64(_value); -#elif defined(ARCH_PPC) || defined(ARCH_I386) - ENDIAN_WRITE32(content.addr, _value); -#else - #error unknown architecture -#endif -} - -inline __attribute__((always_inline)) -uint64_t macho_section::size() const { -#if defined(ARCH_PPC64) - return ENDIAN_SWAP64(content.size); -#elif defined(ARCH_PPC) || defined(ARCH_I386) - return ENDIAN_READ32(content.size); -#else - #error unknown architecture -#endif -} - -inline __attribute__((always_inline)) -void macho_section::set_size(uint64_t _value) { -#if defined(ARCH_PPC64) - content.size = ENDIAN_SWAP64(_value); -#elif defined(ARCH_PPC) || defined(ARCH_I386) - ENDIAN_WRITE32(content.size, _value); -#else - #error unknown architecture -#endif -} - -inline __attribute__((always_inline)) -uint32_t macho_section::offset() const { - return ENDIAN_READ32(content.offset); -} - -inline __attribute__((always_inline)) -void macho_section::set_offset(uint32_t _value) { - ENDIAN_WRITE32(content.offset, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_section::align() const { - return ENDIAN_READ32(content.align); -} - -inline __attribute__((always_inline)) -void macho_section::set_align(uint32_t _value) { - ENDIAN_WRITE32(content.align, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_section::reloff() const { - return ENDIAN_READ32(content.reloff); -} - -inline __attribute__((always_inline)) -void macho_section::set_reloff(uint32_t _value) { - ENDIAN_WRITE32(content.reloff, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_section::nreloc() const { - return ENDIAN_READ32(content.nreloc); -} - -inline __attribute__((always_inline)) -void macho_section::set_nreloc(uint32_t _value) { - ENDIAN_WRITE32(content.nreloc, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_section::flags() const { - return ENDIAN_READ32(content.flags); -} - -inline __attribute__((always_inline)) -void macho_section::set_flags(uint32_t _value) { - ENDIAN_WRITE32(content.flags, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_section::reserved1() const { - return ENDIAN_READ32(content.reserved1); -} - -inline __attribute__((always_inline)) -void macho_section::set_reserved1(uint32_t _value) { - ENDIAN_WRITE32(content.reserved1, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_section::reserved2() const { - return ENDIAN_READ32(content.reserved2); -} - -inline __attribute__((always_inline)) -void macho_section::set_reserved2(uint32_t _value) { - ENDIAN_WRITE32(content.reserved2, _value); -} - -#undef dylib_command -class macho_dylib_command { -public: - uint32_t cmd() const; - void set_cmd(uint32_t); - - uint32_t cmdsize() const; - void set_cmdsize(uint32_t); - - const char* name() const; - void set_name_offset(); - - uint32_t timestamp() const; - void set_timestamp(uint32_t); - - uint32_t current_version() const; - void set_current_version(uint32_t); - - uint32_t compatibility_version() const; - void set_compatibility_version(uint32_t); - - enum { name_offset = sizeof(struct dylib_command) }; - -private: - struct dylib_command content; -}; -#define dylib_command __my_bad - -inline __attribute__((always_inline)) -uint32_t macho_dylib_command::cmd() const { - return ENDIAN_READ32(content.cmd); -} - -inline __attribute__((always_inline)) -void macho_dylib_command::set_cmd(uint32_t _value) { - ENDIAN_WRITE32(content.cmd, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_dylib_command::cmdsize() const { - return ENDIAN_READ32(content.cmdsize); -} - -inline __attribute__((always_inline)) -void macho_dylib_command::set_cmdsize(uint32_t _value) { - ENDIAN_WRITE32(content.cmdsize, _value); -} - -inline __attribute__((always_inline)) -const char* macho_dylib_command::name() const { - return (char*)(&content) + ENDIAN_READ32(content.dylib.name.offset); -} - -inline __attribute__((always_inline)) -void macho_dylib_command::set_name_offset() { - ENDIAN_WRITE32(content.dylib.name.offset, name_offset); -} - -inline __attribute__((always_inline)) -uint32_t macho_dylib_command::timestamp() const { - return ENDIAN_READ32(content.dylib.timestamp); -} - -inline __attribute__((always_inline)) -void macho_dylib_command::set_timestamp(uint32_t _value) { - ENDIAN_WRITE32(content.dylib.timestamp, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_dylib_command::current_version() const { - return ENDIAN_READ32(content.dylib.current_version); -} - -inline __attribute__((always_inline)) -void macho_dylib_command::set_current_version(uint32_t _value) { - ENDIAN_WRITE32(content.dylib.current_version, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_dylib_command::compatibility_version() const { - return ENDIAN_READ32(content.dylib.compatibility_version); -} - -inline __attribute__((always_inline)) -void macho_dylib_command::set_compatibility_version(uint32_t _value) { - ENDIAN_WRITE32(content.dylib.compatibility_version, _value); -} - - - -#undef dylinker_command -class macho_dylinker_command { -public: - uint32_t cmd() const; - void set_cmd(uint32_t); - - uint32_t cmdsize() const; - void set_cmdsize(uint32_t); - - void set_name_offset(); - - enum { name_offset = sizeof(struct dylinker_command) }; - -private: - struct dylinker_command content; -}; -#define dylinker_command __my_bad - -inline __attribute__((always_inline)) -uint32_t macho_dylinker_command::cmd() const { - return ENDIAN_READ32(content.cmd); -} - -inline __attribute__((always_inline)) -void macho_dylinker_command::set_cmd(uint32_t _value) { - ENDIAN_WRITE32(content.cmd, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_dylinker_command::cmdsize() const { - return ENDIAN_READ32(content.cmdsize); -} - -inline __attribute__((always_inline)) -void macho_dylinker_command::set_cmdsize(uint32_t _value) { - ENDIAN_WRITE32(content.cmdsize, _value); -} - -inline __attribute__((always_inline)) -void macho_dylinker_command::set_name_offset() { - ENDIAN_WRITE32(content.name.offset, name_offset); -} - - - -#undef sub_framework_command -class macho_sub_framework_command { -public: - uint32_t cmd() const; - void set_cmd(uint32_t); - - uint32_t cmdsize() const; - void set_cmdsize(uint32_t); - - const char* name() const; - void set_name_offset(); - - enum { name_offset = sizeof(struct sub_framework_command) }; - -private: - struct sub_framework_command content; -}; -#define sub_framework_command __my_bad - -inline __attribute__((always_inline)) -uint32_t macho_sub_framework_command::cmd() const { - return ENDIAN_READ32(content.cmd); -} - -inline __attribute__((always_inline)) -void macho_sub_framework_command::set_cmd(uint32_t _value) { - ENDIAN_WRITE32(content.cmd, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_sub_framework_command::cmdsize() const { - return ENDIAN_READ32(content.cmdsize); -} - -inline __attribute__((always_inline)) -void macho_sub_framework_command::set_cmdsize(uint32_t _value) { - ENDIAN_WRITE32(content.cmdsize, _value); -} - -inline __attribute__((always_inline)) -const char* macho_sub_framework_command::name() const { - return (char*)(&content) + ENDIAN_READ32(content.umbrella.offset); -} - -inline __attribute__((always_inline)) -void macho_sub_framework_command::set_name_offset() { - ENDIAN_WRITE32(content.umbrella.offset, name_offset); -} - -#undef sub_client_command -class macho_sub_client_command { -public: - uint32_t cmd() const; - void set_cmd(uint32_t); - - uint32_t cmdsize() const; - void set_cmdsize(uint32_t); - - const char* name() const; - void set_name_offset(); - - enum { name_offset = sizeof(struct sub_client_command) }; -private: - struct sub_client_command content; -}; -#define sub_client_command __my_bad - -inline __attribute__((always_inline)) -uint32_t macho_sub_client_command::cmd() const { - return ENDIAN_READ32(content.cmd); -} - -inline __attribute__((always_inline)) -void macho_sub_client_command::set_cmd(uint32_t _value) { - ENDIAN_WRITE32(content.cmd, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_sub_client_command::cmdsize() const { - return ENDIAN_READ32(content.cmdsize); -} - -inline __attribute__((always_inline)) -void macho_sub_client_command::set_cmdsize(uint32_t _value) { - ENDIAN_WRITE32(content.cmdsize, _value); -} - -inline __attribute__((always_inline)) -const char* macho_sub_client_command::name() const { - return (char*)(&content) + ENDIAN_READ32(content.client.offset); -} - -inline __attribute__((always_inline)) -void macho_sub_client_command::set_name_offset() { - ENDIAN_WRITE32(content.client.offset, name_offset); -} - - - -#undef sub_umbrella_command -class macho_sub_umbrella_command { -public: - uint32_t cmd() const; - void set_cmd(uint32_t); - - uint32_t cmdsize() const; - void set_cmdsize(uint32_t); - - const char* name() const; - void set_name_offset(); - - enum { name_offset = sizeof(struct sub_umbrella_command) }; -private: - struct sub_umbrella_command content; -}; -#define sub_umbrella_command __my_bad - -inline __attribute__((always_inline)) -uint32_t macho_sub_umbrella_command::cmd() const { - return ENDIAN_READ32(content.cmd); -} - -inline __attribute__((always_inline)) -void macho_sub_umbrella_command::set_cmd(uint32_t _value) { - ENDIAN_WRITE32(content.cmd, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_sub_umbrella_command::cmdsize() const { - return ENDIAN_READ32(content.cmdsize); -} - -inline __attribute__((always_inline)) -void macho_sub_umbrella_command::set_cmdsize(uint32_t _value) { - ENDIAN_WRITE32(content.cmdsize, _value); -} - -inline __attribute__((always_inline)) -const char* macho_sub_umbrella_command::name() const { - return (char*)(&content) + ENDIAN_READ32(content.sub_umbrella.offset); -} - -inline __attribute__((always_inline)) -void macho_sub_umbrella_command::set_name_offset() { - ENDIAN_WRITE32(content.sub_umbrella.offset, name_offset); -} - - - - -#undef sub_library_command -class macho_sub_library_command { -public: - uint32_t cmd() const; - void set_cmd(uint32_t); - - uint32_t cmdsize() const; - void set_cmdsize(uint32_t); - - const char* name() const; - void set_name_offset(); - - enum { name_offset = sizeof(struct sub_library_command) }; -private: - struct sub_library_command content; -}; -#define sub_library_command __my_bad - -inline __attribute__((always_inline)) -uint32_t macho_sub_library_command::cmd() const { - return ENDIAN_READ32(content.cmd); -} - -inline __attribute__((always_inline)) -void macho_sub_library_command::set_cmd(uint32_t _value) { - ENDIAN_WRITE32(content.cmd, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_sub_library_command::cmdsize() const { - return ENDIAN_READ32(content.cmdsize); -} - -inline __attribute__((always_inline)) -void macho_sub_library_command::set_cmdsize(uint32_t _value) { - ENDIAN_WRITE32(content.cmdsize, _value); -} - -inline __attribute__((always_inline)) -const char* macho_sub_library_command::name() const { - return (char*)(&content) + ENDIAN_READ32(content.sub_library.offset); -} - -inline __attribute__((always_inline)) -void macho_sub_library_command::set_name_offset() { - ENDIAN_WRITE32(content.sub_library.offset, name_offset); -} - - - -#undef routines_command -#undef routines_command_64 -class macho_routines_command { -public: - uint32_t cmd() const; - void set_cmd(uint32_t); - - uint32_t cmdsize() const; - void set_cmdsize(uint32_t); - - uint64_t init_address() const; - void set_init_address(uint64_t); - - uint64_t init_module() const; - void set_init_module(uint64_t); - -#if defined(MACHO_64_SAME_ENDIAN) || defined(MACHO_64_OPPOSITE_ENDIAN) - enum { size = sizeof(struct routines_command_64) }; - enum { command = LC_ROUTINES_64 }; -#else - enum { size = sizeof(struct routines_command) }; - enum { command = LC_ROUTINES }; -#endif - -private: -#if defined(MACHO_64_SAME_ENDIAN) || defined(MACHO_64_OPPOSITE_ENDIAN) - struct routines_command_64 content; -#else - struct routines_command content; -#endif -}; -#define routines_command __my_bad -#define routines_command_64 __my_bad - -inline __attribute__((always_inline)) -uint32_t macho_routines_command::cmd() const { - return ENDIAN_READ32(content.cmd); -} - -inline __attribute__((always_inline)) -void macho_routines_command::set_cmd(uint32_t _value) { - ENDIAN_WRITE32(content.cmd, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_routines_command::cmdsize() const { - return ENDIAN_READ32(content.cmdsize); -} - -inline __attribute__((always_inline)) -void macho_routines_command::set_cmdsize(uint32_t _value) { - ENDIAN_WRITE32(content.cmdsize, _value); -} - -inline __attribute__((always_inline)) -uint64_t macho_routines_command::init_address() const { -#if defined(ARCH_PPC64) - return ENDIAN_SWAP64(content.init_address); -#elif defined(ARCH_PPC) || defined(ARCH_I386) - return ENDIAN_READ32(content.init_address); -#else - #error unknown architecture -#endif -} - -inline __attribute__((always_inline)) -void macho_routines_command::set_init_address(uint64_t _value) { -#if defined(ARCH_PPC64) - content.init_address = ENDIAN_SWAP64(_value); -#elif defined(ARCH_PPC) || defined(ARCH_I386) - ENDIAN_WRITE32(content.init_address, _value); -#else - #error unknown architecture -#endif -} - -inline __attribute__((always_inline)) -uint64_t macho_routines_command::init_module() const { -#if defined(ARCH_PPC64) - return ENDIAN_SWAP64(content.init_module); -#elif defined(ARCH_PPC) || defined(ARCH_I386) - return ENDIAN_READ32(content.init_module); -#else - #error unknown architecture -#endif -} - -inline __attribute__((always_inline)) -void macho_routines_command::set_init_module(uint64_t _value) { -#if defined(ARCH_PPC64) - content.init_module = ENDIAN_SWAP64(_value); -#elif defined(ARCH_PPC) || defined(ARCH_I386) - ENDIAN_WRITE32(content.init_module, _value); -#else - #error unknown architecture -#endif -} - - - -#undef symtab_command -class macho_symtab_command { -public: - uint32_t cmd() const; - void set_cmd(uint32_t); - - uint32_t cmdsize() const; - void set_cmdsize(uint32_t); - - uint32_t symoff() const; - void set_symoff(uint32_t); - - uint32_t nsyms() const; - void set_nsyms(uint32_t); - - uint32_t stroff() const; - void set_stroff(uint32_t); - - uint32_t strsize() const; - void set_strsize(uint32_t); - - enum { size = sizeof(struct symtab_command ) }; - -private: - struct symtab_command content; -}; -#define symtab_command __my_bad - - -inline __attribute__((always_inline)) -uint32_t macho_symtab_command::cmd() const { - return ENDIAN_READ32(content.cmd); -} - -inline __attribute__((always_inline)) -void macho_symtab_command::set_cmd(uint32_t _value) { - ENDIAN_WRITE32(content.cmd, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_symtab_command::cmdsize() const { - return ENDIAN_READ32(content.cmdsize); -} - -inline __attribute__((always_inline)) -void macho_symtab_command::set_cmdsize(uint32_t _value) { - ENDIAN_WRITE32(content.cmdsize, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_symtab_command::symoff() const { - return ENDIAN_READ32(content.symoff); -} - -inline __attribute__((always_inline)) -void macho_symtab_command::set_symoff(uint32_t _value) { - ENDIAN_WRITE32(content.symoff, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_symtab_command::nsyms() const { - return ENDIAN_READ32(content.nsyms); -} - -inline __attribute__((always_inline)) -void macho_symtab_command::set_nsyms(uint32_t _value) { - ENDIAN_WRITE32(content.nsyms, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_symtab_command::stroff() const { - return ENDIAN_READ32(content.stroff); -} - -inline __attribute__((always_inline)) -void macho_symtab_command::set_stroff(uint32_t _value) { - ENDIAN_WRITE32(content.stroff, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_symtab_command::strsize() const { - return ENDIAN_READ32(content.strsize); -} - -inline __attribute__((always_inline)) -void macho_symtab_command::set_strsize(uint32_t _value) { - ENDIAN_WRITE32(content.strsize, _value); -} - - -#undef dysymtab_command -class macho_dysymtab_command { -public: - uint32_t cmd() const; - void set_cmd(uint32_t); - - uint32_t cmdsize() const; - void set_cmdsize(uint32_t); - - uint32_t ilocalsym() const; - void set_ilocalsym(uint32_t); - - uint32_t nlocalsym() const; - void set_nlocalsym(uint32_t); - - uint32_t iextdefsym() const; - void set_iextdefsym(uint32_t); - - uint32_t nextdefsym() const; - void set_nextdefsym(uint32_t); - - uint32_t iundefsym() const; - void set_iundefsym(uint32_t); - - uint32_t nundefsym() const; - void set_nundefsym(uint32_t); - - uint32_t tocoff() const; - void set_tocoff(uint32_t); - - uint32_t ntoc() const; - void set_ntoc(uint32_t); - - uint32_t modtaboff() const; - void set_modtaboff(uint32_t); - - uint32_t nmodtab() const; - void set_nmodtab(uint32_t); - - uint32_t extrefsymoff() const; - void set_extrefsymoff(uint32_t); - - uint32_t nextrefsyms() const; - void set_nextrefsyms(uint32_t); - - uint32_t indirectsymoff() const; - void set_indirectsymoff(uint32_t); - - uint32_t nindirectsyms() const; - void set_nindirectsyms(uint32_t); - - uint32_t extreloff() const; - void set_extreloff(uint32_t); - - uint32_t nextrel() const; - void set_nextrel(uint32_t); - - uint32_t locreloff() const; - void set_locreloff(uint32_t); - - uint32_t nlocrel() const; - void set_nlocrel(uint32_t); - - enum { size = sizeof(struct dysymtab_command ) }; -private: - struct dysymtab_command content; -}; -#define dysymtab_command __my_bad - -inline __attribute__((always_inline)) -uint32_t macho_dysymtab_command::cmd() const { - return ENDIAN_READ32(content.cmd); -} - -inline __attribute__((always_inline)) -void macho_dysymtab_command::set_cmd(uint32_t _value) { - ENDIAN_WRITE32(content.cmd, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_dysymtab_command::cmdsize() const { - return ENDIAN_READ32(content.cmdsize); -} - -inline __attribute__((always_inline)) -void macho_dysymtab_command::set_cmdsize(uint32_t _value) { - ENDIAN_WRITE32(content.cmdsize, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_dysymtab_command::ilocalsym() const { - return ENDIAN_READ32(content.ilocalsym); -} - -inline __attribute__((always_inline)) -void macho_dysymtab_command::set_ilocalsym(uint32_t _value) { - ENDIAN_WRITE32(content.ilocalsym, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_dysymtab_command::nlocalsym() const { - return ENDIAN_READ32(content.nlocalsym); -} - -inline __attribute__((always_inline)) -void macho_dysymtab_command::set_nlocalsym(uint32_t _value) { - ENDIAN_WRITE32(content.nlocalsym, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_dysymtab_command::iextdefsym() const { - return ENDIAN_READ32(content.iextdefsym); -} - -inline __attribute__((always_inline)) -void macho_dysymtab_command::set_iextdefsym(uint32_t _value) { - ENDIAN_WRITE32(content.iextdefsym, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_dysymtab_command::nextdefsym() const { - return ENDIAN_READ32(content.nextdefsym); -} - -inline __attribute__((always_inline)) -void macho_dysymtab_command::set_nextdefsym(uint32_t _value) { - ENDIAN_WRITE32(content.nextdefsym, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_dysymtab_command::iundefsym() const { - return ENDIAN_READ32(content.iundefsym); -} - -inline __attribute__((always_inline)) -void macho_dysymtab_command::set_iundefsym(uint32_t _value) { - ENDIAN_WRITE32(content.iundefsym, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_dysymtab_command::nundefsym() const { - return ENDIAN_READ32(content.nundefsym); -} - -inline __attribute__((always_inline)) -void macho_dysymtab_command::set_nundefsym(uint32_t _value) { - ENDIAN_WRITE32(content.nundefsym, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_dysymtab_command::tocoff() const { - return ENDIAN_READ32(content.tocoff); -} - -inline __attribute__((always_inline)) -void macho_dysymtab_command::set_tocoff(uint32_t _value) { - ENDIAN_WRITE32(content.tocoff, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_dysymtab_command::ntoc() const { - return ENDIAN_READ32(content.ntoc); -} - -inline __attribute__((always_inline)) -void macho_dysymtab_command::set_ntoc(uint32_t _value) { - ENDIAN_WRITE32(content.ntoc, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_dysymtab_command::modtaboff() const { - return ENDIAN_READ32(content.modtaboff); -} - -inline __attribute__((always_inline)) -void macho_dysymtab_command::set_modtaboff(uint32_t _value) { - ENDIAN_WRITE32(content.modtaboff, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_dysymtab_command::nmodtab() const { - return ENDIAN_READ32(content.nmodtab); -} - -inline __attribute__((always_inline)) -void macho_dysymtab_command::set_nmodtab(uint32_t _value) { - ENDIAN_WRITE32(content.nmodtab, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_dysymtab_command::extrefsymoff() const { - return ENDIAN_READ32(content.extrefsymoff); -} - -inline __attribute__((always_inline)) -void macho_dysymtab_command::set_extrefsymoff(uint32_t _value) { - ENDIAN_WRITE32(content.extrefsymoff, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_dysymtab_command::nextrefsyms() const { - return ENDIAN_READ32(content.nextrefsyms); -} - -inline __attribute__((always_inline)) -void macho_dysymtab_command::set_nextrefsyms(uint32_t _value) { - ENDIAN_WRITE32(content.nextrefsyms, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_dysymtab_command::indirectsymoff() const { - return ENDIAN_READ32(content.indirectsymoff); -} - -inline __attribute__((always_inline)) -void macho_dysymtab_command::set_indirectsymoff(uint32_t _value) { - ENDIAN_WRITE32(content.indirectsymoff, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_dysymtab_command::nindirectsyms() const { - return ENDIAN_READ32(content.nindirectsyms); -} - -inline __attribute__((always_inline)) -void macho_dysymtab_command::set_nindirectsyms(uint32_t _value) { - ENDIAN_WRITE32(content.nindirectsyms, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_dysymtab_command::extreloff() const { - return ENDIAN_READ32(content.extreloff); -} - -inline __attribute__((always_inline)) -void macho_dysymtab_command::set_extreloff(uint32_t _value) { - ENDIAN_WRITE32(content.extreloff, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_dysymtab_command::nextrel() const { - return ENDIAN_READ32(content.nextrel); -} - -inline __attribute__((always_inline)) -void macho_dysymtab_command::set_nextrel(uint32_t _value) { - ENDIAN_WRITE32(content.nextrel, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_dysymtab_command::locreloff() const { - return ENDIAN_READ32(content.locreloff); -} - -inline __attribute__((always_inline)) -void macho_dysymtab_command::set_locreloff(uint32_t _value) { - ENDIAN_WRITE32(content.locreloff, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_dysymtab_command::nlocrel() const { - return ENDIAN_READ32(content.nlocrel); -} - -inline __attribute__((always_inline)) -void macho_dysymtab_command::set_nlocrel(uint32_t _value) { - ENDIAN_WRITE32(content.nlocrel, _value); -} - - - -#undef twolevel_hints_command -class macho_twolevel_hints_command { -public: - uint32_t cmd() const; - void set_cmd(uint32_t); - - uint32_t cmdsize() const; - void set_cmdsize(uint32_t); - - uint32_t offset() const; - void set_offset(uint32_t); - - uint32_t nhints() const; - void set_nhints(uint32_t); - -private: - struct twolevel_hints_command content; -}; -#define twolevel_hints_command __my_bad - -inline __attribute__((always_inline)) -uint32_t macho_twolevel_hints_command::cmd() const { - return ENDIAN_READ32(content.cmd); -} - -inline __attribute__((always_inline)) -void macho_twolevel_hints_command::set_cmd(uint32_t _value) { - ENDIAN_WRITE32(content.cmd, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_twolevel_hints_command::cmdsize() const { - return ENDIAN_READ32(content.cmdsize); -} - -inline __attribute__((always_inline)) -void macho_twolevel_hints_command::set_cmdsize(uint32_t _value) { - ENDIAN_WRITE32(content.cmdsize, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_twolevel_hints_command::offset() const { - return ENDIAN_READ32(content.offset); -} - -inline __attribute__((always_inline)) -void macho_twolevel_hints_command::set_offset(uint32_t _value) { - ENDIAN_WRITE32(content.offset, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_twolevel_hints_command::nhints() const { - return ENDIAN_READ32(content.nhints); -} - -inline __attribute__((always_inline)) -void macho_twolevel_hints_command::set_nhints(uint32_t _value) { - ENDIAN_WRITE32(content.nhints, _value); -} - - -#undef thread_command -class macho_thread_command { -public: - uint32_t cmd() const; - void set_cmd(uint32_t); - - uint32_t cmdsize() const; - void set_cmdsize(uint32_t); - - uint32_t flavor() const; - void set_flavor(uint32_t); - - uint32_t count() const; - void set_count(uint32_t); - - uint32_t threadState32(uint32_t index) const; - void set_threadState32(uint32_t index, uint32_t value); - - uint64_t threadState64(uint32_t offset) const; - void set_threadState64(uint32_t index, uint64_t value); - - enum { size = sizeof(struct thread_command) + 8 }; - -private: - struct thread_command content; - uint32_t content_flavor; - uint32_t content_count; - uint32_t threadState[1]; -}; -#define thread_command __my_bad - -inline __attribute__((always_inline)) -uint32_t macho_thread_command::cmd() const { - return ENDIAN_READ32(content.cmd); -} - -inline __attribute__((always_inline)) -void macho_thread_command::set_cmd(uint32_t _value) { - ENDIAN_WRITE32(content.cmd, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_thread_command::cmdsize() const { - return ENDIAN_READ32(content.cmdsize); -} - -inline __attribute__((always_inline)) -void macho_thread_command::set_cmdsize(uint32_t _value) { - ENDIAN_WRITE32(content.cmdsize, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_thread_command::flavor() const { - return ENDIAN_READ32(content_flavor); -} - -inline __attribute__((always_inline)) -void macho_thread_command::set_flavor(uint32_t _value) { - ENDIAN_WRITE32(content_flavor, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_thread_command::count() const { - return ENDIAN_READ32(content_count); -} - -inline __attribute__((always_inline)) -void macho_thread_command::set_count(uint32_t _value) { - ENDIAN_WRITE32(content_count, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_thread_command::threadState32(uint32_t index) const -{ - return ENDIAN_READ32(threadState[index]); -} - -inline __attribute__((always_inline)) -void macho_thread_command::set_threadState32(uint32_t index, uint32_t _value) -{ - ENDIAN_WRITE32(threadState[index], _value); -} - -inline __attribute__((always_inline)) -uint64_t macho_thread_command::threadState64(uint32_t index) const -{ - uint64_t temp = *((uint64_t*)(&threadState[index])); - return ENDIAN_SWAP64(temp); -} - -inline __attribute__((always_inline)) -void macho_thread_command::set_threadState64(uint32_t index, uint64_t _value) -{ - *((uint64_t*)(&threadState[index])) = ENDIAN_SWAP64(_value); -} - - - -#undef nlist -#undef nlist_64 -class macho_nlist { -public: - uint32_t n_strx() const; - void set_n_strx(uint32_t); - - uint8_t n_type() const; - void set_n_type(uint8_t); - - uint8_t n_sect() const; - void set_n_sect(uint8_t); - - uint16_t n_desc() const; - void set_n_desc(uint16_t); - - uint64_t n_value() const; - void set_n_value(uint64_t); - - -#if defined(MACHO_64_SAME_ENDIAN) || defined(MACHO_64_OPPOSITE_ENDIAN) - enum { size = sizeof(struct nlist_64) }; -#else - enum { size = sizeof(struct nlist) }; -#endif - -private: -#if defined(MACHO_64_SAME_ENDIAN) || defined(MACHO_64_OPPOSITE_ENDIAN) - struct nlist_64 content; -#else - struct nlist content; -#endif -}; -#define nlist __my_bad -#define nlist_64 __my_bad - -inline __attribute__((always_inline)) -uint32_t macho_nlist::n_strx() const { - return ENDIAN_READ32(content.n_un.n_strx); -} - -inline __attribute__((always_inline)) -void macho_nlist::set_n_strx(uint32_t _value) { - ENDIAN_WRITE32(content.n_un.n_strx, _value); -} - -inline __attribute__((always_inline)) -uint8_t macho_nlist::n_type() const { - return content.n_type; -} - -inline __attribute__((always_inline)) -void macho_nlist::set_n_type(uint8_t _value) { - content.n_type = _value; -} - -inline __attribute__((always_inline)) -uint8_t macho_nlist::n_sect() const { - return content.n_sect; -} - -inline __attribute__((always_inline)) -void macho_nlist::set_n_sect(uint8_t _value) { - content.n_sect = _value; -} - -inline __attribute__((always_inline)) -uint16_t macho_nlist::n_desc() const { - return ENDIAN_READ16(content.n_desc); -} - -inline __attribute__((always_inline)) -void macho_nlist::set_n_desc(uint16_t _value) { - ENDIAN_WRITE16(content.n_desc, _value); -} - -inline __attribute__((always_inline)) -uint64_t macho_nlist::n_value() const { -#if defined(ARCH_PPC64) - return ENDIAN_SWAP64(content.n_value); -#elif defined(ARCH_PPC) || defined(ARCH_I386) - return ENDIAN_READ32(content.n_value); -#else - #error unknown architecture -#endif -} - -inline __attribute__((always_inline)) -void macho_nlist::set_n_value(uint64_t _value) { -#if defined(ARCH_PPC64) - content.n_value = ENDIAN_SWAP64(_value); -#elif defined(ARCH_PPC) || defined(ARCH_I386) - ENDIAN_WRITE32(content.n_value, _value); -#else - #error unknown architecture -#endif -} - - - -#undef relocation_info -class macho_relocation_info { -public: - int32_t r_address() const; - void set_r_address(int32_t); - - uint32_t r_symbolnum() const; - void set_r_symbolnum(uint32_t); - - bool r_pcrel() const; - void set_r_pcrel(bool); - - uint8_t r_length() const; - void set_r_length(uint8_t); - - bool r_extern() const; - void set_r_extern(bool); - - uint8_t r_type() const; - void set_r_type(uint8_t); - - enum { size = sizeof(struct relocation_info) }; -#if defined(MACHO_64_SAME_ENDIAN) || defined(MACHO_64_OPPOSITE_ENDIAN) - enum { pointer_length = 3 }; -#else - enum { pointer_length = 2 }; -#endif - -private: - struct relocation_info content; -}; -#define relocation_info __my_bad - - -inline __attribute__((always_inline)) -int32_t macho_relocation_info::r_address() const { - return ENDIAN_READ32(content.r_address); -} - -inline __attribute__((always_inline)) -void macho_relocation_info::set_r_address(int32_t _value) { - ENDIAN_WRITE32(content.r_address, _value); -} - -inline __attribute__((always_inline)) -uint32_t macho_relocation_info::r_symbolnum() const { - uint32_t temp = ENDIAN_READ32(((const uint32_t*)&content)[1]); -#if defined(ARCH_PPC) || defined(ARCH_PPC64) - return (temp >> 8); -#elif defined(ARCH_I386) - return temp & 0x00FFFFFF; -#else - #error unknown architecture -#endif -} - -inline __attribute__((always_inline)) -void macho_relocation_info::set_r_symbolnum(uint32_t _value) { - uint32_t temp = ENDIAN_READ32(((const uint32_t*)&content)[1]); -#if defined(ARCH_PPC) || defined(ARCH_PPC64) - temp &= 0x000000FF; - temp |= ((_value & 0x00FFFFFF) << 8); -#elif defined(ARCH_I386) - temp &= 0xFF000000; - temp |= (_value & 0x00FFFFFF); -#else - #error unknown architecture -#endif - ENDIAN_WRITE32(((uint32_t*)&content)[1], temp); -} - -inline __attribute__((always_inline)) -bool macho_relocation_info::r_pcrel() const { - uint32_t temp = ENDIAN_READ32(((const uint32_t*)&content)[1]); -#if defined(ARCH_PPC) || defined(ARCH_PPC64) - return ((temp & 0x00000080) != 0); -#elif defined(ARCH_I386) - return ((temp & 0x01000000) != 0); -#else - #error unknown architecture -#endif -} - -inline __attribute__((always_inline)) -void macho_relocation_info::set_r_pcrel(bool _value) { - uint32_t temp = ENDIAN_READ32(((const uint32_t*)&content)[1]); -#if defined(ARCH_PPC) || defined(ARCH_PPC64) - temp &= 0xFFFFFF7F; - if ( _value ) - temp |= 0x00000080; -#elif defined(ARCH_I386) - temp &= 0xFEFFFFFF; - if ( _value ) - temp |= 0x01000000; -#else - #error unknown architecture -#endif - ENDIAN_WRITE32(((uint32_t*)&content)[1], temp); -} - -inline __attribute__((always_inline)) -uint8_t macho_relocation_info::r_length() const { - uint32_t temp = ENDIAN_READ32(((const uint32_t*)&content)[1]); -#if defined(ARCH_PPC) || defined(ARCH_PPC64) - return ((temp & 0x00000060) >> 5); -#elif defined(ARCH_I386) - return ((temp & 0x06000000) >> 25); -#else - #error unknown architecture -#endif -} - -inline __attribute__((always_inline)) -void macho_relocation_info::set_r_length(uint8_t _value) { - uint32_t temp = ENDIAN_READ32(((const uint32_t*)&content)[1]); -#if defined(ARCH_PPC) || defined(ARCH_PPC64) - temp &= 0xFFFFFF9F; - temp |= ((_value & 0x03) << 5); -#elif defined(ARCH_I386) - temp &= 0xF9FFFFFF; - temp |= ((_value & 0x03) << 25); -#else - #error unknown architecture -#endif - ENDIAN_WRITE32(((uint32_t*)&content)[1], temp); -} - -inline __attribute__((always_inline)) -bool macho_relocation_info::r_extern() const { - uint32_t temp = ENDIAN_READ32(((const uint32_t*)&content)[1]); -#if defined(ARCH_PPC) || defined(ARCH_PPC64) - return ((temp & 0x00000010) != 0); -#elif defined(ARCH_I386) - return ((temp & 0x08000000) != 0); -#else - #error unknown architecture -#endif -} - -inline __attribute__((always_inline)) -void macho_relocation_info::set_r_extern(bool _value) { - uint32_t temp = ENDIAN_READ32(((const uint32_t*)&content)[1]); -#if defined(ARCH_PPC) || defined(ARCH_PPC64) - temp &= 0xFFFFFFEF; - if ( _value ) - temp |= 0x00000010; -#elif defined(ARCH_I386) - temp &= 0xF7FFFFFF; - if ( _value ) - temp |= 0x08000000; -#else - #error unknown architecture -#endif - ENDIAN_WRITE32(((uint32_t*)&content)[1], temp); -} - -inline __attribute__((always_inline)) -uint8_t macho_relocation_info::r_type() const { - uint32_t temp = ENDIAN_READ32(((const uint32_t*)&content)[1]); -#if defined(ARCH_PPC) || defined(ARCH_PPC64) - return (temp & 0x0000000F); -#elif defined(ARCH_I386) - return ((temp & 0xF0000000) >> 28); -#else - #error unknown architecture -#endif -} - -inline __attribute__((always_inline)) -void macho_relocation_info::set_r_type(uint8_t _value) { - uint32_t temp = ENDIAN_READ32(((const uint32_t*)&content)[1]); -#if defined(ARCH_PPC) || defined(ARCH_PPC64) - temp &= 0xFFFFFFF0; - temp |= (_value & 0x0F); -#elif defined(ARCH_I386) - temp &= 0x0FFFFFFF; - temp |= ((_value & 0x0F) << 28); -#else - #error unknown architecture -#endif - ENDIAN_WRITE32(((uint32_t*)&content)[1], temp); -} - - - -#undef scattered_relocation_info -class macho_scattered_relocation_info { -public: - bool r_scattered() const; - void set_r_scattered(bool); - - bool r_pcrel() const; - void set_r_pcrel(bool); - - uint8_t r_length() const; - void set_r_length(uint8_t); - - uint8_t r_type() const; - void set_r_type(uint8_t); - - uint32_t r_address() const; - void set_r_address(uint32_t); - - int32_t r_value() const; - void set_r_value(int32_t); - -private: - struct scattered_relocation_info content; -}; -#define scattered_relocation_info __my_bad - -inline __attribute__((always_inline)) -bool macho_scattered_relocation_info::r_scattered() const { - uint32_t temp = *((const uint32_t*)&content); - temp = ENDIAN_READ32(temp); - return ((temp & 0x80000000) != 0); -} - -inline __attribute__((always_inline)) -void macho_scattered_relocation_info::set_r_scattered(bool _value) { - uint32_t temp = ENDIAN_READ32(*((const uint32_t*)&content)); - if ( _value ) - temp |= 0x80000000; - else - temp &= ~0x80000000; - ENDIAN_WRITE32(*((uint32_t*)&content), temp); -} - -inline __attribute__((always_inline)) -bool macho_scattered_relocation_info::r_pcrel() const { - uint32_t temp = *((const uint32_t*)&content); - temp = ENDIAN_READ32(temp); - return ((temp & 0x40000000) != 0); -} - -inline __attribute__((always_inline)) -void macho_scattered_relocation_info::set_r_pcrel(bool _value) { - uint32_t temp = ENDIAN_READ32(*((const uint32_t*)&content)); - if ( _value ) - temp |= 0x40000000; - else - temp &= ~0x40000000; - ENDIAN_WRITE32(*((uint32_t*)&content), temp); -} - -inline __attribute__((always_inline)) -uint8_t macho_scattered_relocation_info::r_length() const { - uint32_t temp = ENDIAN_READ32(*((const uint32_t*)&content)); - return (temp >> 28) & 0x03; -} - -inline __attribute__((always_inline)) -void macho_scattered_relocation_info::set_r_length(uint8_t _value) { - uint32_t temp = ENDIAN_READ32(*((const uint32_t*)&content)); - temp &= 0xCFFFFFFF; - temp |= ((_value & 0x03) << 28); - ENDIAN_WRITE32(*((uint32_t*)&content), temp); -} - -inline __attribute__((always_inline)) -uint8_t macho_scattered_relocation_info::r_type() const { - uint32_t temp = ENDIAN_READ32(*((const uint32_t*)&content)); - return (temp >> 24) & 0x0F; -} - -inline __attribute__((always_inline)) -void macho_scattered_relocation_info::set_r_type(uint8_t _value) { - uint32_t temp = ENDIAN_READ32(*((const uint32_t*)&content)); - temp &= 0xF0FFFFFF; - temp |= ((_value &0x0F) << 24); - ENDIAN_WRITE32(*((uint32_t*)&content), temp); -} - -inline __attribute__((always_inline)) -uint32_t macho_scattered_relocation_info::r_address() const { - uint32_t temp = ENDIAN_READ32(*((const uint32_t*)&content)); - return temp & 0x00FFFFFF; -} - -inline __attribute__((always_inline)) -void macho_scattered_relocation_info::set_r_address(uint32_t _value) { - uint32_t temp = ENDIAN_READ32(*((const uint32_t*)&content)); - _value &= 0x00FFFFFF; - temp &= 0xFF000000; - temp |= _value; - ENDIAN_WRITE32(*((uint32_t*)&content), temp); -} - -inline __attribute__((always_inline)) -int32_t macho_scattered_relocation_info::r_value() const { - return ENDIAN_READ32(content.r_value); -} - -inline __attribute__((always_inline)) -void macho_scattered_relocation_info::set_r_value(int32_t _value) { - ENDIAN_WRITE32(content.r_value, _value); -} - - - - diff --git a/src/MachOFileAbstraction.hpp b/src/MachOFileAbstraction.hpp new file mode 100644 index 0000000..6110e80 --- /dev/null +++ b/src/MachOFileAbstraction.hpp @@ -0,0 +1,712 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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@ +*/ +#ifndef __MACH_O_FILE_ABSTRACTION__ +#define __MACH_O_FILE_ABSTRACTION__ + +#include +#include +#include +#include + +// suport older versions of mach-o/loader.h +#ifndef LC_UUID +#define LC_UUID 0x1b +struct uuid_command { + uint32_t cmd; /* LC_UUID */ + uint32_t cmdsize; /* sizeof(struct uuid_command) */ + uint8_t uuid[16]; /* the 128-bit uuid */ +}; +#endif + + +#include "FileAbstraction.hpp" +#include "Architectures.hpp" + + + +// +// This abstraction layer makes every mach-o file look like a 64-bit mach-o file with native endianness +// + + + +// +// mach-o file header +// +template struct macho_header_content {}; +template <> struct macho_header_content > { mach_header fields; }; +template <> struct macho_header_content > { mach_header_64 fields; }; +template <> struct macho_header_content > { mach_header fields; }; +template <> struct macho_header_content > { mach_header_64 fields; }; + +template +class macho_header { +public: + uint32_t magic() const INLINE { return E::get32(header.fields.magic); } + void set_magic(uint32_t value) INLINE { E::set32(header.fields.magic, value); } + + uint32_t cputype() const INLINE { return E::get32(header.fields.cputype); } + void set_cputype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cputype, value); } + + uint32_t cpusubtype() const INLINE { return E::get32(header.fields.cpusubtype); } + void set_cpusubtype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cpusubtype, value); } + + uint32_t filetype() const INLINE { return E::get32(header.fields.filetype); } + void set_filetype(uint32_t value) INLINE { E::set32(header.fields.filetype, value); } + + uint32_t ncmds() const INLINE { return E::get32(header.fields.ncmds); } + void set_ncmds(uint32_t value) INLINE { E::set32(header.fields.ncmds, value); } + + uint32_t sizeofcmds() const INLINE { return E::get32(header.fields.sizeofcmds); } + void set_sizeofcmds(uint32_t value) INLINE { E::set32(header.fields.sizeofcmds, value); } + + uint32_t flags() const INLINE { return E::get32(header.fields.flags); } + void set_flags(uint32_t value) INLINE { E::set32(header.fields.flags, value); } + + uint32_t reserved() const INLINE { return E::get32(header.fields.reserved); } + void set_reserved(uint32_t value) INLINE { E::set32(header.fields.reserved, value); } + + typedef typename P::E E; +private: + macho_header_content

header; +}; + + +// +// mach-o load command +// +template +class macho_load_command { +public: + uint32_t cmd() const INLINE { return E::get32(command.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(command.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(command.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(command.cmdsize, value); } + + typedef typename P::E E; +private: + load_command command; +}; + + +// +// mach-o segment load command +// +template struct macho_segment_content {}; +template <> struct macho_segment_content > { segment_command fields; enum { CMD = LC_SEGMENT }; }; +template <> struct macho_segment_content > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; }; +template <> struct macho_segment_content > { segment_command fields; enum { CMD = LC_SEGMENT }; }; +template <> struct macho_segment_content > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; }; + +template +class macho_segment_command { +public: + uint32_t cmd() const INLINE { return E::get32(segment.fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(segment.fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(segment.fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(segment.fields.cmdsize, value); } + + const char* segname() const INLINE { return segment.fields.segname; } + void set_segname(const char* value) INLINE { memcpy(&segment.fields.segname, value, 16); } + + uint64_t vmaddr() const INLINE { return P::getP(segment.fields.vmaddr); } + void set_vmaddr(uint64_t value) INLINE { P::setP(segment.fields.vmaddr, value); } + + uint64_t vmsize() const INLINE { return P::getP(segment.fields.vmsize); } + void set_vmsize(uint64_t value) INLINE { P::setP(segment.fields.vmsize, value); } + + uint64_t fileoff() const INLINE { return P::getP(segment.fields.fileoff); } + void set_fileoff(uint64_t value) INLINE { P::setP(segment.fields.fileoff, value); } + + uint64_t filesize() const INLINE { return P::getP(segment.fields.filesize); } + void set_filesize(uint64_t value) INLINE { P::setP(segment.fields.filesize, value); } + + uint32_t maxprot() const INLINE { return E::get32(segment.fields.maxprot); } + void set_maxprot(uint32_t value) INLINE { E::set32((uint32_t&)segment.fields.maxprot, value); } + + uint32_t initprot() const INLINE { return E::get32(segment.fields.initprot); } + void set_initprot(uint32_t value) INLINE { E::set32((uint32_t&)segment.fields.initprot, value); } + + uint32_t nsects() const INLINE { return E::get32(segment.fields.nsects); } + void set_nsects(uint32_t value) INLINE { E::set32(segment.fields.nsects, value); } + + uint32_t flags() const INLINE { return E::get32(segment.fields.flags); } + void set_flags(uint32_t value) INLINE { E::set32(segment.fields.flags, value); } + + enum { + CMD = macho_segment_content

::CMD + }; + + typedef typename P::E E; +private: + macho_segment_content

segment; +}; + + +// +// mach-o section +// +template struct macho_section_content {}; +template <> struct macho_section_content > { section fields; }; +template <> struct macho_section_content > { section_64 fields; }; +template <> struct macho_section_content > { section fields; }; +template <> struct macho_section_content > { section_64 fields; }; + +template +class macho_section { +public: + const char* sectname() const INLINE { return section.fields.sectname; } + void set_sectname(const char* value) INLINE { memcpy(§ion.fields.sectname, value, 16); } + + const char* segname() const INLINE { return section.fields.segname; } + void set_segname(const char* value) INLINE { memcpy(§ion.fields.segname, value, 16); } + + uint64_t addr() const INLINE { return P::getP(section.fields.addr); } + void set_addr(uint64_t value) INLINE { P::setP(section.fields.addr, value); } + + uint64_t size() const INLINE { return P::getP(section.fields.size); } + void set_size(uint64_t value) INLINE { P::setP(section.fields.size, value); } + + uint32_t offset() const INLINE { return E::get32(section.fields.offset); } + void set_offset(uint32_t value) INLINE { E::set32(section.fields.offset, value); } + + uint32_t align() const INLINE { return E::get32(section.fields.align); } + void set_align(uint32_t value) INLINE { E::set32(section.fields.align, value); } + + uint32_t reloff() const INLINE { return E::get32(section.fields.reloff); } + void set_reloff(uint32_t value) INLINE { E::set32(section.fields.reloff, value); } + + uint32_t nreloc() const INLINE { return E::get32(section.fields.nreloc); } + void set_nreloc(uint32_t value) INLINE { E::set32(section.fields.nreloc, value); } + + uint32_t flags() const INLINE { return E::get32(section.fields.flags); } + void set_flags(uint32_t value) INLINE { E::set32(section.fields.flags, value); } + + uint32_t reserved1() const INLINE { return E::get32(section.fields.reserved1); } + void set_reserved1(uint32_t value) INLINE { E::set32(section.fields.reserved1, value); } + + uint32_t reserved2() const INLINE { return E::get32(section.fields.reserved2); } + void set_reserved2(uint32_t value) INLINE { E::set32(section.fields.reserved2, value); } + + typedef typename P::E E; +private: + macho_section_content

section; +}; + + +// +// mach-o dylib load command +// +template +class macho_dylib_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t name_offset() const INLINE { return E::get32(fields.dylib.name.offset); } + void set_name_offset(uint32_t value) INLINE { E::set32(fields.dylib.name.offset, value); } + + uint32_t timestamp() const INLINE { return E::get32(fields.dylib.timestamp); } + void set_timestamp(uint32_t value) INLINE { E::set32(fields.dylib.timestamp, value); } + + uint32_t current_version() const INLINE { return E::get32(fields.dylib.current_version); } + void set_current_version(uint32_t value) INLINE { E::set32(fields.dylib.current_version, value); } + + uint32_t compatibility_version() const INLINE { return E::get32(fields.dylib.compatibility_version); } + void set_compatibility_version(uint32_t value) INLINE { E::set32(fields.dylib.compatibility_version, value); } + + const char* name() const INLINE { return (const char*)&fields + name_offset(); } + void set_name_offset() INLINE { set_name_offset(sizeof(fields)); } + + typedef typename P::E E; +private: + dylib_command fields; +}; + + +// +// mach-o dylinker load command +// +template +class macho_dylinker_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t name_offset() const INLINE { return E::get32(fields.name.offset); } + void set_name_offset(uint32_t value) INLINE { E::set32(fields.name.offset, value); } + + const char* name() const INLINE { return (const char*)&fields + name_offset(); } + void set_name_offset() INLINE { set_name_offset(sizeof(fields)); } + + typedef typename P::E E; +private: + dylinker_command fields; +}; + + +// +// mach-o sub_framework load command +// +template +class macho_sub_framework_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t umbrella_offset() const INLINE { return E::get32(fields.umbrella.offset); } + void set_umbrella_offset(uint32_t value) INLINE { E::set32(fields.umbrella.offset, value); } + + const char* umbrella() const INLINE { return (const char*)&fields + umbrella_offset(); } + void set_umbrella_offset() INLINE { set_umbrella_offset(sizeof(fields)); } + + typedef typename P::E E; +private: + sub_framework_command fields; +}; + + +// +// mach-o sub_client load command +// +template +class macho_sub_client_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t client_offset() const INLINE { return E::get32(fields.client.offset); } + void set_client_offset(uint32_t value) INLINE { E::set32(fields.client.offset, value); } + + const char* client() const INLINE { return (const char*)&fields + client_offset(); } + void set_client_offset() INLINE { set_client_offset(sizeof(fields)); } + + typedef typename P::E E; +private: + sub_client_command fields; +}; + + +// +// mach-o sub_umbrella load command +// +template +class macho_sub_umbrella_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t sub_umbrella_offset() const INLINE { return E::get32(fields.sub_umbrella.offset); } + void set_sub_umbrella_offset(uint32_t value) INLINE { E::set32(fields.sub_umbrella.offset, value); } + + const char* sub_umbrella() const INLINE { return (const char*)&fields + sub_umbrella_offset(); } + void set_sub_umbrella_offset() INLINE { set_sub_umbrella_offset(sizeof(fields)); } + + typedef typename P::E E; +private: + sub_umbrella_command fields; +}; + + +// +// mach-o sub_library load command +// +template +class macho_sub_library_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t sub_library_offset() const INLINE { return E::get32(fields.sub_library.offset); } + void set_sub_library_offset(uint32_t value) INLINE { E::set32(fields.sub_library.offset, value); } + + const char* sub_library() const INLINE { return (const char*)&fields + sub_library_offset(); } + void set_sub_library_offset() INLINE { set_sub_library_offset(sizeof(fields)); } + + typedef typename P::E E; +private: + sub_library_command fields; +}; + + +// +// mach-o uuid load command +// +template +class macho_uuid_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + const uint8_t* uuid() const INLINE { return fields.uuid; } + void set_uuid(uint8_t uuid[16]) INLINE { memcpy(&fields.uuid, uuid, 16); } + + typedef typename P::E E; +private: + uuid_command fields; +}; + + +// +// mach-o routines load command +// +template struct macho_routines_content {}; +template <> struct macho_routines_content > { routines_command fields; enum { CMD = LC_ROUTINES }; }; +template <> struct macho_routines_content > { routines_command_64 fields; enum { CMD = LC_ROUTINES_64 }; }; +template <> struct macho_routines_content > { routines_command fields; enum { CMD = LC_ROUTINES }; }; +template <> struct macho_routines_content > { routines_command_64 fields; enum { CMD = LC_ROUTINES_64 }; }; + +template +class macho_routines_command { +public: + uint32_t cmd() const INLINE { return E::get32(routines.fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(routines.fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(routines.fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(routines.fields.cmdsize, value); } + + uint64_t init_address() const INLINE { return P::getP(routines.fields.init_address); } + void set_init_address(uint64_t value) INLINE { P::setP(routines.fields.init_address, value); } + + uint64_t init_module() const INLINE { return P::getP(routines.fields.init_module); } + void set_init_module(uint64_t value) INLINE { P::setP(routines.fields.init_module, value); } + + uint64_t reserved1() const INLINE { return P::getP(routines.fields.reserved1); } + void set_reserved1(uint64_t value) INLINE { P::setP(routines.fields.reserved1, value); } + + uint64_t reserved2() const INLINE { return P::getP(routines.fields.reserved2); } + void set_reserved2(uint64_t value) INLINE { P::setP(routines.fields.reserved2, value); } + + uint64_t reserved3() const INLINE { return P::getP(routines.fields.reserved3); } + void set_reserved3(uint64_t value) INLINE { P::setP(routines.fields.reserved3, value); } + + uint64_t reserved4() const INLINE { return P::getP(routines.fields.reserved4); } + void set_reserved4(uint64_t value) INLINE { P::setP(routines.fields.reserved4, value); } + + uint64_t reserved5() const INLINE { return P::getP(routines.fields.reserved5); } + void set_reserved5(uint64_t value) INLINE { P::setP(routines.fields.reserved5, value); } + + uint64_t reserved6() const INLINE { return P::getP(routines.fields.reserved6); } + void set_reserved6(uint64_t value) INLINE { P::setP(routines.fields.reserved6, value); } + + typedef typename P::E E; + enum { + CMD = macho_routines_content

::CMD + }; +private: + macho_routines_content

routines; +}; + + +// +// mach-o symbol table load command +// +template +class macho_symtab_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t symoff() const INLINE { return E::get32(fields.symoff); } + void set_symoff(uint32_t value) INLINE { E::set32(fields.symoff, value); } + + uint32_t nsyms() const INLINE { return E::get32(fields.nsyms); } + void set_nsyms(uint32_t value) INLINE { E::set32(fields.nsyms, value); } + + uint32_t stroff() const INLINE { return E::get32(fields.stroff); } + void set_stroff(uint32_t value) INLINE { E::set32(fields.stroff, value); } + + uint32_t strsize() const INLINE { return E::get32(fields.strsize); } + void set_strsize(uint32_t value) INLINE { E::set32(fields.strsize, value); } + + + typedef typename P::E E; +private: + symtab_command fields; +}; + + +// +// mach-o dynamic symbol table load command +// +template +class macho_dysymtab_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t ilocalsym() const INLINE { return E::get32(fields.ilocalsym); } + void set_ilocalsym(uint32_t value) INLINE { E::set32(fields.ilocalsym, value); } + + uint32_t nlocalsym() const INLINE { return E::get32(fields.nlocalsym); } + void set_nlocalsym(uint32_t value) INLINE { E::set32(fields.nlocalsym, value); } + + uint32_t iextdefsym() const INLINE { return E::get32(fields.iextdefsym); } + void set_iextdefsym(uint32_t value) INLINE { E::set32(fields.iextdefsym, value); } + + uint32_t nextdefsym() const INLINE { return E::get32(fields.nextdefsym); } + void set_nextdefsym(uint32_t value) INLINE { E::set32(fields.nextdefsym, value); } + + uint32_t iundefsym() const INLINE { return E::get32(fields.iundefsym); } + void set_iundefsym(uint32_t value) INLINE { E::set32(fields.iundefsym, value); } + + uint32_t nundefsym() const INLINE { return E::get32(fields.nundefsym); } + void set_nundefsym(uint32_t value) INLINE { E::set32(fields.nundefsym, value); } + + uint32_t tocoff() const INLINE { return E::get32(fields.tocoff); } + void set_tocoff(uint32_t value) INLINE { E::set32(fields.tocoff, value); } + + uint32_t ntoc() const INLINE { return E::get32(fields.ntoc); } + void set_ntoc(uint32_t value) INLINE { E::set32(fields.ntoc, value); } + + uint32_t modtaboff() const INLINE { return E::get32(fields.modtaboff); } + void set_modtaboff(uint32_t value) INLINE { E::set32(fields.modtaboff, value); } + + uint32_t nmodtab() const INLINE { return E::get32(fields.nmodtab); } + void set_nmodtab(uint32_t value) INLINE { E::set32(fields.nmodtab, value); } + + uint32_t extrefsymoff() const INLINE { return E::get32(fields.extrefsymoff); } + void set_extrefsymoff(uint32_t value) INLINE { E::set32(fields.extrefsymoff, value); } + + uint32_t nextrefsyms() const INLINE { return E::get32(fields.nextrefsyms); } + void set_nextrefsyms(uint32_t value) INLINE { E::set32(fields.nextrefsyms, value); } + + uint32_t indirectsymoff() const INLINE { return E::get32(fields.indirectsymoff); } + void set_indirectsymoff(uint32_t value) INLINE { E::set32(fields.indirectsymoff, value); } + + uint32_t nindirectsyms() const INLINE { return E::get32(fields.nindirectsyms); } + void set_nindirectsyms(uint32_t value) INLINE { E::set32(fields.nindirectsyms, value); } + + uint32_t extreloff() const INLINE { return E::get32(fields.extreloff); } + void set_extreloff(uint32_t value) INLINE { E::set32(fields.extreloff, value); } + + uint32_t nextrel() const INLINE { return E::get32(fields.nextrel); } + void set_nextrel(uint32_t value) INLINE { E::set32(fields.nextrel, value); } + + uint32_t locreloff() const INLINE { return E::get32(fields.locreloff); } + void set_locreloff(uint32_t value) INLINE { E::set32(fields.locreloff, value); } + + uint32_t nlocrel() const INLINE { return E::get32(fields.nlocrel); } + void set_nlocrel(uint32_t value) INLINE { E::set32(fields.nlocrel, value); } + + typedef typename P::E E; +private: + dysymtab_command fields; +}; + + +// +// mach-o two-level hints load command +// +template +class macho_twolevel_hints_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t offset() const INLINE { return E::get32(fields.offset); } + void set_offset(uint32_t value) INLINE { E::set32(fields.offset, value); } + + uint32_t nhints() const INLINE { return E::get32(fields.nhints); } + void set_nhints(uint32_t value) INLINE { E::set32(fields.nhints, value); } + + typedef typename P::E E; +private: + twolevel_hints_command fields; +}; + + +// +// mach-o threads load command +// +template +class macho_thread_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t flavor() const INLINE { return E::get32(fields_flavor); } + void set_flavor(uint32_t value) INLINE { E::set32(fields_flavor, value); } + + uint32_t count() const INLINE { return E::get32(fields_count); } + void set_count(uint32_t value) INLINE { E::set32(fields_count, value); } + + uint64_t thread_register(uint32_t index) const INLINE { return P::getP(thread_registers[index]); } + void set_thread_register(uint32_t index, uint64_t value) INLINE { P::setP(thread_registers[index], value); } + + typedef typename P::E E; + typedef typename P::uint_t pint_t; +private: + struct thread_command fields; + uint32_t fields_flavor; + uint32_t fields_count; + pint_t thread_registers[1]; +}; + + + + +// +// mach-o symbol table entry +// +template struct macho_nlist_content {}; +template <> struct macho_nlist_content > { struct nlist fields; }; +template <> struct macho_nlist_content > { struct nlist_64 fields; }; +template <> struct macho_nlist_content > { struct nlist fields; }; +template <> struct macho_nlist_content > { struct nlist_64 fields; }; + +template +class macho_nlist { +public: + uint32_t n_strx() const INLINE { return E::get32(entry.fields.n_un.n_strx); } + void set_n_strx(uint32_t value) INLINE { E::set32((uint32_t&)entry.fields.n_un.n_strx, value); } + + uint8_t n_type() const INLINE { return entry.fields.n_type; } + void set_n_type(uint8_t value) INLINE { entry.fields.n_type = value; } + + uint8_t n_sect() const INLINE { return entry.fields.n_sect; } + void set_n_sect(uint8_t value) INLINE { entry.fields.n_sect = value; } + + uint16_t n_desc() const INLINE { return E::get16(entry.fields.n_desc); } + void set_n_desc(uint16_t value) INLINE { E::set16((uint16_t&)entry.fields.n_desc, value); } + + uint64_t n_value() const INLINE { return P::getP(entry.fields.n_value); } + void set_n_value(uint64_t value) INLINE { P::setP(entry.fields.n_value, value); } + + typedef typename P::E E; +private: + macho_nlist_content

entry; +}; + + + +// +// mach-o relocation info +// +template +class macho_relocation_info { +public: + uint32_t r_address() const INLINE { return E::get32(address); } + void set_r_address(uint32_t value) INLINE { E::set32(address, value); } + + uint32_t r_symbolnum() const INLINE { return E::getBits(other, 0, 24); } + void set_r_symbolnum(uint32_t value) INLINE { E::setBits(other, value, 0, 24); } + + bool r_pcrel() const INLINE { return E::getBits(other, 24, 1); } + void set_r_pcrel(bool value) INLINE { E::setBits(other, value, 24, 1); } + + uint8_t r_length() const INLINE { return E::getBits(other, 25, 2); } + void set_r_length(uint8_t value) INLINE { E::setBits(other, value, 25, 2); } + + bool r_extern() const INLINE { return E::getBits(other, 27, 1); } + void set_r_extern(bool value) INLINE { E::setBits(other, value, 27, 1); } + + uint8_t r_type() const INLINE { return E::getBits(other, 28, 4); } + void set_r_type(uint8_t value) INLINE { E::setBits(other, value, 28, 4); } + + void set_r_length() INLINE { set_r_length((sizeof(typename P::uint_t)==8) ? 3 : 2); } + + typedef typename P::E E; +private: + uint32_t address; + uint32_t other; +}; + + +// +// mach-o scattered relocation info +// The bit fields are always in big-endian order (see mach-o/reloc.h) +// +template +class macho_scattered_relocation_info { +public: + bool r_scattered() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 0, 1); } + void set_r_scattered(bool x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 0, 1); E::set32(other, temp); } + + bool r_pcrel() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 1, 1); } + void set_r_pcrel(bool x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 1, 1); E::set32(other, temp); } + + uint8_t r_length() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 2, 2); } + void set_r_length(uint8_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 2, 2); E::set32(other, temp); } + + uint8_t r_type() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 4, 4); } + void set_r_type(uint8_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 4, 4); E::set32(other, temp); } + + uint32_t r_address() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 8, 24); } + void set_r_address(uint32_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 8, 24); E::set32(other, temp); } + + uint32_t r_value() const INLINE { return E::get32(value); } + void set_r_value(uint32_t x) INLINE { E::set32(value, x); } + + uint32_t r_other() const INLINE { return other; } + + typedef typename P::E E; +private: + uint32_t other; + uint32_t value; +}; + + + + + + +#endif // __MACH_O_FILE_ABSTRACTION__ + + diff --git a/src/MachOReaderArchive.hpp b/src/MachOReaderArchive.hpp new file mode 100644 index 0000000..d40ca70 --- /dev/null +++ b/src/MachOReaderArchive.hpp @@ -0,0 +1,408 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __OBJECT_FILE_ARCHIVE_MACH_O__ +#define __OBJECT_FILE_ARCHIVE_MACH_O__ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "MachOFileAbstraction.hpp" +#include "ObjectFile.h" +#include "MachOReaderRelocatable.hpp" + + +namespace mach_o { +namespace archive { + +typedef const struct ranlib* ConstRanLibPtr; + +template +class Reader : public ObjectFile::Reader +{ +public: + static bool validFile(const uint8_t* fileContent, uint64_t fileLength); + static Reader* make(const uint8_t* fileContent, uint64_t fileLength, const char* path, + time_t modTime, const ObjectFile::ReaderOptions& options) + { return new Reader(fileContent, fileLength, path, modTime, options); } + virtual ~Reader() {} + + virtual const char* getPath() { return fPath; } + virtual time_t getModificationTime(){ return 0; } + virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; } + virtual std::vector& getAtoms(); + virtual std::vector* getJustInTimeAtomsFor(const char* name); + virtual std::vector* getStabs() { return NULL; } + +private: + class Entry : ar_hdr + { + public: + const char* getName() const; + time_t getModTime() const; + const uint8_t* getContent() const; + uint32_t getContentSize() const; + const Entry* getNext() const; + private: + bool hasLongName() const; + unsigned int getLongNameSpace() const; + + }; + + class CStringEquals + { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + typedef __gnu_cxx::hash_map, CStringEquals> NameToEntryMap; + + typedef typename A::P P; + typedef typename A::P::E E; + + Reader(const uint8_t fileContent[], uint64_t fileLength, + const char* path, time_t modTime, const ObjectFile::ReaderOptions& options); + const struct ranlib* ranlibBinarySearch(const char* name); + const struct ranlib* ranlibLinearSearch(const char* name); + const struct ranlib* ranlibHashSearch(const char* name); + ObjectFile::Reader* makeObjectReaderForMember(const Entry* member); + void dumpTableOfContents(); + void buildHashTable(); + + const char* fPath; + time_t fModTime; + const ObjectFile::ReaderOptions& fOptions; + const uint8_t* fFileContent; + uint64_t fFileLength; + const struct ranlib* fTableOfContents; + uint32_t fTableOfContentCount; + const char* fStringPool; + std::vector fAllAtoms; + std::set fInstantiatedEntries; + std::set fPossibleEntries; + NameToEntryMap fHashTable; + + static std::vector fgEmptyList; +}; + +template +std::vector Reader::fgEmptyList; + + +template +bool Reader::Entry::hasLongName() const +{ + return ( strncmp(this->ar_name, AR_EFMT1, strlen(AR_EFMT1)) == 0 ); +} + +template +unsigned int Reader::Entry::getLongNameSpace() const +{ + char* endptr; + long result = strtol(&this->ar_name[strlen(AR_EFMT1)], &endptr, 10); + return result; +} + +template +const char* Reader::Entry::getName() const +{ + if ( this->hasLongName() ) { + int len = this->getLongNameSpace(); + static char longName[256]; + strncpy(longName, ((char*)this)+sizeof(ar_hdr), len); + longName[len] = '\0'; + return longName; + } + else { + static char shortName[20]; + strncpy(shortName, this->ar_name, 16); + shortName[16] = '\0'; + char* space = strchr(shortName, ' '); + if ( space != NULL ) + *space = '\0'; + return shortName; + } +} + +template +time_t Reader::Entry::getModTime() const +{ + char temp[14]; + strncpy(temp, this->ar_size, 12); + temp[12] = '\0'; + char* endptr; + return (time_t)strtol(temp, &endptr, 12); +} + + +template +const uint8_t* Reader::Entry::getContent() const +{ + if ( this->hasLongName() ) + return ((uint8_t*)this) + sizeof(ar_hdr) + this->getLongNameSpace(); + else + return ((uint8_t*)this) + sizeof(ar_hdr); +} + + +template +uint32_t Reader::Entry::getContentSize() const +{ + char temp[12]; + strncpy(temp, this->ar_size, 10); + temp[10] = '\0'; + char* endptr; + long size = strtol(temp, &endptr, 10); + // long name is included in ar_size + if ( this->hasLongName() ) + size -= this->getLongNameSpace(); + return size; +} + + +template +const class Reader::Entry* Reader::Entry::getNext() const +{ + const uint8_t* p = this->getContent() + getContentSize(); + p = (const uint8_t*)(((uintptr_t)p+3) & (-4)); // 4-byte align + return (class Reader::Entry*)p; +} + +template +bool Reader::validFile(const uint8_t* fileContent, uint64_t fileLength) +{ + // must have valid archive header + if ( strncmp((const char*)fileContent, "!\n", 8) != 0 ) + return false; + + // peak at first .o file and verify it is correct architecture + const Entry* const start = (Entry*)&fileContent[8]; + const Entry* const end = (Entry*)&fileContent[fileLength]; + for (const Entry* p=start; p < end; p = p->getNext()) { + const char* memberName = p->getName(); + // skip option table-of-content member + if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) ) + continue; + // archive is valid if first .o file is valid + return mach_o::relocatable::Reader::validFile(p->getContent()); + } + // empty archive + return true; +} + +template +Reader::Reader(const uint8_t fileContent[], uint64_t fileLength, const char* path, time_t modTime, const ObjectFile::ReaderOptions& options) + : fPath(NULL), fModTime(modTime), fOptions(options), fFileContent(NULL), fTableOfContents(NULL), fTableOfContentCount(0), + fStringPool(NULL) +{ + fPath = strdup(path); + fFileContent = fileContent; + fFileLength = fileLength; + + if ( strncmp((const char*)fileContent, "!\n", 8) != 0 ) + throw "not an archive"; + + if ( !options.fFullyLoadArchives ) { + const Entry* const firstMember = (Entry*)&fFileContent[8]; + if ( (strcmp(firstMember->getName(), SYMDEF_SORTED) == 0) || (strcmp(firstMember->getName(), SYMDEF) == 0) ) { + const uint8_t* contents = firstMember->getContent(); + uint32_t ranlibArrayLen = E::get32(*((uint32_t*)contents)); + fTableOfContents = (const struct ranlib*)&contents[4]; + fTableOfContentCount = ranlibArrayLen / sizeof(struct ranlib); + fStringPool = (const char*)&contents[ranlibArrayLen+8]; + if ( ((uint8_t*)(&fTableOfContents[fTableOfContentCount]) > &fileContent[fileLength]) + || ((uint8_t*)fStringPool > &fileContent[fileLength]) ) + throw "malformed archive, perhaps wrong architecture"; + this->buildHashTable(); + } + else + throw "archive has no table of contents"; + } +} + + +template +ObjectFile::Reader* Reader::makeObjectReaderForMember(const Entry* member) +{ + const char* memberName = member->getName(); + char memberPath[strlen(fPath) + strlen(memberName)+4]; + strcpy(memberPath, fPath); + strcat(memberPath, "("); + strcat(memberPath, memberName); + strcat(memberPath, ")"); + //fprintf(stderr, "using %s from %s\n", memberName, fPath); + try { + ObjectFile::Reader* obj = mach_o::relocatable::Reader::make(member->getContent(), memberPath, fModTime, fOptions); + unsigned int objIndex = 0; + for (class std::set::iterator it=fPossibleEntries.begin(); it != fPossibleEntries.end(); it++, objIndex++) { + if ( *it == member ) + break; + } + obj->setSortOrder((fSortOrder<<16) + objIndex); + //fprintf(stderr, "%s order = 0x%08X, index=%u\n", memberPath, obj->getSortOrder(), objIndex); + return obj; + } + catch (const char* msg) { + throwf("in %s, %s", memberPath, msg); + } +} + +template +std::vector& Reader::getAtoms() +{ + if ( fOptions.fFullyLoadArchives ) { + // build vector of all atoms from all .o files in this archive + const Entry* const start = (Entry*)&fFileContent[8]; + const Entry* const end = (Entry*)&fFileContent[fFileLength]; + unsigned int objIndex = 0; + for (const Entry* p=start; p < end; p = p->getNext()) { + const char* memberName = p->getName(); + if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) ) + continue; + ObjectFile::Reader* r = this->makeObjectReaderForMember(p); + r->setSortOrder((fSortOrder<<16) + objIndex++); + std::vector& atoms = r->getAtoms(); + fAllAtoms.insert(fAllAtoms.end(), atoms.begin(), atoms.end()); + } + return fAllAtoms; + } + else { + // return nonthing for now, getJustInTimeAtomsFor() will return atoms as needed + return fgEmptyList; + } +} + + +template +ConstRanLibPtr Reader::ranlibBinarySearch(const char* key) +{ + const struct ranlib* base = fTableOfContents; + for (uint32_t n = fTableOfContentCount; n > 0; n /= 2) { + const struct ranlib* pivot = &base[n/2]; + const char* pivotStr = &fStringPool[E::get32(pivot->ran_un.ran_strx)]; + int cmp = strcmp(key, pivotStr); + if ( cmp == 0 ) + return pivot; + if ( cmp > 0 ) { + // key > pivot + // move base to symbol after pivot + base = &pivot[1]; + --n; + } + else { + // key < pivot + // keep same base + } + } + return NULL; +} + +template +ConstRanLibPtr Reader::ranlibLinearSearch(const char* key) +{ + for (uint32_t i = 0; i < fTableOfContentCount; ++i) { + const struct ranlib* entry = &fTableOfContents[i]; + const char* entryName = &fStringPool[E::get32(entry->ran_un.ran_strx)]; + if ( strcmp(key, entryName) == 0 ) + return entry; + } + return NULL; +} + +template +ConstRanLibPtr Reader::ranlibHashSearch(const char* name) +{ + class NameToEntryMap::iterator pos = fHashTable.find(name); + if ( pos != fHashTable.end() ) + return pos->second; + else + return NULL; +} + +template +void Reader::buildHashTable() +{ + // walk through list backwards, adding/overwriting entries + // this assures that with duplicates those earliest in the list will be found + for (int i = fTableOfContentCount-1; i >= 0; --i) { + const struct ranlib* entry = &fTableOfContents[i]; + const char* entryName = &fStringPool[E::get32(entry->ran_un.ran_strx)]; + const Entry* member = (Entry*)&fFileContent[E::get32(entry->ran_off)]; + //fprintf(stderr, "adding hash %d, %s -> %p\n", i, entryName, entry); + fHashTable[entryName] = entry; + fPossibleEntries.insert(member); + } +} + +template +void Reader::dumpTableOfContents() +{ + for (unsigned int i=0; i < fTableOfContentCount; ++i) { + const struct ranlib* e = &fTableOfContents[i]; + printf("%s in %s\n", &fStringPool[E::get32(e->ran_un.ran_strx)], ((Entry*)&fFileContent[E::get32(e->ran_off)])->getName()); + } +} + +template +std::vector* Reader::getJustInTimeAtomsFor(const char* name) +{ + if ( fOptions.fFullyLoadArchives ) { + return NULL; + } + else { + const struct ranlib* result = NULL; + // do a hash search of table of contents looking for requested symbol + result = ranlibHashSearch(name); + if ( result != NULL ) { + const Entry* member = (Entry*)&fFileContent[E::get32(result->ran_off)]; + if ( fInstantiatedEntries.count(member) == 0 ) { + // only return these atoms once + fInstantiatedEntries.insert(member); + ObjectFile::Reader* r = makeObjectReaderForMember(member); + //fprintf(stderr, "%s found in %s(%s)\n", name, this->getPath(), member->getName()); + return new std::vector(r->getAtoms()); + } + } + //fprintf(stderr, "%s NOT found in archive %s\n", name, fPath); + return NULL; + } +} + + + + + +}; // namespace archive +}; // namespace mach_o + + +#endif // __OBJECT_FILE_ARCHIVE_MACH_O__ diff --git a/src/MachOReaderDylib.hpp b/src/MachOReaderDylib.hpp new file mode 100644 index 0000000..e9ff931 --- /dev/null +++ b/src/MachOReaderDylib.hpp @@ -0,0 +1,470 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __OBJECT_FILE_DYLIB_MACH_O__ +#define __OBJECT_FILE_DYLIB_MACH_O__ + +#include +#include +#include +#include + + +#include +#include +#include +#include + +#include "MachOFileAbstraction.hpp" +#include "ObjectFile.h" + +// +// +// To implement architecture xxx, you must write template specializations for the following method: +// Reader::validFile() +// +// + + + + +namespace mach_o { +namespace dylib { + + +// forward reference +template class Reader; + + +class Segment : public ObjectFile::Segment +{ +public: + Segment(const char* name) { fName = name; } + virtual const char* getName() const { return fName; } + virtual bool isContentReadable() const { return true; } + virtual bool isContentWritable() const { return false; } + virtual bool isContentExecutable() const { return false; } +private: + const char* fName; +}; + + +// +// An ExportAtom has no content. It exists so that the linker can track which imported +// symbols can from which dynamic libraries. +// +template +class ExportAtom : public ObjectFile::Atom +{ +public: + virtual ObjectFile::Reader* getFile() const { return &fOwner; } + virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; } + virtual const char* getName() const { return fName; } + virtual const char* getDisplayName() const { return fName; } + virtual Scope getScope() const { return ObjectFile::Atom::scopeGlobal; } + virtual DefinitionKind getDefinitionKind() const { return fWeakDefinition ? kExternalWeakDefinition : kExternalDefinition; } + virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableIn; } + virtual bool isZeroFill() const { return false; } + virtual uint64_t getSize() const { return 0; } + virtual std::vector& getReferences() const { return fgEmptyReferenceList; } + virtual bool mustRemainInSection() const { return false; } + virtual const char* getSectionName() const { return "._imports"; } + virtual Segment& getSegment() const { return fgImportSegment; } + virtual bool requiresFollowOnAtom() const{ return false; } + virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } + virtual std::vector* getLineInfo() const { return NULL; } + virtual uint8_t getAlignment() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const {} + + virtual void setScope(Scope) { } + +protected: + friend class Reader; + typedef typename A::P P; + + ExportAtom(ObjectFile::Reader& owner, const char* name, bool weak) + : fOwner(owner), fName(name), fWeakDefinition(weak) {} + virtual ~ExportAtom() {} + + ObjectFile::Reader& fOwner; + const char* fName; + bool fWeakDefinition; + + static std::vector fgEmptyReferenceList; + static Segment fgImportSegment; +}; + +template +Segment ExportAtom::fgImportSegment("__LINKEDIT"); + +template +std::vector ExportAtom::fgEmptyReferenceList; + + +// +// The reader for a dylib extracts all exported symbols names from the memory-mapped +// dylib, builds a hash table, then unmaps the file. This is an important memory +// savings for large dylibs. +// +template +class Reader : public ObjectFile::Reader +{ +public: + static bool validFile(const uint8_t* fileContent); + static Reader* make(const uint8_t* fileContent, uint64_t fileLength, const char* path, const ObjectFile::ReaderOptions& options) + { return new Reader(fileContent, fileLength, path, options); } + virtual ~Reader() {} + + virtual const char* getPath() { return fPath; } + virtual time_t getModificationTime() { return 0; } + virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; } + virtual std::vector& getAtoms(); + virtual std::vector* getJustInTimeAtomsFor(const char* name); + virtual std::vector* getStabs() { return NULL; } + virtual const char* getInstallPath() { return fDylibInstallPath; } + virtual uint32_t getTimestamp() { return fDylibTimeStamp; } + virtual uint32_t getCurrentVersion() { return fDylibtCurrentVersion; } + virtual uint32_t getCompatibilityVersion() { return fDylibCompatibilityVersion; } + virtual std::vector* getDependentLibraryPaths(); + virtual bool reExports(ObjectFile::Reader*); + virtual std::vector* getAllowableClients(); + +protected: + const char* parentUmbrella() { return fParentUmbrella; } + +private: + typedef typename A::P P; + typedef typename A::P::E E; + + class CStringEquals + { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + struct AtomAndWeak { ObjectFile::Atom* atom; bool weak; }; + typedef __gnu_cxx::hash_map, CStringEquals> NameToAtomMap; + typedef typename NameToAtomMap::iterator NameToAtomMapIterator; + + struct PathAndFlag { const char* path; bool reExport; }; + + Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, const ObjectFile::ReaderOptions& options); + + const char* fPath; + const char* fParentUmbrella; + std::vector fAllowableClients; + const char* fDylibInstallPath; + uint32_t fDylibTimeStamp; + uint32_t fDylibtCurrentVersion; + uint32_t fDylibCompatibilityVersion; + std::vector fDependentLibraryPaths; + NameToAtomMap fAtoms; + + static bool fgLogHashtable; + static std::vector fgEmptyAtomList; +}; + +template +std::vector Reader::fgEmptyAtomList; +template +bool Reader::fgLogHashtable = false; + + +template +Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, const ObjectFile::ReaderOptions& options) + : fParentUmbrella(NULL), fDylibInstallPath(NULL), fDylibTimeStamp(0), fDylibtCurrentVersion(0), fDylibCompatibilityVersion(0) +{ + // sanity check + if ( ! validFile(fileContent) ) + throw "not a valid mach-o object file"; + + fPath = strdup(path); + + const macho_header

* header = (const macho_header

*)fileContent; + const uint32_t cmd_count = header->ncmds(); + const macho_load_command

* const cmds = (macho_load_command

*)((char*)header + sizeof(macho_header

)); + + // a "blank" stub has zero load commands + if ( (header->filetype() == MH_DYLIB_STUB) && (cmd_count == 0) ) { + // no further processing needed + munmap((caddr_t)fileContent, fileLength); + return; + } + + // pass 1 builds list of all dependent libraries + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + PathAndFlag entry; + entry.path = strdup(((struct macho_dylib_command

*)cmd)->name()); + entry.reExport = false; + fDependentLibraryPaths.push_back(entry); + break; + } + cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); + } + + // pass 2 determines re-export info + const macho_dysymtab_command

* dynamicInfo = NULL; + const macho_nlist

* symbolTable = NULL; + const char* strings = NULL; + cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_SYMTAB: + { + const macho_symtab_command

* symtab = (macho_symtab_command

*)cmd; + symbolTable = (const macho_nlist

*)((char*)header + symtab->symoff()); + strings = (char*)header + symtab->stroff(); + } + break; + case LC_DYSYMTAB: + dynamicInfo = (macho_dysymtab_command

*)cmd; + break; + case LC_ID_DYLIB: + macho_dylib_command

* dylibID = (macho_dylib_command

*)cmd; + fDylibInstallPath = strdup(dylibID->name()); + fDylibTimeStamp = dylibID->timestamp(); + fDylibtCurrentVersion = dylibID->current_version(); + fDylibCompatibilityVersion = dylibID->compatibility_version(); + break; + case LC_SUB_UMBRELLA: + if ( !options.fFlatNamespace ) { + const char* frameworkLeafName = ((macho_sub_umbrella_command

*)cmd)->sub_umbrella(); + for (typename std::vector::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { + const char* dylibName = it->path; + const char* lastSlash = strrchr(dylibName, '/'); + if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], frameworkLeafName) == 0) ) + it->reExport = true; + } + } + break; + case LC_SUB_LIBRARY: + if ( !options.fFlatNamespace ) { + const char* dylibBaseName = ((macho_sub_library_command

*)cmd)->sub_library(); + for (typename std::vector::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { + const char* dylibName = it->path; + const char* lastSlash = strrchr(dylibName, '/'); + const char* leafStart = &lastSlash[1]; + if ( lastSlash == NULL ) + leafStart = dylibName; + const char* firstDot = strchr(leafStart, '.'); + int len = strlen(leafStart); + if ( firstDot != NULL ) + len = firstDot - leafStart; + if ( strncmp(leafStart, dylibBaseName, len) == 0 ) + it->reExport = true; + } + } + break; + case LC_SUB_FRAMEWORK: + fParentUmbrella = strdup(((macho_sub_framework_command

*)cmd)->umbrella()); + break; + } + + cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); + } + // Process the rest of the commands here. + cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_SUB_CLIENT: + const char *temp = strdup(((macho_sub_client_command

*)cmd)->client()); + + fAllowableClients.push_back(temp); + break; + } + + cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); + } + + // validate minimal load commands + if ( fDylibInstallPath == NULL ) + throw "dylib missing LC_ID_DYLIB load command"; + if ( symbolTable == NULL ) + throw "dylib missing LC_SYMTAB load command"; + if ( dynamicInfo == NULL ) + throw "dylib missing LC_DYSYMTAB load command"; + + // build hash table + if ( dynamicInfo->tocoff() == 0 ) { + if ( fgLogHashtable ) fprintf(stderr, "ld64: building hashtable of %u toc entries for %s\n", dynamicInfo->nextdefsym(), path); + const macho_nlist

* start = &symbolTable[dynamicInfo->iextdefsym()]; + const macho_nlist

* end = &start[dynamicInfo->nextdefsym()]; + fAtoms.resize(dynamicInfo->nextdefsym()); // set initial bucket count + for (const macho_nlist

* sym=start; sym < end; ++sym) { + AtomAndWeak bucket; + bucket.atom = NULL; + bucket.weak = ((sym->n_desc() & N_WEAK_DEF) != 0); + const char* name = strdup(&strings[sym->n_strx()]); + if ( fgLogHashtable ) fprintf(stderr, " adding %s to hash table for %s\n", name, this->getPath()); + fAtoms[name] = bucket; + } + } + else { + int32_t count = dynamicInfo->ntoc(); + fAtoms.resize(count); // set initial bucket count + if ( fgLogHashtable ) fprintf(stderr, "ld64: building hashtable of %u entries for %s\n", count, path); + const struct dylib_table_of_contents* toc = (dylib_table_of_contents*)((char*)header + dynamicInfo->tocoff()); + for (int32_t i = 0; i < count; ++i) { + const uint32_t index = E::get32(toc[i].symbol_index); + const macho_nlist

* sym = &symbolTable[index]; + AtomAndWeak bucket; + bucket.atom = NULL; + bucket.weak = ((sym->n_desc() & N_WEAK_DEF) != 0); + const char* name = strdup(&strings[sym->n_strx()]); + if ( fgLogHashtable ) fprintf(stderr, " adding %s to hash table for %s\n", name, this->getPath()); + fAtoms[name] = bucket; + } + } + + // unmap file + munmap((caddr_t)fileContent, fileLength); +} + +template +std::vector& Reader::getAtoms() +{ + // TO DO: for flat-namespace libraries, when linking flat_namespace + // we need to create an atom which references all undefines + return fgEmptyAtomList; +} + + +template +std::vector* Reader::getJustInTimeAtomsFor(const char* name) +{ + std::vector* atoms = NULL; + + NameToAtomMapIterator pos = fAtoms.find(name); + if ( pos != fAtoms.end() ) { + if ( pos->second.atom == NULL ) { + // instantiate atom and update hash table + pos->second.atom = new ExportAtom(*this, name, pos->second.weak); + if ( fgLogHashtable ) fprintf(stderr, "getJustInTimeAtomsFor: %s found in %s\n", name, this->getPath()); + } + // return a vector of one atom + atoms = new std::vector; + atoms->push_back(pos->second.atom); + } + else { + if ( fgLogHashtable ) fprintf(stderr, "getJustInTimeAtomsFor: %s NOT found in %s\n", name, this->getPath()); + } + return atoms; +} + + + +template +std::vector* Reader::getDependentLibraryPaths() +{ + std::vector* result = new std::vector; + for (typename std::vector::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { + result->push_back(it->path); + } + return result; +} + +template +std::vector* Reader::getAllowableClients() +{ + std::vector* result = new std::vector; + for (typename std::vector::iterator it = fAllowableClients.begin(); + it != fAllowableClients.end(); + it++) { + result->push_back(*it); + } + return (fAllowableClients.size() != 0 ? result : NULL); +} + +template +bool Reader::reExports(ObjectFile::Reader* child) +{ + // A dependent dylib is re-exported under two conditions: + // 1) parent contains LC_SUB_UMBRELLA or LC_SUB_LIBRARY with child name + for (typename std::vector::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { + if ( it->reExport && (strcmp(it->path, child->getPath()) == 0) ) + return true; + } + + // 2) child contains LC_SUB_FRAMEWORK with parent name + const char* parentUmbrellaName = ((Reader*)child)->parentUmbrella(); + if ( parentUmbrellaName != NULL ) { + const char* parentName = this->getPath(); + const char* lastSlash = strrchr(parentName, '/'); + if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], parentUmbrellaName) == 0) ) + return true; + } + + return false; +} + +template <> +bool Reader::validFile(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return false; + if ( header->cputype() != CPU_TYPE_POWERPC ) + return false; + if ( (header->filetype() != MH_DYLIB) && (header->filetype() != MH_DYLIB_STUB) ) + return false; + return true; +} + +template <> +bool Reader::validFile(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC_64 ) + return false; + if ( header->cputype() != CPU_TYPE_POWERPC64 ) + return false; + if ( (header->filetype() != MH_DYLIB) && (header->filetype() != MH_DYLIB_STUB) ) + return false; + return true; +} + +template <> +bool Reader::validFile(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return false; + if ( header->cputype() != CPU_TYPE_I386 ) + return false; + if ( (header->filetype() != MH_DYLIB) && (header->filetype() != MH_DYLIB_STUB) ) + return false; + return true; +} + + + + +}; // namespace dylib +}; // namespace mach_o + + +#endif // __OBJECT_FILE_DYLIB_MACH_O__ diff --git a/src/MachOReaderRelocatable.hpp b/src/MachOReaderRelocatable.hpp new file mode 100644 index 0000000..508be47 --- /dev/null +++ b/src/MachOReaderRelocatable.hpp @@ -0,0 +1,2663 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __OBJECT_FILE_MACH_O__ +#define __OBJECT_FILE_MACH_O__ + +#include +#include +#include +#include +#include +#include +#ifndef S_ATTR_DEBUG + #define S_ATTR_DEBUG 0x02000000 +#endif + +#include +#include +#include + +#include "MachOFileAbstraction.hpp" +#include "Architectures.hpp" +#include "ObjectFile.h" +#include "dwarf2.h" +#include "debugline.h" + + +// +// +// To implement architecture xxx, you must write template specializations for the following six methods: +// Reader::validFile() +// Reader::validSectionType() +// Reader::addRelocReference() +// Reference::getDescription() +// +// + + + +extern __attribute__((noreturn)) void throwf(const char* format, ...); + +namespace mach_o { +namespace relocatable { + + + +// forward reference +template class Reader; +template class SymbolAtomSorter; + +struct AtomAndOffset +{ + AtomAndOffset(ObjectFile::Atom* a=NULL) : atom(a), offset(0) {} + AtomAndOffset(ObjectFile::Atom* a, uint32_t off) : atom(a), offset(off) {} + ObjectFile::Atom* atom; + uint32_t offset; +}; + + +template +class Reference : public ObjectFile::Reference +{ +public: + typedef typename A::P P; + typedef typename A::P::uint_t pint_t; + typedef typename A::ReferenceKinds Kinds; + + Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& toTarget); + Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& fromTarget, const AtomAndOffset& toTarget); + Reference(Kinds kind, const AtomAndOffset& at, const char* toName, uint32_t toOffset); + + virtual ~Reference() {} + + + virtual bool isTargetUnbound() const { return ( fToTarget.atom == NULL ); } + virtual bool isFromTargetUnbound() const { return ( fFromTarget.atom == NULL ); } + virtual uint8_t getKind() const { return (uint8_t)fKind; } + virtual uint64_t getFixUpOffset() const { return fFixUpOffsetInSrc; } + virtual const char* getTargetName() const { return (fToTargetName != NULL) ? fToTargetName : fToTarget.atom->getName(); } + virtual ObjectFile::Atom& getTarget() const { return *fToTarget.atom; } + virtual uint64_t getTargetOffset() const { return fToTarget.offset; } + virtual bool hasFromTarget() const { return ( (fFromTarget.atom != NULL) || (fFromTargetName != NULL) ); } + virtual ObjectFile::Atom& getFromTarget() const { return *fFromTarget.atom; } + virtual const char* getFromTargetName() const { return (fFromTargetName != NULL) ? fFromTargetName : fFromTarget.atom->getName(); } + virtual void setTarget(ObjectFile::Atom& target, uint64_t offset) { fToTarget.atom = ⌖ fToTarget.offset = offset; } + virtual void setToTargetOffset(uint64_t offset) { fToTarget.offset = offset; } + virtual void setFromTarget(ObjectFile::Atom& target) { fFromTarget.atom = ⌖ } + virtual void setFromTargetName(const char* name) { fFromTargetName = name; } + virtual void setFromTargetOffset(uint64_t offset) { fFromTarget.offset = offset; } + virtual const char* getDescription() const; + virtual uint64_t getFromTargetOffset() const { return fFromTarget.offset; } + + +private: + pint_t fFixUpOffsetInSrc; + AtomAndOffset fToTarget; + AtomAndOffset fFromTarget; + const char* fToTargetName; + const char* fFromTargetName; + Kinds fKind; +}; + + +template +Reference::Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& toTarget) + : fFixUpOffsetInSrc(at.offset), fToTarget(toTarget), fToTargetName(NULL), fFromTargetName(NULL), + fKind(kind) +{ + // make reference a by-name where needed + if ( (kind != A::kNoFixUp) && (kind != A::kFollowOn) && (toTarget.atom->getScope() != ObjectFile::Atom::scopeTranslationUnit) ) { + fToTargetName = toTarget.atom->getName(); + //fprintf(stderr, "Reference(): changing to by-name %p %s, target scope=%d\n", toTarget.atom, fToTargetName, toTarget.atom->getScope()); + fToTarget.atom = NULL; + } + ((class BaseAtom*)at.atom)->addReference(this); + //fprintf(stderr, "Reference(): %p fToTarget<%s, %08X>\n", this, (fToTarget.atom != NULL) ? fToTarget.atom->getDisplayName() : fToTargetName , fToTarget.offset); +} + +template +Reference::Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& fromTarget, const AtomAndOffset& toTarget) + : fFixUpOffsetInSrc(at.offset), fToTarget(toTarget), fFromTarget(fromTarget), + fToTargetName(NULL), fFromTargetName(NULL), fKind(kind) +{ + // make reference a by-name where needed + if ( (kind != A::kNoFixUp) && (kind != A::kFollowOn) + && (toTarget.atom->getScope() != ObjectFile::Atom::scopeTranslationUnit) + && (toTarget.atom != at.atom) ) { + fToTargetName = toTarget.atom->getName(); + fToTarget.atom = NULL; + } + ((class BaseAtom*)at.atom)->addReference(this); + //fprintf(stderr, "Reference(): %p kind=%d, fToTarget<%s, %08X>, fromTarget<%s, %08X>\n", this, kind, + // this->getTargetName(), fToTarget.offset, this->getFromTargetName(), fromTarget.offset); +} + +template +Reference::Reference(Kinds kind, const AtomAndOffset& at, const char* toName, uint32_t toOffset) + : fFixUpOffsetInSrc(at.offset), + fToTargetName(toName), fFromTargetName(NULL), fKind(kind) +{ + fToTarget.offset = toOffset; + ((class BaseAtom*)at.atom)->addReference(this); +} + + +template +class Segment : public ObjectFile::Segment +{ +public: + Segment(const macho_section* sect); + virtual const char* getName() const { return fSection->segname(); } + virtual bool isContentReadable() const { return true; } + virtual bool isContentWritable() const { return fWritable; } + virtual bool isContentExecutable() const { return fExecutable; } +private: + const macho_section* fSection; + bool fWritable; + bool fExecutable; +}; + +template +Segment::Segment(const macho_section* sect) + : fSection(sect), fWritable(false), fExecutable(false) +{ + if ( strcmp(fSection->segname(), "__DATA") == 0 ) { + fWritable = true; + } + else if ( strcmp(fSection->segname(), "__OBJC") == 0 ) { + fWritable = true; + } + else if ( strcmp(fSection->segname(), "__TEXT") == 0 ) { + fExecutable = true; + } + else if ( strcmp(fSection->segname(), "__IMPORT") == 0 ) { + fWritable = true; + fExecutable = true; + } +} + + +class DataSegment : public ObjectFile::Segment +{ +public: + virtual const char* getName() const { return "__DATA"; } + virtual bool isContentReadable() const { return true; } + virtual bool isContentWritable() const { return true; } + virtual bool isContentExecutable() const { return false; } + + static DataSegment fgSingleton; +}; + +DataSegment DataSegment::fgSingleton; + + +class BaseAtom : public ObjectFile::Atom +{ +public: + BaseAtom() : fStabsStartIndex(0), fStabsCount(0) {} + + virtual void setSize(uint64_t size) = 0; + virtual void addReference(ObjectFile::Reference* ref) = 0; + virtual void addLineInfo(const ObjectFile::LineInfo& info) = 0; + virtual void alignAtLeast(uint8_t align) = 0; + + uint32_t fStabsStartIndex; + uint32_t fStabsCount; +}; + + +// +// A SymbolAtom represents a chunk of a mach-o object file that has a symbol table entry +// pointing to it. A C function or global variable is represented by one of these atoms. +// +// +template +class SymbolAtom : public BaseAtom +{ +public: + virtual ObjectFile::Reader* getFile() const { return &fOwner; } + virtual bool getTranslationUnitSource(const char** dir, const char** name) const + { return fOwner.getTranslationUnitSource(dir, name); } + virtual const char* getName() const { return &fOwner.fStrings[fSymbol->n_strx()]; } + virtual const char* getDisplayName() const { return getName(); } + virtual ObjectFile::Atom::Scope getScope() const { return fScope; } + virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ((fSymbol->n_desc() & N_WEAK_DEF) != 0) + ? ObjectFile::Atom::kWeakDefinition : ObjectFile::Atom::kRegularDefinition; } + virtual SymbolTableInclusion getSymbolTableInclusion() const { return ((fSymbol->n_desc() & REFERENCED_DYNAMICALLY) != 0) + ? ObjectFile::Atom::kSymbolTableInAndNeverStrip : ObjectFile::Atom::kSymbolTableIn; } + virtual bool isZeroFill() const { return ((fSection->flags() & SECTION_TYPE) == S_ZEROFILL); } + virtual uint64_t getSize() const { return fSize; } + virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } + virtual bool mustRemainInSection() const { return true; } + virtual const char* getSectionName() const; + virtual Segment& getSegment() const { return *fSegment; } + virtual bool requiresFollowOnAtom() const; + virtual ObjectFile::Atom& getFollowOnAtom() const; + virtual std::vector* getLineInfo() const { return (std::vector*)&fLineInfo; } + virtual uint8_t getAlignment() const { return fAlignment; } + virtual void copyRawContent(uint8_t buffer[]) const; + virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; } + virtual void setSize(uint64_t size); + virtual void addReference(ObjectFile::Reference* ref) { fReferences.insert(fReferences.begin(), (Reference*)ref); } + virtual void addLineInfo(const ObjectFile::LineInfo& info) { fLineInfo.push_back(info); } + virtual void alignAtLeast(uint8_t align) { fAlignment = std::max(align, fAlignment); } + +protected: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + typedef typename A::ReferenceKinds Kinds; + typedef typename std::vector*> ReferenceVector; + typedef typename ReferenceVector::iterator ReferenceVectorIterator; // seems to help C++ parser + typedef typename ReferenceVector::const_iterator ReferenceVectorConstIterator; // seems to help C++ parser + friend class Reader; + friend class SymbolAtomSorter; + + SymbolAtom(Reader&, const macho_nlist

*, const macho_section

*); + virtual ~SymbolAtom() {} + + Reader& fOwner; + const macho_nlist

* fSymbol; + pint_t fAddress; + pint_t fSize; + const macho_section

* fSection; + Segment* fSegment; + ReferenceVector fReferences; + std::vector fLineInfo; + ObjectFile::Atom::Scope fScope; + uint8_t fAlignment; +}; + + +template +SymbolAtom::SymbolAtom(Reader& owner, const macho_nlist

* symbol, const macho_section

* section) + : fOwner(owner), fSymbol(symbol), fAddress(0), fSize(0), fSection(section), fSegment(NULL), fAlignment(0) +{ + uint8_t type = symbol->n_type(); + if ( (type & N_EXT) == 0 ) + fScope = ObjectFile::Atom::scopeTranslationUnit; + else if ( (type & N_PEXT) != 0 ) + fScope = ObjectFile::Atom::scopeLinkageUnit; + else + fScope = ObjectFile::Atom::scopeGlobal; + if ( (type & N_TYPE) == N_SECT ) { + // real definition + fSegment = new Segment(fSection); + fAddress = fSymbol->n_value(); + if ( (fSymbol->n_desc() & N_NO_DEAD_STRIP) != 0 ) + this->setDontDeadStrip(); + } + else { + printf("unknown symbol type: %d\n", type); + } + //fprintf(stderr, "SymbolAtom(%p) %s fAddress=0x%X\n", this, this->getDisplayName(), (uint32_t)fAddress); + // support for .o files built with old ld64 + if ( (fSymbol->n_desc() & N_WEAK_DEF) && (strcmp(fSection->sectname(),"__picsymbolstub1__TEXT") == 0) ) { + const char* name = this->getName(); + const int nameLen = strlen(name); + if ( (nameLen > 6) && strcmp(&name[nameLen-5], "$stub") == 0 ) { + // switch symbol to point at name that does not have trailing $stub + char correctName[nameLen]; + strncpy(correctName, name, nameLen-5); + correctName[nameLen-5] = '\0'; + const macho_nlist

* symbolsStart = fOwner.fSymbols; + const macho_nlist

* symbolsEnd = &symbolsStart[fOwner.fSymbolCount]; + for(const macho_nlist

* s = symbolsStart; s < symbolsEnd; ++s) { + if ( strcmp(&fOwner.fStrings[s->n_strx()], correctName) == 0 ) { + fSymbol = s; + break; + } + } + } + } + // support for labeled stubs + switch ( section->flags() & SECTION_TYPE ) { + case S_SYMBOL_STUBS: + setSize(section->reserved2()); + break; + case S_LAZY_SYMBOL_POINTERS: + case S_NON_LAZY_SYMBOL_POINTERS: + setSize(sizeof(pint_t)); + break; + case S_4BYTE_LITERALS: + setSize(4); + break; + case S_8BYTE_LITERALS: + setSize(8); + break; + case S_CSTRING_LITERALS: + setSize(strlen((char*)(fOwner.fHeader) + section->offset() + fAddress - section->addr()) + 1); + case S_REGULAR: + case S_ZEROFILL: + case S_COALESCED: + // size calculate later after next atom is found + break; + } +} + + +template +void SymbolAtom::setSize(uint64_t size) +{ + fSize = size; + + // Try to compute the alignment base on the address aligned at in object file and the size + uint8_t sizeAlign = __builtin_ctz(fSize); + uint8_t sizeAndSectAlign = std::min((uint8_t)fSection->align(), sizeAlign); + // If address is zero, can't figure out better alignment than section alignment and size + if ( fAddress == 0 ) + fAlignment = sizeAndSectAlign; + else + fAlignment = std::min((uint8_t)__builtin_ctz(fAddress), sizeAndSectAlign); +} + + +template +const char* SymbolAtom::getSectionName() const +{ + if ( strlen(fSection->sectname()) > 15 ) { + static char temp[18]; + strncpy(temp, fSection->sectname(), 16); + temp[17] = '\0'; + return temp; + } + return fSection->sectname(); +} + +template +bool SymbolAtom::requiresFollowOnAtom() const +{ + // requires follow-on if built with old compiler and not the last atom + if ( (fOwner.fHeader->flags() & MH_SUBSECTIONS_VIA_SYMBOLS) == 0) { + for (ReferenceVectorConstIterator it=fReferences.begin(); it != fReferences.end(); it++) { + Reference* ref = *it; + if ( ref->getKind() == A::kFollowOn ) + return true; + } + } + return false; +} + +template +ObjectFile::Atom& SymbolAtom::getFollowOnAtom() const +{ + for (ReferenceVectorConstIterator it=fReferences.begin(); it != fReferences.end(); it++) { + Reference* ref = *it; + if ( ref->getKind() == A::kFollowOn ) + return ref->getTarget(); + } + return *((ObjectFile::Atom*)NULL); +} + + + + +template +void SymbolAtom::copyRawContent(uint8_t buffer[]) const +{ + // copy base bytes + if ( isZeroFill() ) + bzero(buffer, fSize); + else { + uint32_t fileOffset = fSection->offset() - fSection->addr() + fAddress; + memcpy(buffer, (char*)(fOwner.fHeader)+fileOffset, fSize); + } +} + + +template +class SymbolAtomSorter +{ +public: + SymbolAtomSorter(std::map& map) : fMap(map) {} + + typedef typename A::P::uint_t pint_t; + + bool operator()(ObjectFile::Atom* left, ObjectFile::Atom* right) + { + pint_t leftAddr = ((SymbolAtom*)left)->fAddress; + pint_t rightAddr = ((SymbolAtom*)right)->fAddress; + if ( leftAddr == rightAddr ) { + // two atoms with same address, must have been a function with multiple labels + // make sure we sort these so the one with real content (in map) is last + std::map::iterator pos = fMap.find(leftAddr); + if ( pos != fMap.end() ) { + return ( pos->second == right ); + } + return false; + } + else { + return ( leftAddr < rightAddr ); + } + } +private: + std::map& fMap; +}; + + +// +// A TentativeAtom represents a C "common" or "tentative" defintion of data. +// For instance, "int foo;" is neither a declaration or a definition and +// is represented by a TentativeAtom. +// +template +class TentativeAtom : public BaseAtom +{ +public: + virtual ObjectFile::Reader* getFile() const { return &fOwner; } + virtual bool getTranslationUnitSource(const char** dir, const char** name) const + { return fOwner.getTranslationUnitSource(dir, name); } + virtual const char* getName() const { return &fOwner.fStrings[fSymbol->n_strx()]; } + virtual const char* getDisplayName() const { return getName(); } + virtual ObjectFile::Atom::Scope getScope() const { return fScope; } + virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ObjectFile::Atom::kTentativeDefinition; } + virtual bool isZeroFill() const { return true; } + virtual SymbolTableInclusion getSymbolTableInclusion() const { return ((fSymbol->n_desc() & REFERENCED_DYNAMICALLY) != 0) + ? ObjectFile::Atom::kSymbolTableInAndNeverStrip : ObjectFile::Atom::kSymbolTableIn; } + virtual uint64_t getSize() const { return fSymbol->n_value(); } + virtual std::vector& getReferences() const { return fgNoReferences; } + virtual bool mustRemainInSection() const { return true; } + virtual const char* getSectionName() const { return "__common"; } + virtual ObjectFile::Segment& getSegment() const { return DataSegment::fgSingleton; } + virtual bool requiresFollowOnAtom() const { return false; } + virtual ObjectFile::Atom& getFollowOnAtom() const { return *(ObjectFile::Atom*)NULL; } + virtual std::vector* getLineInfo() const { return NULL; } + virtual uint8_t getAlignment() const; + virtual void copyRawContent(uint8_t buffer[]) const; + virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; } + virtual void setSize(uint64_t size) { } + virtual void addReference(ObjectFile::Reference* ref) { throw "can't add references"; } + virtual void addLineInfo(const ObjectFile::LineInfo& info) { throw "can't add line info to tentative definition"; } + virtual void alignAtLeast(uint8_t align) { } + +protected: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + typedef typename A::ReferenceKinds Kinds; + friend class Reader; + + TentativeAtom(Reader&, const macho_nlist

*); + virtual ~TentativeAtom() {} + + Reader& fOwner; + const macho_nlist

* fSymbol; + ObjectFile::Atom::Scope fScope; + static std::vector fgNoReferences; +}; + +template +std::vector TentativeAtom::fgNoReferences; + +template +TentativeAtom::TentativeAtom(Reader& owner, const macho_nlist

* symbol) + : fOwner(owner), fSymbol(symbol) +{ + uint8_t type = symbol->n_type(); + if ( (type & N_EXT) == 0 ) + fScope = ObjectFile::Atom::scopeTranslationUnit; + else if ( (type & N_PEXT) != 0 ) + fScope = ObjectFile::Atom::scopeLinkageUnit; + else + fScope = ObjectFile::Atom::scopeGlobal; + if ( ((type & N_TYPE) == N_UNDF) && (symbol->n_value() != 0) ) { + // tentative definition + } + else { + printf("unknown symbol type: %d\n", type); + } + //fprintf(stderr, "TentativeAtom(%p) %s\n", this, this->getDisplayName()); +} + + +template +uint8_t TentativeAtom::getAlignment() const +{ + // common symbols align to their size + // that is, a 4-byte common aligns to 4-bytes + // to be safe, odd size commons align to the next power-of-2 size + uint8_t alignment = (uint8_t)ceil(log2(this->getSize())); + // limit alignment of extremely large commons to 2^15 bytes (8-page) + if ( alignment < 15 ) + return alignment; + else + return 15; +} + +template +void TentativeAtom::copyRawContent(uint8_t buffer[]) const +{ + bzero(buffer, getSize()); +} + + +// +// An AnonymousAtom represents compiler generated data that has no name. +// For instance, a literal C-string or a 64-bit floating point constant +// is represented by an AnonymousAtom. +// +template +class AnonymousAtom : public BaseAtom +{ +public: + virtual ObjectFile::Reader* getFile() const { return &fOwner; } + virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; } + virtual const char* getName() const { return fSynthesizedName; } + virtual const char* getDisplayName() const; + virtual ObjectFile::Atom::Scope getScope() const; + virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const; + virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } + virtual bool isZeroFill() const; + virtual uint64_t getSize() const { return fSize; } + virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } + virtual bool mustRemainInSection() const { return true; } + virtual const char* getSectionName() const; + virtual Segment& getSegment() const { return *fSegment; } + virtual bool requiresFollowOnAtom() const; + virtual ObjectFile::Atom& getFollowOnAtom() const; + virtual std::vector* getLineInfo() const { return NULL; } + virtual uint8_t getAlignment() const; + virtual void copyRawContent(uint8_t buffer[]) const; + virtual void setScope(ObjectFile::Atom::Scope newScope) { } + virtual void setSize(uint64_t size) { fSize = size; } + virtual void addReference(ObjectFile::Reference* ref) { fReferences.insert(fReferences.begin(), (Reference*)ref); } + virtual void addLineInfo(const ObjectFile::LineInfo& info) { fprintf(stderr, "can't add line info to anonymous symbol %s\n", this->getDisplayName()); } + virtual void alignAtLeast(uint8_t align) { } + BaseAtom* redirectTo() { return fRedirect; } + bool isWeakImportStub() { return fWeakImportStub; } + +protected: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + typedef typename A::ReferenceKinds Kinds; + typedef typename std::vector*> ReferenceVector; + typedef typename ReferenceVector::iterator ReferenceVectorIterator; // seems to help C++ parser + typedef typename ReferenceVector::const_iterator ReferenceVectorConstIterator; // seems to help C++ parser + friend class Reader; + + AnonymousAtom(Reader&, const macho_section

*, uint32_t addr, uint32_t size); + virtual ~AnonymousAtom() {} + + Reader& fOwner; + const char* fSynthesizedName; + const macho_section

* fSection; + uint32_t fAddress; + uint32_t fSize; + Segment* fSegment; + ReferenceVector fReferences; + BaseAtom* fRedirect; + bool fWeakImportStub; + bool fReallyNonLazyPointer; // HACK until compiler stops emitting anonymous non-lazy pointers +}; + +template +AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* section, uint32_t addr, uint32_t size) + : fOwner(owner), fSynthesizedName(NULL), fSection(section), fAddress(addr), fSize(size), fSegment(NULL), + fWeakImportStub(false), fReallyNonLazyPointer(false) +{ + fSegment = new Segment(fSection); + fRedirect = this; + uint8_t type = fSection->flags() & SECTION_TYPE; + switch ( type ) { + case S_ZEROFILL: + { + asprintf((char**)&fSynthesizedName, "zero-fill-at-0x%08X", addr); + } + break; + case S_REGULAR: + // handle .o files created by old ld64 -r that are missing cstring section type + if ( strcmp(fSection->sectname(), "__cstring") != 0 ) + break; + // else fall into cstring case + case S_CSTRING_LITERALS: + { + const char* str = (char*)(owner.fHeader) + section->offset() + addr - section->addr(); + asprintf((char**)&fSynthesizedName, "cstring=%s", str); + } + break; + case S_4BYTE_LITERALS: + { + uint32_t value = E::get32(*(uint32_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr - section->addr())); + asprintf((char**)&fSynthesizedName, "4-byte-literal=0x%08X", value); + } + break; + case S_8BYTE_LITERALS: + { + uint64_t value = E::get64(*(uint64_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr - section->addr())); + asprintf((char**)&fSynthesizedName, "8-byte-literal=0x%016llX", value); + } + break; + case S_LITERAL_POINTERS: + { + // FIX FIX, we need the name to include the name of the target so that we can coalesce them + asprintf((char**)&fSynthesizedName, "literal-pointer@%d", addr - (uint32_t)fSection->addr()); + } + break; + case S_MOD_INIT_FUNC_POINTERS: + asprintf((char**)&fSynthesizedName, "initializer$%d", (addr - (uint32_t)fSection->addr())/sizeof(pint_t)); + break; + case S_MOD_TERM_FUNC_POINTERS: + asprintf((char**)&fSynthesizedName, "terminator$%d", (addr - (uint32_t)fSection->addr())/sizeof(pint_t)); + break; + case S_SYMBOL_STUBS: + { + uint32_t index = (fAddress - fSection->addr()) / fSection->reserved2(); + index += fSection->reserved1(); + uint32_t symbolIndex = E::get32(fOwner.fIndirectTable[index]); + const macho_nlist

* sym = &fOwner.fSymbols[symbolIndex]; + uint32_t strOffset = sym->n_strx(); + // want name to not have $stub suffix, this is what automatic stub generation expects + fSynthesizedName = &fOwner.fStrings[strOffset]; + // check for weak import + fWeakImportStub = fOwner.isWeakImportSymbol(sym); + // sometimes the compiler gets confused and generates a stub to a static function + // if so, we should redirect any call to the stub to be calls to the real static function atom + if ( ((sym->n_type() & N_TYPE) != N_UNDF) && ((sym->n_desc() & N_WEAK_DEF) == 0) ) { + BaseAtom* staticAtom = fOwner.findAtomByName(fSynthesizedName); + if ( staticAtom != NULL ) + fRedirect = staticAtom; + } + } + break; + case S_LAZY_SYMBOL_POINTERS: + case S_NON_LAZY_SYMBOL_POINTERS: + { + uint32_t index = (fAddress - fSection->addr()) / sizeof(pint_t); + index += fSection->reserved1(); + uint32_t symbolIndex = E::get32(fOwner.fIndirectTable[index]); + if ( symbolIndex == INDIRECT_SYMBOL_LOCAL ) { + // Silly codegen with non-lazy pointer to a local symbol + // All atoms not created yet, so we need to scan symbol table + uint32_t fileOffset = fSection->offset() - fSection->addr() + fAddress; + pint_t nonLazyPtrValue = P::getP(*((pint_t*)((char*)(fOwner.fHeader)+fileOffset))); + const macho_nlist

* end = &fOwner.fSymbols[fOwner.fSymbolCount]; + for (const macho_nlist

* sym = fOwner.fSymbols; sym < end; ++sym) { + if ( ((sym->n_type() & N_TYPE) == N_SECT) && (sym->n_value() == nonLazyPtrValue) ) { + const char* name = &fOwner.fStrings[sym->n_strx()]; + char* str = new char[strlen(name)+16]; + strcpy(str, name); + strcat(str, "$non_lazy_ptr"); + fSynthesizedName = str; + // add direct reference to target later, because its atom may not be constructed yet + fOwner.fLocalNonLazys.push_back(this); + return; + } + } + throwf("malformed .o file: non-lazy-pointer with value 0x%08X missing symbol", nonLazyPtrValue); + } + const macho_nlist

* targetSymbol = &fOwner.fSymbols[symbolIndex]; + const char* name = &fOwner.fStrings[targetSymbol->n_strx()]; + char* str = new char[strlen(name)+16]; + strcpy(str, name); + if ( type == S_LAZY_SYMBOL_POINTERS ) + strcat(str, "$lazy_ptr"); + else + strcat(str, "$non_lazy_ptr"); + fSynthesizedName = str; + + if ( fOwner.isWeakImportSymbol(targetSymbol) ) + new Reference(A::kPointerWeakImport, AtomAndOffset(this), name, 0); + else + new Reference(A::kPointer, AtomAndOffset(this), name, 0); + } + break; + default: + throwf("section type %d not supported with address=0x%08X", type, addr); + } + //fprintf(stderr, "AnonymousAtom(%p) %s \n", this, this->getDisplayName()); +} + + +template +const char* AnonymousAtom::getDisplayName() const +{ + if ( fSynthesizedName != NULL ) + return fSynthesizedName; + + static char temp[512]; + if ( (fSection->flags() & SECTION_TYPE) == S_CSTRING_LITERALS ) { + uint32_t fileOffset = fSection->offset() - fSection->addr() + fAddress; + sprintf(temp, "atom string literal: \"%s\"", (char*)(fOwner.fHeader)+fileOffset); + } + else { + sprintf(temp, "%s@%d", fSection->sectname(), fAddress - (uint32_t)fSection->addr() ); + } + return temp; +} + +template +ObjectFile::Atom::Scope AnonymousAtom::getScope() const +{ + if ( fReallyNonLazyPointer ) + return ObjectFile::Atom::scopeLinkageUnit; + // in order for literals to be coalesced they must be scoped to linkage unit + switch ( fSection->flags() & SECTION_TYPE ) { + case S_CSTRING_LITERALS: + case S_4BYTE_LITERALS: + case S_8BYTE_LITERALS: + case S_SYMBOL_STUBS: + case S_NON_LAZY_SYMBOL_POINTERS: + return ObjectFile::Atom::scopeLinkageUnit; + default: + return ObjectFile::Atom::scopeTranslationUnit; + } +} + +template +ObjectFile::Atom::DefinitionKind AnonymousAtom::getDefinitionKind() const +{ + if ( fReallyNonLazyPointer ) + return ObjectFile::Atom::kWeakDefinition; + // in order for literals to be coalesced they must be weak + switch ( fSection->flags() & SECTION_TYPE ) { + case S_CSTRING_LITERALS: + case S_4BYTE_LITERALS: + case S_8BYTE_LITERALS: + case S_NON_LAZY_SYMBOL_POINTERS: + return ObjectFile::Atom::kWeakDefinition; + default: + return ObjectFile::Atom::kRegularDefinition; + } +} + +template +bool AnonymousAtom::isZeroFill() const +{ + return ( (fSection->flags() & SECTION_TYPE) == S_ZEROFILL ); +} + + +template +const char* AnonymousAtom::getSectionName() const +{ + if ( fReallyNonLazyPointer ) + return "__nl_symbol_ptr"; + if ( strlen(fSection->sectname()) > 15 ) { + static char temp[18]; + strncpy(temp, fSection->sectname(), 16); + temp[17] = '\0'; + return temp; + } + return fSection->sectname(); +} + +template +uint8_t AnonymousAtom::getAlignment() const +{ + if ( fReallyNonLazyPointer ) + return (uint8_t)log2(sizeof(pint_t)); + switch ( fSection->flags() & SECTION_TYPE ) { + case S_4BYTE_LITERALS: + return 2; + case S_8BYTE_LITERALS: + return 3; + case S_NON_LAZY_SYMBOL_POINTERS: + return (uint8_t)log2(sizeof(pint_t)); + default: + return fSection->align(); + } +} + +template +bool AnonymousAtom::requiresFollowOnAtom() const +{ + // requires follow-on if built with old compiler and not the last atom + if ( (fOwner.fHeader->flags() & MH_SUBSECTIONS_VIA_SYMBOLS) == 0) { + for (ReferenceVectorConstIterator it=fReferences.begin(); it != fReferences.end(); it++) { + Reference* ref = *it; + if ( ref->getKind() == A::kFollowOn ) + return true; + } + } + return false; +} + +template +ObjectFile::Atom& AnonymousAtom::getFollowOnAtom() const +{ + for (ReferenceVectorConstIterator it=fReferences.begin(); it != fReferences.end(); it++) { + Reference* ref = *it; + if ( ref->getKind() == A::kFollowOn ) + return ref->getTarget(); + } + return *((ObjectFile::Atom*)NULL); +} + +template +void AnonymousAtom::copyRawContent(uint8_t buffer[]) const +{ + // copy base bytes + if ( isZeroFill() ) + bzero(buffer, fSize); + else { + uint32_t fileOffset = fSection->offset() - fSection->addr() + fAddress; + memcpy(buffer, (char*)(fOwner.fHeader)+fileOffset, fSize); + } +} + + + + +template +class Reader : public ObjectFile::Reader +{ +public: + static bool validFile(const uint8_t* fileContent); + static Reader* make(const uint8_t* fileContent, const char* path, time_t modTime, + const ObjectFile::ReaderOptions& options) + { return new Reader(fileContent, path, modTime, options); } + virtual ~Reader() {} + + virtual const char* getPath() { return fPath; } + virtual time_t getModificationTime() { return fModTime; } + virtual ObjectFile::Reader::DebugInfoKind getDebugInfoKind() { return fDebugInfo; } + virtual std::vector& getAtoms() { return (std::vector&)(fAtoms); } + virtual std::vector* getJustInTimeAtomsFor(const char* name) { return NULL; } + virtual std::vector* getStabs() { return &fStabs; } + + bool getTranslationUnitSource(const char** dir, const char** name) const; + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + //typedef typename std::vector*> AtomVector; + //typedef typename AtomVector::iterator AtomVectorIterator; // seems to help C++ parser + typedef typename A::ReferenceKinds Kinds; + friend class AnonymousAtom; + friend class TentativeAtom; + friend class SymbolAtom; + Reader(const uint8_t* fileContent, const char* path, time_t modTime, const ObjectFile::ReaderOptions& options); + bool addRelocReference(const macho_section

* sect, const macho_relocation_info

* reloc); + bool addRelocReference_powerpc(const macho_section

* sect, const macho_relocation_info

* reloc); + Kinds pointerDiffKindForLength_powerpc(uint8_t r_length); + bool read_comp_unit(const char ** name, const char ** comp_dir, uint64_t *stmt_list); + static bool isWeakImportSymbol(const macho_nlist

* sym); + static bool skip_form(const uint8_t ** offset, const uint8_t * end, uint64_t form, uint8_t addr_size, bool dwarf64); + static const char* assureFullPath(const char* path); + AtomAndOffset findAtomAndOffset(uint32_t addr); + AtomAndOffset findAtomAndOffset(uint32_t baseAddr, uint32_t realAddr); + Reference* makeReference(Kinds kind, uint32_t atAddr, uint32_t toAddr); + Reference* makeReference(Kinds kind, uint32_t atAddr, uint32_t fromAddr, uint32_t toAddr); + Reference* makeReferenceWithToBase(Kinds kind, uint32_t atAddr, uint32_t toAddr, uint32_t toBaseAddr); + Reference* makeReferenceWithToBase(Kinds kind, uint32_t atAddr, uint32_t fromAddr, uint32_t toAddr, uint32_t toBaseAddr); + Reference* makeByNameReference(Kinds kind, uint32_t atAddr, const char* toName, uint32_t toOffset); + Reference* makeReferenceToEH(const char* ehName, pint_t ehAtomAddress, const macho_section

* ehSect); + void validSectionType(uint8_t type); + + BaseAtom* findAtomByName(const char*); + + const char* fPath; + time_t fModTime; + const ObjectFile::ReaderOptions& fOptions; + const macho_header

* fHeader; + const char* fStrings; + const macho_nlist

* fSymbols; + uint32_t fSymbolCount; + const macho_segment_command

* fSegment; + const uint32_t* fIndirectTable; + std::vector fAtoms; + std::map fAddrToAtom; + std::vector*> fLocalNonLazys; + ObjectFile::Reader::DebugInfoKind fDebugInfo; + const macho_section

* fDwarfDebugInfoSect; + const macho_section

* fDwarfDebugAbbrevSect; + const macho_section

* fDwarfDebugLineSect; + const char* fDwarfTranslationUnitDir; + const char* fDwarfTranslationUnitFile; + std::map fDwarfIndexToFile; + std::vector fStabs; +}; + + +template +Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, const ObjectFile::ReaderOptions& options) + : fPath(strdup(path)), fModTime(modTime), fOptions(options), fHeader((const macho_header

*)fileContent), + fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fSegment(NULL), fIndirectTable(NULL), + fDebugInfo(kDebugInfoNone), fDwarfDebugInfoSect(NULL), fDwarfDebugAbbrevSect(NULL), + fDwarfTranslationUnitDir(NULL), fDwarfTranslationUnitFile(NULL) +{ + // sanity check + if ( ! validFile(fileContent) ) + throw "not a valid mach-o object file"; + + // cache intersting pointers + const macho_header

* header = (const macho_header

*)fileContent; + const uint32_t cmd_count = header->ncmds(); + const macho_load_command

* const cmds = (macho_load_command

*)((char*)header + sizeof(macho_header

)); + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_SYMTAB: + { + const macho_symtab_command

* symtab = (macho_symtab_command

*)cmd; + fSymbolCount = symtab->nsyms(); + fSymbols = (const macho_nlist

*)((char*)header + symtab->symoff()); + fStrings = (char*)header + symtab->stroff(); + } + break; + case LC_DYSYMTAB: + { + const macho_dysymtab_command

* dsymtab = (struct macho_dysymtab_command

*)cmd; + fIndirectTable = (uint32_t*)((char*)fHeader + dsymtab->indirectsymoff()); + } + break; + case LC_UUID: + if (getDebugInfoKind() != kDebugInfoDwarf) + fDebugInfo = kDebugInfoStabsUUID; + break; + + default: + if ( cmd->cmd() == macho_segment_command

::CMD ) { + fSegment = (macho_segment_command

*)cmd; + } + break; + } + cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); + } + const macho_section

* const sectionsStart = (macho_section

*)((char*)fSegment + sizeof(macho_segment_command

)); + const macho_section

* const sectionsEnd = §ionsStart[fSegment->nsects()]; + + // inital guess for number of atoms + fAtoms.reserve(fSymbolCount); + + // add all atoms that have entries in symbol table + const macho_section

* sections = (macho_section

*)((char*)fSegment + sizeof(macho_segment_command

)); + for (uint32_t i=0; i < fSymbolCount; ++i) { + const macho_nlist

& sym = fSymbols[i]; + if ( (sym.n_type() & N_STAB) == 0 ) { + uint8_t type = (sym.n_type() & N_TYPE); + if ( type == N_SECT ) { + const macho_section

* section = §ions[sym.n_sect()-1]; + bool suppress = false; + // ignore atoms in debugger sections + if ( (section->flags() & S_ATTR_DEBUG) == 0 ) { + // ignore labels for atoms in other sections + switch ( section->flags() & SECTION_TYPE ) { + case S_REGULAR: + if ( (sym.n_desc() & N_WEAK_DEF) && strcmp(section->sectname(), "__picsymbolstub1__TEXT") == 0 ) + suppress = true; // ignore stubs in crt1.o built by old ld64 that was missing S_SYMBOL_STUBS + case S_ZEROFILL: + case S_COALESCED: + case S_4BYTE_LITERALS: + case S_8BYTE_LITERALS: + case S_CSTRING_LITERALS: + { + BaseAtom* newAtom = new SymbolAtom(*this, &sym, section); + std::map::iterator pos = fAddrToAtom.find(sym.n_value()); + if ( pos != fAddrToAtom.end() ) { + // another label to an existing address + // make this one be the real one and followed by the previous + BaseAtom* existingAtom = pos->second; + //fprintf(stderr, "new atom %s has same address as existing atom %s\n", newAtom->getDisplayName(), existingAtom->getDisplayName()); + new Reference(A::kFollowOn, AtomAndOffset(newAtom), AtomAndOffset(existingAtom)); + newAtom->setSize(0); + } + else { + fAddrToAtom[sym.n_value()] = newAtom; + } + if ( ! suppress ) + fAtoms.push_back(newAtom); + } + break; + case S_SYMBOL_STUBS: + case S_LAZY_SYMBOL_POINTERS: + case S_NON_LAZY_SYMBOL_POINTERS: + // ignore symboled stubs produces by old ld64 + break; + default: + fprintf(stderr, "ld64 warning: symbol %s found in unsupported section in %s\n", + &fStrings[sym.n_strx()], this->getPath()); + } + } + } + else if ( (type == N_UNDF) && (sym.n_value() != 0) ) { + fAtoms.push_back(new TentativeAtom(*this, &sym)); + } + } + } + + // sort SymbolAtoms by address + std::sort(fAtoms.begin(), fAtoms.end(), SymbolAtomSorter(fAddrToAtom)); + + // add all fixed size anonymous atoms from special sections + for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + uint32_t atomSize = 0; + uint8_t type (sect->flags() & SECTION_TYPE); + validSectionType(type); + bool suppress = false; + switch ( type ) { + case S_SYMBOL_STUBS: + suppress = true; + atomSize = sect->reserved2(); + break; + case S_LAZY_SYMBOL_POINTERS: + suppress = true; + atomSize = sizeof(pint_t); + break; + case S_NON_LAZY_SYMBOL_POINTERS: + case S_LITERAL_POINTERS: + case S_MOD_INIT_FUNC_POINTERS: + case S_MOD_TERM_FUNC_POINTERS: + atomSize = sizeof(pint_t); + break; + case S_INTERPOSING: + atomSize = sizeof(pint_t)*2; + break; + case S_4BYTE_LITERALS: + atomSize = 4; + break; + case S_8BYTE_LITERALS: + atomSize = 8; + break; + } + if ( atomSize != 0 ) { + for(uint32_t sectOffset=0; sectOffset < sect->size(); sectOffset += atomSize) { + uint32_t atomAddr = sect->addr() + sectOffset; + // add if not already an atom at that address + if ( fAddrToAtom.find(atomAddr) == fAddrToAtom.end() ) { + AnonymousAtom* newAtom = new AnonymousAtom(*this, sect, atomAddr, atomSize); + if ( !suppress ) + fAtoms.push_back(newAtom); + fAddrToAtom[atomAddr] = newAtom->redirectTo(); + } + } + } + } + + // add all c-string anonymous atoms + for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( ((sect->flags() & SECTION_TYPE) == S_CSTRING_LITERALS) || strcmp(sect->sectname(), "__cstring") == 0 ) { + uint32_t stringLen; + uint32_t stringAddr; + BaseAtom* firstEmptyString = NULL; + for(uint32_t sectOffset=0; sectOffset < sect->size(); sectOffset += stringLen) { + stringAddr = sect->addr() + sectOffset; + stringLen = strlen((char*)(fHeader) + sect->offset() + sectOffset) + 1; + // add if not already an atom at that address + if ( fAddrToAtom.find(stringAddr) == fAddrToAtom.end() ) { + BaseAtom* newAtom = new AnonymousAtom(*this, sect, stringAddr, stringLen); + if ( stringLen == 1 ) { + // because of padding it may look like there are lots of empty strings + // map them all to the first empty string + if ( firstEmptyString == NULL ) { + firstEmptyString = newAtom; + fAtoms.push_back(firstEmptyString); + } + fAddrToAtom[stringAddr] = firstEmptyString; + } + else { + fAtoms.push_back(newAtom); + fAddrToAtom[stringAddr] = newAtom; + } + } + } + } + } + + // create atoms to cover any non-debug ranges not handled above + for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + pint_t sectionStartAddr = sect->addr(); + pint_t sectionEndAddr = sect->addr() + sect->size(); + const bool setFollowOnAtom = ((fHeader->flags() & MH_SUBSECTIONS_VIA_SYMBOLS) == 0); + if ( sect->size() != 0 ) { + // ignore dwarf sections. If ld every supports processing dwarf, this logic will need to change + if ( (sect->flags() & S_ATTR_DEBUG) != 0 ) { + fDebugInfo = kDebugInfoDwarf; + if ( strcmp(sect->sectname(), "__debug_info") == 0 ) + fDwarfDebugInfoSect = sect; + else if ( strcmp(sect->sectname(), "__debug_abbrev") == 0 ) + fDwarfDebugAbbrevSect = sect; + else if ( strcmp(sect->sectname(), "__debug_line") == 0 ) + fDwarfDebugLineSect = sect; + } + else { + if ( strcmp(sect->segname(), "__DWARFA") == 0 ) { + throw "object file contains old DWARF debug info - rebuild with newer compiler"; + } + uint8_t type (sect->flags() & SECTION_TYPE); + switch ( type ) { + case S_REGULAR: + case S_ZEROFILL: + case S_COALESCED: + // detect if compiler has generated anonymous non-lazy pointers at end of __data section + // HACK BEGIN - until compiler stops generated anonymous non-lazy pointers + if ( (sect->size() >= sizeof(pint_t)) + && ((sect->size() % sizeof(pint_t)) == 0) + && (sect->align() >= log2(sizeof(pint_t))) + && (strcmp(sect->sectname(), "__data") == 0) + && (strcmp(sect->segname(), "__DATA") == 0) ) { + // find every pointer sized external reloc from end of section and split off into own atom + uint32_t possiblePointerAddress = sect->size() - sizeof(pint_t); + const uint8_t* sectionContent = ((uint8_t*)(fHeader))+sect->offset(); + const macho_relocation_info

* relocs = (macho_relocation_info

*)((char*)(fHeader) + sect->reloff()); + const macho_relocation_info

* relocsEnd = &relocs[sect->nreloc()]; + for (const macho_relocation_info

* r = relocs; r < relocsEnd; ++r) { + if ( ((r->r_address() & R_SCATTERED) == 0) + && r->r_extern() + && (r->r_address() == possiblePointerAddress) + && (fAddrToAtom.find(possiblePointerAddress+sect->addr()) == fAddrToAtom.end()) + && (P::getP(*((pint_t*)(sectionContent+possiblePointerAddress))) == 0) ) { + // create an anonymous atom to cover this non-lazy pointer + AnonymousAtom* newAtom = new AnonymousAtom(*this, sect, sect->addr()+possiblePointerAddress, sizeof(pint_t)); + const macho_nlist

* targetSymbol = &fSymbols[r->r_symbolnum()]; + char* name; + asprintf(&name, "%s$non_lazy_ptr", &fStrings[targetSymbol->n_strx()]); + newAtom->fSynthesizedName = name; + newAtom->fReallyNonLazyPointer = true; + fAtoms.push_back(newAtom); + fAddrToAtom[sect->addr()+possiblePointerAddress] = newAtom; + possiblePointerAddress -= sizeof(pint_t); + sectionEndAddr -= sizeof(pint_t); + } + else { + break; + } + } + } + // HACK END - until compiler stops generated anonymous non-lazy pointers + uint32_t previousAtomAddr = 0; + BaseAtom* previousAtom = NULL; + if ( fAddrToAtom.find(sectionStartAddr) == fAddrToAtom.end() ) { + // if there is not an atom already at the start of this section, add an anonymous one + BaseAtom* newAtom = new AnonymousAtom(*this, sect, sect->addr(), 0); + fAtoms.push_back(newAtom); + fAddrToAtom[sect->addr()] = newAtom; + previousAtomAddr = sectionStartAddr; + previousAtom = newAtom; + } + // calculate size of all atoms in this section and add follow-on references + for (std::map::iterator it=fAddrToAtom.begin(); it != fAddrToAtom.end(); it++) { + // note: this algorithm depends on the map iterator returning entries in address order + if ( (it->first >= sectionStartAddr) && (it->first < sectionEndAddr) ) { + //fprintf(stderr, " atom %s in section\n", it->second->getDisplayName()); + if ( previousAtom != NULL ) { + previousAtom->setSize(it->first - previousAtomAddr); + // FIX FIX: this setting of followOn atoms does not work when there are multiple + // labels for the same atom + if ( setFollowOnAtom && (it->second != previousAtom) ) + makeReference(A::kFollowOn, previousAtomAddr, it->first); + } + previousAtomAddr = it->first; + previousAtom = it->second; + } + } + if ( previousAtom != NULL ) { + // set last atom in section + previousAtom->setSize(sectionEndAddr - previousAtomAddr); + } + break; + } + } + } + } + + // add relocation based references + for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + // ignore dwarf sections. If ld every supports processing dwarf, this logic will need to change + if ( (sect->flags() & S_ATTR_DEBUG) == 0 ) { + switch ( sect->flags() & SECTION_TYPE ) { + case S_SYMBOL_STUBS: + case S_LAZY_SYMBOL_POINTERS: + // we ignore compiler generated stubs, so ignore those relocs too + break; + default: + const macho_relocation_info

* relocs = (macho_relocation_info

*)((char*)(fHeader) + sect->reloff()); + const uint32_t relocCount = sect->nreloc(); + //fprintf(stderr, "relocCount = %d in section %s\n", relocCount, sect->sectname()); + for (uint32_t r = 0; r < relocCount; ++r) { + try { + if ( addRelocReference(sect, &relocs[r]) ) + ++r; // skip next + } + catch (const char* msg) { + throwf("in section %s,%s reloc %u: %s\n", sect->segname(), sect->sectname(), r, msg); + } + } + } + } + } + + // add direct references to local non-lazy-pointers, can do this now that all atoms are constructed + for (typename std::vector*>::iterator it=fLocalNonLazys.begin(); it != fLocalNonLazys.end(); it++) { + AnonymousAtom* localNonLazy = *it; + uint32_t fileOffset = localNonLazy->fSection->offset() - localNonLazy->fSection->addr() + localNonLazy->fAddress; + pint_t nonLazyPtrValue = P::getP(*((pint_t*)((char*)(fHeader)+fileOffset))); + makeReference(A::kPointer, localNonLazy->fAddress, nonLazyPtrValue); + } + + // add implicit direct reference from each C++ function to its eh info + for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( ((sect->flags() & SECTION_TYPE) == S_COALESCED) && (strcmp(sect->sectname(), "__eh_frame") == 0) ) { + for (std::map::iterator it=fAddrToAtom.begin(); it != fAddrToAtom.end(); it++) { + // note: this algorithm depens on the map iterator returning entries in address order + if ( (it->first >= sect->addr()) && (it->first < sect->addr()+sect->size()) ) { + uint32_t ehAtomAddress = it->first; + BaseAtom* ehAtom = it->second; + const char* ehName = ehAtom->getName(); + if ( (ehName != NULL) && (strcmp(&ehName[strlen(ehName)-3], ".eh") == 0) ) + makeReferenceToEH(ehName, ehAtomAddress, sect); + } + } + } + } + + + //for (std::map::iterator it=fAddrToAtom.begin(); it != fAddrToAtom.end(); it++) { + // fprintf(stderr, "[0x%0X -> 0x%0llX) : %s\n", it->first, it->first+it->second->getSize(), it->second->getDisplayName()); + //} + + // add translation unit info from dwarf + uint64_t stmtList; + if ( (fDebugInfo == kDebugInfoDwarf) && (fOptions.fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone) ) { + if ( !read_comp_unit(&fDwarfTranslationUnitFile, &fDwarfTranslationUnitDir, &stmtList) ) { + // if can't parse dwarf, warn and give up + fDwarfTranslationUnitFile = NULL; + fDwarfTranslationUnitDir = NULL; + fprintf(stderr, "ld64: warning can't parse dwarf compilation unit info in %s\n", this->getPath()); + fDebugInfo = kDebugInfoNone; + } + } + + // add line number info to atoms from dwarf + if ( (fDebugInfo == kDebugInfoDwarf) && (fOptions.fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone) ) { + // file with just data will have no __debug_line info + if ( (fDwarfDebugLineSect != NULL) && (fDwarfDebugLineSect->size() != 0) && (fAddrToAtom.size() != 0) ) { + // validate stmt_list + if ( (stmtList != (uint64_t)-1) && (stmtList < fDwarfDebugLineSect->size()) ) { + const uint8_t* debug_line = (uint8_t*)(fHeader) + fDwarfDebugLineSect->offset(); + if ( debug_line != NULL ) { + struct line_reader_data* lines = line_open(&debug_line[stmtList], + fDwarfDebugLineSect->size() - stmtList, E::little_endian); + struct line_info result; + ObjectFile::Atom* curAtom = NULL; + uint32_t curAtomOffset = 0; + uint32_t curAtomAddress = 0; + uint32_t curAtomSize = 0; + while ( line_next (lines, &result, line_stop_line) ) { + // for performance, see if in next pc is in current atom + if ( (curAtom != NULL) && (result.pc <= curAtomAddress+curAtomSize) && (curAtomAddress <= result.pc) ) { + curAtomOffset = result.pc - curAtomAddress; + } + else { + // do slow look up of atom by address + AtomAndOffset ao = this->findAtomAndOffset(result.pc); + curAtom = ao.atom; + if ( curAtom == NULL ) + break; // file has line info but no functions + curAtomOffset = ao.offset; + curAtomAddress = result.pc; + curAtomSize = curAtom->getSize(); + } + const char* filename; + std::map::iterator pos = fDwarfIndexToFile.find(result.file); + if ( pos == fDwarfIndexToFile.end() ) { + filename = line_file(lines, result.file); + fDwarfIndexToFile[result.file] = filename; + } + else { + filename = pos->second; + } + ObjectFile::LineInfo info; + info.atomOffset = curAtomOffset; + info.fileName = filename; + info.lineNumber = result.line; + //fprintf(stderr, "addr=0x%08llX, line=%lld, file=%s\n", result.pc, result.line, filename); + ((BaseAtom*)curAtom)->addLineInfo(info); + } + line_free(lines); + } + else { + fprintf(stderr, "ld64: warning could not parse dwarf line number info in %s\n", this->getPath()); + } + } + } + } + + // if no dwarf, try processing stabs debugging info + if ( (fDebugInfo == kDebugInfoNone) && (fOptions.fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone) ) { + // scan symbol table for stabs entries + fStabs.reserve(fSymbolCount); // reduce re-allocations + BaseAtom* currentAtom = NULL; + pint_t currentAtomAddress = 0; + enum { start, inBeginEnd, inFun } state = start; + for (uint32_t symbolIndex = 0; symbolIndex < fSymbolCount; ++symbolIndex ) { + const macho_nlist

* sym = &fSymbols[symbolIndex]; + uint8_t type = sym->n_type(); + const char* symString = (sym->n_strx() != 0) ? &fStrings[sym->n_strx()] : NULL; + if ( (type & N_STAB) != 0 ) { + fDebugInfo = kDebugInfoStabs; + Stab stab; + stab.atom = NULL; + stab.type = type; + stab.other = sym->n_sect(); + stab.desc = sym->n_desc(); + stab.value = sym->n_value(); + stab.string = NULL; + switch (state) { + case start: + switch (type) { + case N_BNSYM: + // beginning of function block + state = inBeginEnd; + // fall into case to lookup atom by addresss + case N_LCSYM: + case N_STSYM: + currentAtomAddress = sym->n_value(); + currentAtom = (BaseAtom*)this->findAtomAndOffset(currentAtomAddress).atom; + if ( currentAtom != NULL ) { + stab.atom = currentAtom; + stab.string = symString; + } + else { + fprintf(stderr, "can't find atom for stabs BNSYM at %08llX in %s\n", + (uint64_t)sym->n_value(), path); + } + break; + case N_SO: + case N_OSO: + case N_OPT: + case N_LSYM: + // not associated with an atom, just copy + stab.string = symString; + break; + case N_GSYM: + // n_value field is NOT atom address ;-( + // need to find atom by name match + const char* colon = strchr(symString, ':'); + if ( colon != NULL ) { + // build underscore leading name + int nameLen = colon - symString; + char symName[nameLen+2]; + strlcpy(&symName[1], symString, nameLen+1); + symName[0] = '_'; + symName[nameLen+1] = '\0'; + currentAtom = findAtomByName(symName); + if ( currentAtom != NULL ) { + stab.atom = currentAtom; + stab.string = symString; + } + } + if ( stab.atom == NULL ) { + fprintf(stderr, "can't find atom for N_GSYM stabs %s in %s\n", symString, path); + } + break; + case N_FUN: + // old style stabs without BNSYM + state = inFun; + currentAtomAddress = sym->n_value(); + currentAtom = (BaseAtom*)this->findAtomAndOffset(currentAtomAddress).atom; + if ( currentAtom != NULL ) { + stab.atom = currentAtom; + stab.string = symString; + } + else { + fprintf(stderr, "can't find atom for stabs FUN at %08llX in %s\n", + (uint64_t)currentAtomAddress, path); + } + break; + case N_SOL: + case N_SLINE: + stab.string = symString; + // old stabs + break; + case N_BINCL: + case N_EINCL: + case N_EXCL: + stab.string = symString; + // -gfull built .o file + break; + default: + fprintf(stderr, "unknown stabs type 0x%X in %s\n", type, path); + } + break; + case inBeginEnd: + stab.atom = currentAtom; + switch (type) { + case N_ENSYM: + state = start; + currentAtom = NULL; + break; + case N_LCSYM: + case N_STSYM: + BaseAtom* nestedAtom = (BaseAtom*)this->findAtomAndOffset(sym->n_value()).atom; + if ( nestedAtom != NULL ) { + stab.atom = nestedAtom; + stab.string = symString; + } + else { + fprintf(stderr, "can't find atom for stabs 0x%X at %08llX in %s\n", + type, (uint64_t)sym->n_value(), path); + } + break; + case N_LBRAC: + case N_RBRAC: + case N_SLINE: + // adjust value to be offset in atom + stab.value -= currentAtomAddress; + default: + stab.string = symString; + break; + } + break; + case inFun: + switch (type) { + case N_FUN: + if ( sym->n_sect() != 0 ) { + // found another start stab, must be really old stabs... + currentAtomAddress = sym->n_value(); + currentAtom = (BaseAtom*)this->findAtomAndOffset(currentAtomAddress).atom; + if ( currentAtom != NULL ) { + stab.atom = currentAtom; + stab.string = symString; + } + else { + fprintf(stderr, "can't find atom for stabs FUN at %08llX in %s\n", + (uint64_t)currentAtomAddress, path); + } + } + else { + // found ending stab, switch back to start state + stab.string = symString; + stab.atom = currentAtom; + state = start; + currentAtom = NULL; + } + break; + case N_LBRAC: + case N_RBRAC: + case N_SLINE: + // adjust value to be offset in atom + stab.value -= currentAtomAddress; + stab.atom = currentAtom; + break; + case N_SO: + stab.string = symString; + state = start; + break; + default: + stab.atom = currentAtom; + stab.string = symString; + break; + } + break; + } + // add to list of stabs for this .o file + fStabs.push_back(stab); + } + } + } + + +#if 0 + // special case precompiled header .o file (which has no content) to have one empty atom + if ( fAtoms.size() == 0 ) { + int pathLen = strlen(path); + if ( (pathLen > 6) && (strcmp(&path[pathLen-6], ".gch.o")==0) ) { + ObjectFile::Atom* phony = new AnonymousAtom(*this, (uint32_t)0); + //phony->fSynthesizedName = ".gch.o"; + fAtoms.push_back(phony); + } + } +#endif +} + + +template +void Reader::validSectionType(uint8_t type) +{ +} + +template +bool Reader::getTranslationUnitSource(const char** dir, const char** name) const +{ + if ( fDebugInfo == kDebugInfoDwarf ) { + *dir = fDwarfTranslationUnitDir; + *name = fDwarfTranslationUnitFile; + return true; + } + return false; +} + +template +BaseAtom* Reader::findAtomByName(const char* name) +{ + // first search the more important atoms + for (std::map::iterator it=fAddrToAtom.begin(); it != fAddrToAtom.end(); it++) { + const char* atomName = it->second->getName(); + if ( (atomName != NULL) && (strcmp(atomName, name) == 0) ) { + return it->second; + } + } + // try all atoms, because this might have been a tentative definition + for (std::vector::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) { + BaseAtom* atom = (BaseAtom*)(*it); + const char* atomName = atom->getName(); + if ( (atomName != NULL) && (strcmp(atomName, name) == 0) ) { + return atom; + } + } + return NULL; +} + +template +Reference* Reader::makeReference(Kinds kind, uint32_t atAddr, uint32_t toAddr) +{ + return new Reference(kind, findAtomAndOffset(atAddr), findAtomAndOffset(toAddr)); +} + +template +Reference* Reader::makeReference(Kinds kind, uint32_t atAddr, uint32_t fromAddr, uint32_t toAddr) +{ + return new Reference(kind, findAtomAndOffset(atAddr), findAtomAndOffset(fromAddr), findAtomAndOffset(toAddr)); +} + +template +Reference* Reader::makeReferenceWithToBase(Kinds kind, uint32_t atAddr, uint32_t toAddr, uint32_t toBaseAddr) +{ + return new Reference(kind, findAtomAndOffset(atAddr), findAtomAndOffset(toBaseAddr, toAddr)); +} + +template +Reference* Reader::makeReferenceWithToBase(Kinds kind, uint32_t atAddr, uint32_t fromAddr, uint32_t toAddr, uint32_t toBaseAddr) +{ + return new Reference(kind, findAtomAndOffset(atAddr), findAtomAndOffset(fromAddr), findAtomAndOffset(toBaseAddr, toAddr)); +} + +template +Reference* Reader::makeByNameReference(Kinds kind, uint32_t atAddr, const char* toName, uint32_t toOffset) +{ + return new Reference(kind, findAtomAndOffset(atAddr), toName, toOffset); +} + +template +Reference* Reader::makeReferenceToEH(const char* ehName, pint_t ehAtomAddress, const macho_section

* ehSect) +{ + // add a direct reference from function atom to its eh frame atom + const uint8_t* ehContent = (const uint8_t*)(fHeader) + ehAtomAddress - ehSect->addr() + ehSect->offset(); + int32_t deltaMinus8 = P::getP(*(pint_t*)(&ehContent[8])); // offset 8 in eh info is delta to function + uint32_t funcAddr = ehAtomAddress + deltaMinus8 + 8; + return makeReference(A::kNoFixUp, funcAddr, ehAtomAddress) ; +} + + + +template +AtomAndOffset Reader::findAtomAndOffset(uint32_t addr) +{ + // STL has no built-in for "find largest key that is same or less than" + std::map::iterator it = fAddrToAtom.upper_bound(addr); + --it; // upper_bound gets us next key, so we back up one + AtomAndOffset result; + result.atom = it->second; + result.offset = addr - it->first; + //fprintf(stderr, "findAtomAndOffset(0x%0X) ==> %s (0x%0X -> 0x%0llX)\n", + // addr, result.atom->getDisplayName(), it->first, it->first+result.atom->getSize()); + return result; +} + +// "scattered" relocations enable you to offset into an atom past the end of it +// baseAddr is the address of the target atom, +// realAddr is the points into it +template +AtomAndOffset Reader::findAtomAndOffset(uint32_t baseAddr, uint32_t realAddr) +{ + std::map::iterator it = fAddrToAtom.find(baseAddr); + if ( it != fAddrToAtom.end() ) { + AtomAndOffset result; + result.atom = it->second; + result.offset = realAddr - it->first; + //fprintf(stderr, "findAtomAndOffset(0x%08X, 0x%08X) => %s + 0x%08X\n", baseAddr, realAddr, result.atom->getDisplayName(), result.offset); + return result; + } + // getting here means we have a scattered relocation to an address without a label + // we should never get here... + // one case we do get here is because sometimes the compiler generates non-lazy pointers in the __data section + return findAtomAndOffset(realAddr); +} + + +/* Skip over a LEB128 value (signed or unsigned). */ +static void +skip_leb128 (const uint8_t ** offset, const uint8_t * end) +{ + while (*offset != end && **offset >= 0x80) + (*offset)++; + if (*offset != end) + (*offset)++; +} + +/* Read a ULEB128 into a 64-bit word. Return (uint64_t)-1 on overflow + or error. On overflow, skip past the rest of the uleb128. */ +static uint64_t +read_uleb128 (const uint8_t ** offset, const uint8_t * end) +{ + uint64_t result = 0; + int bit = 0; + + do { + uint64_t b; + + if (*offset == end) + return (uint64_t) -1; + + b = **offset & 0x7f; + + if (bit >= 64 || b << bit >> bit != b) + result = (uint64_t) -1; + else + result |= b << bit, bit += 7; + } while (*(*offset)++ >= 0x80); + return result; +} + + +/* Skip over a DWARF attribute of form FORM. */ +template +bool Reader::skip_form(const uint8_t ** offset, const uint8_t * end, uint64_t form, + uint8_t addr_size, bool dwarf64) +{ + int64_t sz=0; + + switch (form) + { + case DW_FORM_addr: + sz = addr_size; + break; + + case DW_FORM_block2: + if (end - *offset < 2) + return false; + sz = 2 + A::P::E::get16(*(uint16_t*)offset); + break; + + case DW_FORM_block4: + if (end - *offset < 4) + return false; + sz = 2 + A::P::E::get32(*(uint32_t*)offset); + break; + + case DW_FORM_data2: + case DW_FORM_ref2: + sz = 2; + break; + + case DW_FORM_data4: + case DW_FORM_ref4: + sz = 4; + break; + + case DW_FORM_data8: + case DW_FORM_ref8: + sz = 8; + break; + + case DW_FORM_string: + while (*offset != end && **offset) + ++*offset; + case DW_FORM_data1: + case DW_FORM_flag: + case DW_FORM_ref1: + sz = 1; + break; + + case DW_FORM_block: + sz = read_uleb128 (offset, end); + break; + + case DW_FORM_block1: + if (*offset == end) + return false; + sz = 1 + **offset; + break; + + case DW_FORM_sdata: + case DW_FORM_udata: + case DW_FORM_ref_udata: + skip_leb128 (offset, end); + return true; + + case DW_FORM_strp: + case DW_FORM_ref_addr: + sz = dwarf64 ? 8 : 4; + break; + + default: + return false; + } + if (end - *offset < sz) + return false; + *offset += sz; + return true; +} + +// Look at the compilation unit DIE and determine +// its NAME, compilation directory (in COMP_DIR) and its +// line number information offset (in STMT_LIST). NAME and COMP_DIR +// may be NULL (especially COMP_DIR) if they are not in the .o file; +// STMT_LIST will be (uint64_t) -1. +// +// At present this assumes that there's only one compilation unit DIE. +// +template +bool Reader::read_comp_unit(const char ** name, const char ** comp_dir, + uint64_t *stmt_list) +{ + const uint8_t * debug_info; + const uint8_t * debug_abbrev; + const uint8_t * di; + const uint8_t * da; + const uint8_t * end; + const uint8_t * enda; + uint64_t sz; + uint16_t vers; + uint64_t abbrev_base; + uint64_t abbrev; + uint8_t address_size; + bool dwarf64; + + *name = NULL; + *comp_dir = NULL; + *stmt_list = (uint64_t) -1; + + if ( (fDwarfDebugInfoSect == NULL) || (fDwarfDebugAbbrevSect == NULL) ) + return false; + + debug_info = (uint8_t*)(fHeader) + fDwarfDebugInfoSect->offset(); + debug_abbrev = (uint8_t*)(fHeader) + fDwarfDebugAbbrevSect->offset(); + di = debug_info; + + if (fDwarfDebugInfoSect->size() < 12) + /* Too small to be a real debug_info section. */ + return false; + sz = A::P::E::get32(*(uint32_t*)di); + di += 4; + dwarf64 = sz == 0xffffffff; + if (dwarf64) + sz = A::P::E::get64(*(uint64_t*)di), di += 8; + else if (sz > 0xffffff00) + /* Unknown dwarf format. */ + return false; + + /* Verify claimed size. */ + if (sz + (di - debug_info) > fDwarfDebugInfoSect->size() || sz <= (dwarf64 ? 23 : 11)) + return false; + + vers = A::P::E::get16(*(uint16_t*)di); + if (vers < 2 || vers > 3) + /* DWARF version wrong for this code. + Chances are we could continue anyway, but we don't know for sure. */ + return false; + di += 2; + + /* Find the debug_abbrev section. */ + abbrev_base = dwarf64 ? A::P::E::get64(*(uint64_t*)di) : A::P::E::get32(*(uint32_t*)di); + di += dwarf64 ? 8 : 4; + + if (abbrev_base > fDwarfDebugAbbrevSect->size()) + return false; + da = debug_abbrev + abbrev_base; + enda = debug_abbrev + fDwarfDebugAbbrevSect->size(); + + address_size = *di++; + + /* Find the abbrev number we're looking for. */ + end = di + sz; + abbrev = read_uleb128 (&di, end); + if (abbrev == (uint64_t) -1) + return false; + + /* Skip through the debug_abbrev section looking for that abbrev. */ + for (;;) + { + uint64_t this_abbrev = read_uleb128 (&da, enda); + uint64_t attr; + + if (this_abbrev == abbrev) + /* This is almost always taken. */ + break; + skip_leb128 (&da, enda); /* Skip the tag. */ + if (da == enda) + return false; + da++; /* Skip the DW_CHILDREN_* value. */ + + do { + attr = read_uleb128 (&da, enda); + skip_leb128 (&da, enda); + } while (attr != 0 && attr != (uint64_t) -1); + if (attr != 0) + return false; + } + + /* Check that the abbrev is one for a DW_TAG_compile_unit. */ + if (read_uleb128 (&da, enda) != DW_TAG_compile_unit) + return false; + if (da == enda) + return false; + da++; /* Skip the DW_CHILDREN_* value. */ + + /* Now, go through the DIE looking for DW_AT_name, + DW_AT_comp_dir, and DW_AT_stmt_list. */ + for (;;) + { + uint64_t attr = read_uleb128 (&da, enda); + uint64_t form = read_uleb128 (&da, enda); + + if (attr == (uint64_t) -1) + return false; + else if (attr == 0) + return true; + + if (form == DW_FORM_indirect) + form = read_uleb128 (&di, end); + + if (attr == DW_AT_name && form == DW_FORM_string) + *name = (const char *) di; + else if (attr == DW_AT_comp_dir && form == DW_FORM_string) + *comp_dir = (const char *) di; + /* Really we should support DW_FORM_strp here, too, but + there's usually no reason for the producer to use that form + for the DW_AT_name and DW_AT_comp_dir attributes. */ + else if (attr == DW_AT_stmt_list && form == DW_FORM_data4) + *stmt_list = A::P::E::get32(*(uint32_t*)di); + else if (attr == DW_AT_stmt_list && form == DW_FORM_data8) + *stmt_list = A::P::E::get64(*(uint64_t*)di); + if (! skip_form (&di, end, form, address_size, dwarf64)) + return false; + } +} + +template +const char* Reader::assureFullPath(const char* path) +{ + if ( path[0] == '/' ) + return path; + char cwdbuff[MAXPATHLEN]; + if ( getcwd(cwdbuff, MAXPATHLEN) != NULL ) { + char* result; + asprintf(&result, "%s/%s", cwdbuff, path); + if ( result != NULL ) + return result; + } + return path; +} + + +// +// +// To implement architecture xxx, you must write template specializations for the following six methods: +// Reader::validFile() +// Reader::addRelocReference() +// Reference::getDescription() +// +// + + +template <> +bool Reader::validFile(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return false; + if ( header->cputype() != CPU_TYPE_POWERPC ) + return false; + if ( header->filetype() != MH_OBJECT ) + return false; + return true; +} + +template <> +bool Reader::validFile(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC_64 ) + return false; + if ( header->cputype() != CPU_TYPE_POWERPC64 ) + return false; + if ( header->filetype() != MH_OBJECT ) + return false; + return true; +} + +template <> +bool Reader::validFile(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return false; + if ( header->cputype() != CPU_TYPE_I386 ) + return false; + if ( header->filetype() != MH_OBJECT ) + return false; + return true; +} + + + +template +bool Reader::isWeakImportSymbol(const macho_nlist

* sym) +{ + return ( ((sym->n_type() & N_TYPE) == N_UNDF) && ((sym->n_desc() & N_WEAK_REF) != 0) ); +} + +template <> +bool Reader::addRelocReference(const macho_section* sect, const macho_relocation_info* reloc) +{ + return addRelocReference_powerpc(sect, reloc); +} + +template <> +bool Reader::addRelocReference(const macho_section* sect, const macho_relocation_info* reloc) +{ + return addRelocReference_powerpc(sect, reloc); +} + + +// +// ppc and ppc64 both use the same relocations, so process them in one common routine +// +template +bool Reader::addRelocReference_powerpc(const macho_section* sect, + const macho_relocation_info* reloc) +{ + uint32_t srcAddr; + uint32_t dstAddr; + uint32_t* fixUpPtr; + int32_t displacement = 0; + uint32_t instruction = 0; + uint32_t offsetInTarget; + int16_t lowBits; + bool result = false; + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + const macho_relocation_info

* nextReloc = &reloc[1]; + const char* targetName = NULL; + bool weakImport = false; + fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address()); + if ( reloc->r_type() != PPC_RELOC_PAIR ) + instruction = BigEndian::get32(*fixUpPtr); + srcAddr = sect->addr() + reloc->r_address(); + if ( reloc->r_extern() ) { + const macho_nlist

* targetSymbol = &fSymbols[reloc->r_symbolnum()]; + targetName = &fStrings[targetSymbol->n_strx()]; + weakImport = this->isWeakImportSymbol(targetSymbol); + } + switch ( reloc->r_type() ) { + case PPC_RELOC_BR24: + { + if ( (instruction & 0x4C000000) == 0x48000000 ) { + displacement = (instruction & 0x03FFFFFC); + if ( (displacement & 0x02000000) != 0 ) + displacement |= 0xFC000000; + } + else { + printf("bad instruction for BR24 reloc"); + } + if ( reloc->r_extern() ) { + offsetInTarget = srcAddr + displacement; + if ( weakImport ) + makeByNameReference(A::kBranch24WeakImport, srcAddr, targetName, offsetInTarget); + else + makeByNameReference(A::kBranch24, srcAddr, targetName, offsetInTarget); + } + else { + dstAddr = srcAddr + displacement; + // if this is a branch to a stub, we need to see if the stub is for a weak imported symbol + ObjectFile::Atom* atom = findAtomAndOffset(dstAddr).atom; + if ( (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn) + && ((AnonymousAtom*)atom)->isWeakImportStub() ) + makeReference(A::kBranch24WeakImport, srcAddr, dstAddr); + else + makeReference(A::kBranch24, srcAddr, dstAddr); + } + } + break; + case PPC_RELOC_BR14: + { + displacement = (instruction & 0x0000FFFC); + if ( (displacement & 0x00008000) != 0 ) + displacement |= 0xFFFF0000; + if ( reloc->r_extern() ) { + offsetInTarget = srcAddr + displacement; + makeByNameReference(A::kBranch14, srcAddr, targetName, offsetInTarget); + } + else { + dstAddr = srcAddr + displacement; + makeReference(A::kBranch14, srcAddr, dstAddr); + } + } + break; + case PPC_RELOC_PAIR: + // skip, processed by a previous look ahead + break; + case PPC_RELOC_LO16: + { + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { + printf("PPC_RELOC_LO16 missing following pair\n"); + break; + } + result = true; + lowBits = (instruction & 0xFFFF); + if ( reloc->r_extern() ) { + offsetInTarget = (nextReloc->r_address() << 16) | ((uint32_t)lowBits & 0x0000FFFF); + makeByNameReference(A::kAbsLow16, srcAddr, targetName, offsetInTarget); + } + else { + dstAddr = (nextReloc->r_address() << 16) + ((uint32_t)lowBits & 0x0000FFFF); + makeReference(A::kAbsLow16, srcAddr, dstAddr); + } + } + break; + case PPC_RELOC_LO14: + { + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { + printf("PPC_RELOC_LO14 missing following pair\n"); + break; + } + result = true; + lowBits = (instruction & 0xFFFC); + if ( reloc->r_extern() ) { + offsetInTarget = (nextReloc->r_address() << 16) | ((uint32_t)lowBits & 0x0000FFFF); + makeByNameReference(A::kAbsLow14, srcAddr, targetName, offsetInTarget); + } + else { + dstAddr = (nextReloc->r_address() << 16) | ((uint32_t)lowBits & 0x0000FFFF); + Reference* ref = makeReference(A::kAbsLow14, srcAddr, dstAddr); + BaseAtom* target = ((BaseAtom*)&(ref->getTarget())); + if ( target != NULL ) + target->alignAtLeast(2); + } + } + break; + case PPC_RELOC_HI16: + { + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { + printf("PPC_RELOC_HI16 missing following pair\n"); + break; + } + result = true; + if ( reloc->r_extern() ) { + offsetInTarget = ((instruction & 0x0000FFFF) << 16) | (nextReloc->r_address() & 0x0000FFFF); + makeByNameReference(A::kAbsHigh16, srcAddr, targetName, offsetInTarget); + } + else { + dstAddr = ((instruction & 0x0000FFFF) << 16) | (nextReloc->r_address() & 0x0000FFFF); + makeReference(A::kAbsHigh16, srcAddr, dstAddr); + } + } + break; + case PPC_RELOC_HA16: + { + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { + printf("PPC_RELOC_HA16 missing following pair\n"); + break; + } + result = true; + lowBits = (nextReloc->r_address() & 0x0000FFFF); + if ( reloc->r_extern() ) { + offsetInTarget = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits; + makeByNameReference(A::kAbsHigh16AddLow, srcAddr, targetName, offsetInTarget); + } + else { + dstAddr = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits; + makeReference(A::kAbsHigh16AddLow, srcAddr, dstAddr); + } + } + break; + case PPC_RELOC_VANILLA: + { + pint_t pointerValue = P::getP(*((pint_t*)fixUpPtr)); + if ( reloc->r_extern() ) { + if ( weakImport ) + makeByNameReference(A::kPointerWeakImport, srcAddr, targetName, pointerValue); + else + makeByNameReference(A::kPointer, srcAddr, targetName, pointerValue); + } + else { + makeReference(A::kPointer, srcAddr, pointerValue); + } + } + break; + case PPC_RELOC_JBSR: + // this is from -mlong-branch codegen. We ignore the jump island + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { + printf("PPC_RELOC_JBSR missing following pair\n"); + break; + } + result = true; + makeReference(A::kBranch24, srcAddr, nextReloc->r_address()); + break; + default: + printf("unknown relocation type %d\n", reloc->r_type()); + } + } + else { + const macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; + srcAddr = sect->addr() + sreloc->r_address(); + dstAddr = sreloc->r_value(); + uint32_t betterDstAddr; + fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + sreloc->r_address()); + const macho_scattered_relocation_info

* nextSReloc = &sreloc[1]; + const macho_relocation_info

* nextReloc = &reloc[1]; + // file format allows pair to be scattered or not + bool nextRelocIsPair = false; + uint32_t nextRelocAddress = 0; + uint32_t nextRelocValue = 0; + if ( (nextReloc->r_address() & R_SCATTERED) == 0 ) { + if ( nextReloc->r_type() == PPC_RELOC_PAIR ) { + nextRelocIsPair = true; + nextRelocAddress = nextReloc->r_address(); + result = true; + } + } + else { + if ( nextSReloc->r_type() == PPC_RELOC_PAIR ) { + nextRelocIsPair = true; + nextRelocAddress = nextSReloc->r_address(); + nextRelocValue = nextSReloc->r_value(); + result = true; + } + } + switch (sreloc->r_type()) { + case PPC_RELOC_VANILLA: + { + betterDstAddr = P::getP(*(pint_t*)fixUpPtr); + //fprintf(stderr, "scattered pointer reloc: srcAddr=0x%08X, dstAddr=0x%08X, pointer=0x%08X\n", srcAddr, dstAddr, betterDstAddr); + // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr) + makeReferenceWithToBase(A::kPointer, srcAddr, betterDstAddr, dstAddr); + } + break; + case PPC_RELOC_BR14: + { + instruction = BigEndian::get32(*fixUpPtr); + displacement = (instruction & 0x0000FFFC); + if ( (displacement & 0x00008000) != 0 ) + displacement |= 0xFFFF0000; + betterDstAddr = srcAddr+displacement; + //fprintf(stderr, "betterDstAddr=0x%08X, srcAddr=0x%08X, displacement=0x%08X\n", betterDstAddr, srcAddr, displacement); + makeReferenceWithToBase(A::kBranch14, srcAddr, betterDstAddr, dstAddr); + } + break; + case PPC_RELOC_BR24: + { + instruction = BigEndian::get32(*fixUpPtr); + if ( (instruction & 0x4C000000) == 0x48000000 ) { + displacement = (instruction & 0x03FFFFFC); + if ( (displacement & 0x02000000) != 0 ) + displacement |= 0xFC000000; + betterDstAddr = srcAddr+displacement; + makeReferenceWithToBase(A::kBranch24, srcAddr, betterDstAddr, dstAddr); + } + } + break; + case PPC_RELOC_LO16_SECTDIFF: + { + if ( ! nextRelocIsPair ) { + printf("PPC_RELOC_LO16_SECTDIFF missing following PAIR\n"); + break; + } + instruction = BigEndian::get32(*fixUpPtr); + lowBits = (instruction & 0xFFFF); + displacement = (nextRelocAddress << 16) | ((uint32_t)lowBits & 0x0000FFFF); + makeReferenceWithToBase(A::kPICBaseLow16, srcAddr, nextRelocValue, nextRelocValue + displacement, dstAddr); + } + break; + case PPC_RELOC_LO14_SECTDIFF: + { + if ( ! nextRelocIsPair ) { + printf("PPC_RELOC_LO14_SECTDIFF missing following PAIR\n"); + break; + } + instruction = BigEndian::get32(*fixUpPtr); + lowBits = (instruction & 0xFFFC); + displacement = (nextRelocAddress << 16) | ((uint32_t)lowBits & 0x0000FFFF); + Reference* ref = makeReferenceWithToBase(A::kPICBaseLow14, srcAddr, nextRelocValue, nextRelocValue + displacement, dstAddr); + BaseAtom* target = ((BaseAtom*)&(ref->getTarget())); + if ( target != NULL ) // can be NULL if target is turned into by-name reference + target->alignAtLeast(2); + } + break; + case PPC_RELOC_HA16_SECTDIFF: + { + if ( ! nextRelocIsPair ) { + printf("PPC_RELOC_HA16_SECTDIFF missing following PAIR\n"); + break; + } + instruction = BigEndian::get32(*fixUpPtr); + lowBits = (nextRelocAddress & 0x0000FFFF); + displacement = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits; + makeReferenceWithToBase(A::kPICBaseHigh16, srcAddr, nextRelocValue, nextRelocValue + displacement, dstAddr); + } + break; + case PPC_RELOC_LO14: + { + if ( ! nextRelocIsPair ) { + printf("PPC_RELOC_LO14 missing following PAIR\n"); + break; + } + instruction = BigEndian::get32(*fixUpPtr); + lowBits = (instruction & 0xFFFC); + betterDstAddr = (nextRelocAddress << 16) + ((uint32_t)lowBits & 0x0000FFFF); + makeReferenceWithToBase(A::kAbsLow14, srcAddr, betterDstAddr, dstAddr); + } + break; + case PPC_RELOC_LO16: + { + if ( ! nextRelocIsPair ) { + printf("PPC_RELOC_LO16 missing following PAIR\n"); + break; + } + instruction = BigEndian::get32(*fixUpPtr); + lowBits = (instruction & 0xFFFF); + betterDstAddr = (nextRelocAddress << 16) + ((uint32_t)lowBits & 0x0000FFFF); + makeReferenceWithToBase(A::kAbsLow16, srcAddr, betterDstAddr, dstAddr); + } + break; + case PPC_RELOC_HA16: + { + if ( ! nextRelocIsPair ) { + printf("PPC_RELOC_HA16 missing following PAIR\n"); + break; + } + instruction = BigEndian::get32(*fixUpPtr); + lowBits = (nextRelocAddress & 0xFFFF); + betterDstAddr = ((instruction & 0xFFFF) << 16) + (int32_t)lowBits; + makeReferenceWithToBase(A::kAbsHigh16AddLow, srcAddr, betterDstAddr, dstAddr); + } + break; + case PPC_RELOC_SECTDIFF: + case PPC_RELOC_LOCAL_SECTDIFF: + { + if ( ! nextRelocIsPair ) { + printf("PPC_RELOC_SECTDIFF missing following pair\n"); + break; + } + makeReference(pointerDiffKindForLength_powerpc(sreloc->r_length()), srcAddr, nextRelocValue, dstAddr); + } + break; + case PPC_RELOC_PAIR: + break; + case PPC_RELOC_HI16_SECTDIFF: + printf("unexpected scattered relocation type PPC_RELOC_HI16_SECTDIFF\n"); + break; + default: + printf("unknown scattered relocation type %d\n", sreloc->r_type()); + } + } + return result; +} + +template <> +ppc::ReferenceKinds Reader::pointerDiffKindForLength_powerpc(uint8_t r_length) +{ + if ( r_length == 2 ) + return ppc::kPointerDiff32; + else + throw "bad diff relocations r_length for ppc architecture"; + } + +template <> +ppc64::ReferenceKinds Reader::pointerDiffKindForLength_powerpc(uint8_t r_length) +{ + if ( r_length == 2 ) + return ppc64::kPointerDiff32; + else if ( r_length == 3 ) + return ppc64::kPointerDiff64; + else + throw "bad diff relocations r_length for ppc64 architecture"; + } + +template <> +bool Reader::addRelocReference(const macho_section* sect, const macho_relocation_info* reloc) +{ + uint32_t srcAddr; + uint32_t dstAddr; + uint32_t* fixUpPtr; + bool result = false; + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + srcAddr = sect->addr() + reloc->r_address(); + fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address()); + switch ( reloc->r_type() ) { + case GENERIC_RELOC_VANILLA: + { + if ( reloc->r_length() != 2 ) + throw "bad vanilla relocation length"; + x86::ReferenceKinds kind; + uint32_t pointerValue = E::get32(*fixUpPtr); + if ( reloc->r_pcrel() ) { + kind = x86::kPCRel32; + pointerValue += srcAddr + sizeof(uint32_t); + } + else { + kind = x86::kPointer; + } + if ( reloc->r_extern() ) { + const macho_nlist

* targetSymbol = &fSymbols[reloc->r_symbolnum()]; + if ( this->isWeakImportSymbol(targetSymbol) ) + kind = x86::kPointerWeakImport; + const char* targetName = &fStrings[targetSymbol->n_strx()]; + makeByNameReference(kind, srcAddr, targetName, pointerValue); + } + else { + // if this is a branch to a stub, we need to see if the stub is for a weak imported symbol + ObjectFile::Atom* atom = findAtomAndOffset(pointerValue).atom; + if ( reloc->r_pcrel() && (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn) + && ((AnonymousAtom*)atom)->isWeakImportStub() ) + makeReference(x86::kPCRel32WeakImport, srcAddr, pointerValue); + else + makeReference(kind, srcAddr, pointerValue); + } + } + break; + default: + printf("unknown relocation type %d\n", reloc->r_type()); + } + } + else { + const macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; + srcAddr = sect->addr() + sreloc->r_address(); + dstAddr = sreloc->r_value(); + fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + sreloc->r_address()); + const macho_scattered_relocation_info

* nextSReloc = &sreloc[1]; + const macho_relocation_info

* nextReloc = &reloc[1]; + pint_t betterDstAddr; + // file format allows pair to be scattered or not + bool nextRelocIsPair = false; + uint32_t nextRelocAddress = 0; + uint32_t nextRelocValue = 0; + if ( (nextReloc->r_address() & R_SCATTERED) == 0 ) { + if ( nextReloc->r_type() == PPC_RELOC_PAIR ) { + nextRelocIsPair = true; + nextRelocAddress = nextReloc->r_address(); + result = true; + } + } + else { + if ( nextSReloc->r_type() == PPC_RELOC_PAIR ) { + nextRelocIsPair = true; + nextRelocAddress = nextSReloc->r_address(); + nextRelocValue = nextSReloc->r_value(); + } + } + switch (sreloc->r_type()) { + case GENERIC_RELOC_VANILLA: + betterDstAddr = LittleEndian::get32(*fixUpPtr); + //fprintf(stderr, "pointer reloc: srcAddr=0x%08X, dstAddr=0x%08X, pointer=0x%08lX\n", srcAddr, dstAddr, betterDstAddr); + // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr) + if ( sreloc->r_pcrel() ) { + betterDstAddr += srcAddr + 4; + makeReferenceWithToBase(x86::kPCRel32, srcAddr, betterDstAddr, dstAddr); + } + else { + makeReferenceWithToBase(x86::kPointer, srcAddr, betterDstAddr, dstAddr); + } + break; + case GENERIC_RELOC_SECTDIFF: + case GENERIC_RELOC_LOCAL_SECTDIFF: + { + if ( !nextRelocIsPair ) { + printf("GENERIC_RELOC_SECTDIFF missing following pair\n"); + break; + } + if ( sreloc->r_length() != 2 ) + throw "bad length for GENERIC_RELOC_SECTDIFF"; + betterDstAddr = LittleEndian::get32(*fixUpPtr); + makeReferenceWithToBase(x86::kPointerDiff, srcAddr, nextRelocValue, betterDstAddr+nextRelocValue, dstAddr); + } + break; + case GENERIC_RELOC_PAIR: + // do nothing, already used via a look ahead + break; + default: + printf("unknown scattered relocation type %d\n", sreloc->r_type()); + } + } + return result; +} + + + +template <> +const char* Reference::getDescription() const +{ + static char temp[1024]; + switch( fKind ) { + case x86::kNoFixUp: + sprintf(temp, "reference to "); + break; + case x86::kFollowOn: + sprintf(temp, "followed by "); + break; + case x86::kPointerWeakImport: + sprintf(temp, "offset 0x%04X, weak import pointer to ", fFixUpOffsetInSrc); + break; + case x86::kPointer: + sprintf(temp, "offset 0x%04X, pointer to ", fFixUpOffsetInSrc); + break; + case x86::kPointerDiff: + { + // by-name references have quoted names + const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; + const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; + sprintf(temp, "offset 0x%04X, 32-bit pointer difference: (&%s%s%s + 0x%08X) - (&%s%s%s + 0x%08X)", + fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + return temp; + } + break; + case x86::kPCRel32WeakImport: + sprintf(temp, "offset 0x%04X, rel32 reference to weak imported ", fFixUpOffsetInSrc); + break; + case x86::kPCRel32: + sprintf(temp, "offset 0x%04X, rel32 reference to ", fFixUpOffsetInSrc); + break; + } + // always quote by-name references + if ( fToTargetName != NULL ) { + strcat(temp, "\""); + strcat(temp, fToTargetName); + strcat(temp, "\""); + } + else if ( fToTarget.atom != NULL ) { + strcat(temp, fToTarget.atom->getDisplayName()); + } + else { + strcat(temp, "NULL target"); + } + if ( fToTarget.offset != 0 ) + sprintf(&temp[strlen(temp)], " plus 0x%08X", fToTarget.offset); + + return temp; +} + + +template <> +const char* Reference::getDescription() const +{ + static char temp[1024]; + switch( fKind ) { + case ppc::kNoFixUp: + sprintf(temp, "reference to "); + break; + case ppc::kFollowOn: + sprintf(temp, "followed by "); + break; + case ppc::kPointerWeakImport: + sprintf(temp, "offset 0x%04X, weak import pointer to ", fFixUpOffsetInSrc); + break; + case ppc::kPointer: + sprintf(temp, "offset 0x%04X, pointer to ", fFixUpOffsetInSrc); + break; + case ppc::kPointerDiff32: + { + // by-name references have quoted names + const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; + const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; + sprintf(temp, "offset 0x%04X, 32-bit pointer difference: (&%s%s%s + %d) - (&%s%s%s + %d)", + fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + return temp; + } + case ppc::kPointerDiff64: + throw "unsupported refrence kind"; + break; + case ppc::kBranch24WeakImport: + sprintf(temp, "offset 0x%04X, pc-rel branch fixup to weak imported ", fFixUpOffsetInSrc); + break; + case ppc::kBranch24: + case ppc::kBranch14: + sprintf(temp, "offset 0x%04X, pc-rel branch fixup to ", fFixUpOffsetInSrc); + break; + case ppc::kPICBaseLow16: + sprintf(temp, "offset 0x%04X, low 16 fixup from pic-base offset 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.offset); + break; + case ppc::kPICBaseLow14: + sprintf(temp, "offset 0x%04X, low 14 fixup from pic-base offset 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.offset); + break; + case ppc::kPICBaseHigh16: + sprintf(temp, "offset 0x%04X, high 16 fixup from pic-base offset 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.offset); + break; + case ppc::kAbsLow16: + sprintf(temp, "offset 0x%04X, low 16 fixup to absolute address of ", fFixUpOffsetInSrc); + break; + case ppc::kAbsLow14: + sprintf(temp, "offset 0x%04X, low 14 fixup to absolute address of ", fFixUpOffsetInSrc); + break; + case ppc::kAbsHigh16: + sprintf(temp, "offset 0x%04X, high 16 fixup to absolute address of ", fFixUpOffsetInSrc); + break; + case ppc::kAbsHigh16AddLow: + sprintf(temp, "offset 0x%04X, high 16 fixup to absolute address of ", fFixUpOffsetInSrc); + break; + } + // always quote by-name references + if ( fToTargetName != NULL ) { + strcat(temp, "\""); + strcat(temp, fToTargetName); + strcat(temp, "\""); + } + else if ( fToTarget.atom != NULL ) { + strcat(temp, fToTarget.atom->getDisplayName()); + } + else { + strcat(temp, "NULL target"); + } + if ( fToTarget.offset != 0 ) + sprintf(&temp[strlen(temp)], " plus 0x%08X", fToTarget.offset); + + return temp; +} + +template <> +const char* Reference::getDescription() const +{ + static char temp[1024]; + switch( fKind ) { + case ppc64::kNoFixUp: + sprintf(temp, "reference to "); + break; + case ppc64::kFollowOn: + sprintf(temp, "followed by "); + break; + case ppc64::kPointerWeakImport: + sprintf(temp, "offset 0x%04llX, weak import pointer to ", fFixUpOffsetInSrc); + break; + case ppc64::kPointer: + sprintf(temp, "offset 0x%04llX, pointer to ", fFixUpOffsetInSrc); + break; + case ppc64::kPointerDiff64: + { + // by-name references have quoted names + const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; + const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; + sprintf(temp, "offset 0x%04llX, 64-bit pointer difference: (&%s%s%s + %u) - (&%s%s%s + %u)", + fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + return temp; + } + case ppc64::kPointerDiff32: + { + // by-name references have quoted names + const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; + const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; + sprintf(temp, "offset 0x%04llX, 32-bit pointer difference: (&%s%s%s + %u) - (&%s%s%s + %u)", + fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + return temp; + } + case ppc64::kBranch24WeakImport: + sprintf(temp, "offset 0x%04llX, pc-rel branch fixup to weak imported ", fFixUpOffsetInSrc); + break; + case ppc64::kBranch24: + case ppc64::kBranch14: + sprintf(temp, "offset 0x%04llX, pc-rel branch fixup to ", fFixUpOffsetInSrc); + break; + case ppc64::kPICBaseLow16: + sprintf(temp, "offset 0x%04llX, low 16 fixup from pic-base offset 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.offset); + break; + case ppc64::kPICBaseLow14: + sprintf(temp, "offset 0x%04llX, low 14 fixup from pic-base offset 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.offset); + break; + case ppc64::kPICBaseHigh16: + sprintf(temp, "offset 0x%04llX, high 16 fixup from pic-base offset 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.offset); + break; + case ppc64::kAbsLow16: + sprintf(temp, "offset 0x%04llX, low 16 fixup to absolute address of ", fFixUpOffsetInSrc); + break; + case ppc64::kAbsLow14: + sprintf(temp, "offset 0x%04llX, low 14 fixup to absolute address of ", fFixUpOffsetInSrc); + break; + case ppc64::kAbsHigh16: + sprintf(temp, "offset 0x%04llX, high 16 fixup to absolute address of ", fFixUpOffsetInSrc); + break; + case ppc64::kAbsHigh16AddLow: + sprintf(temp, "offset 0x%04llX, high 16 fixup to absolute address of ", fFixUpOffsetInSrc); + break; + } + // always quote by-name references + if ( fToTargetName != NULL ) { + strcat(temp, "\""); + strcat(temp, fToTargetName); + strcat(temp, "\""); + } + else if ( fToTarget.atom != NULL ) { + strcat(temp, fToTarget.atom->getDisplayName()); + } + else { + strcat(temp, "NULL target"); + } + if ( fToTarget.offset != 0 ) + sprintf(&temp[strlen(temp)], " plus 0x%08X", fToTarget.offset); + + return temp; +} + + + + +}; // namespace relocatable +}; // namespace mach_o + +#endif // __OBJECT_FILE_MACH_O__ diff --git a/src/MachOWriterExecutable.hpp b/src/MachOWriterExecutable.hpp new file mode 100644 index 0000000..30aa24b --- /dev/null +++ b/src/MachOWriterExecutable.hpp @@ -0,0 +1,4574 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __EXECUTABLE_MACH_O__ +#define __EXECUTABLE_MACH_O__ + +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "ObjectFile.h" +#include "ExecutableFile.h" +#include "Options.h" + +#include "MachOFileAbstraction.hpp" + + +// +// +// To implement architecture xxx, you must write template specializations for the following methods: +// MachHeaderAtom::setHeaderInfo() +// ThreadsLoadCommandsAtom::getSize() +// ThreadsLoadCommandsAtom::copyRawContent() +// Writer::addObjectRelocs() +// Writer::fixUpReferenceRelocatable() +// Writer::fixUpReferenceFinal() +// Writer::stubableReferenceKind() +// Writer::weakImportReferenceKind() +// Writer::GOTReferenceKind() +// + + +namespace mach_o { +namespace executable { + +// forward references +template class WriterAtom; +template class PageZeroAtom; +template class CustomStackAtom; +template class MachHeaderAtom; +template class SegmentLoadCommandsAtom; +template class SymbolTableLoadCommandsAtom; +template class ThreadsLoadCommandsAtom; +template class DylibIDLoadCommandsAtom; +template class RoutinesLoadCommandsAtom; +template class DyldLoadCommandsAtom; +template class UUIDLoadCommandAtom; +template class LinkEditAtom; +template class SectionRelocationsLinkEditAtom; +template class LocalRelocationsLinkEditAtom; +template class ExternalRelocationsLinkEditAtom; +template class SymbolTableLinkEditAtom; +template class IndirectTableLinkEditAtom; +template class StringsLinkEditAtom; +template class LoadCommandsPaddingAtom; +template class StubAtom; +template class StubHelperAtom; +template class LazyPointerAtom; +template class NonLazyPointerAtom; + + +// SectionInfo should be nested inside Writer, but I can't figure out how to make the type accessible to the Atom classes +class SectionInfo : public ObjectFile::Section { +public: + SectionInfo() : fFileOffset(0), fSize(0), fRelocCount(0), fRelocOffset(0), fIndirectSymbolOffset(0), + fAlignment(0), fAllLazyPointers(false), fAllNonLazyPointers(false), fAllStubs(false), + fAllSelfModifyingStubs(false), fAllZeroFill(false), fVirtualSection(false) + { fSegmentName[0] = '\0'; fSectionName[0] = '\0'; } + void setIndex(unsigned int index) { fIndex=index; } + std::vector fAtoms; + char fSegmentName[20]; + char fSectionName[20]; + uint64_t fFileOffset; + uint64_t fSize; + uint32_t fRelocCount; + uint32_t fRelocOffset; + uint32_t fIndirectSymbolOffset; + uint8_t fAlignment; + bool fAllLazyPointers; + bool fAllNonLazyPointers; + bool fAllStubs; + bool fAllSelfModifyingStubs; + bool fAllZeroFill; + bool fVirtualSection; +}; + +// SegmentInfo should be nested inside Writer, but I can't figure out how to make the type accessible to the Atom classes +class SegmentInfo +{ +public: + SegmentInfo() : fInitProtection(0), fMaxProtection(0), fFileOffset(0), fFileSize(0), + fBaseAddress(0), fSize(0), fFixedAddress(false) { fName[0] = '\0'; } + std::vector fSections; + char fName[20]; + uint32_t fInitProtection; + uint32_t fMaxProtection; + uint64_t fFileOffset; + uint64_t fFileSize; + uint64_t fBaseAddress; + uint64_t fSize; + bool fFixedAddress; +}; + +template +class Writer : public ExecutableFile::Writer +{ +public: + Writer(const char* path, Options& options, std::vector& dynamicLibraries); + virtual ~Writer(); + + virtual const char* getPath() { return fFilePath; } + virtual time_t getModificationTime() { return 0; } + virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; } + virtual std::vector& getAtoms() { return fWriterSynthesizedAtoms; } + virtual std::vector* getJustInTimeAtomsFor(const char* name) { return NULL; } + virtual std::vector* getStabs() { return NULL; } + + virtual class ObjectFile::Atom* getUndefinedProxyAtom(const char* name); + virtual uint64_t write(std::vector& atoms, + std::vector& stabs, + class ObjectFile::Atom* entryPointAtom, + class ObjectFile::Atom* dyldHelperAtom, + bool createUUID); + +private: + typedef typename A::P P; + typedef typename A::P::uint_t pint_t; + + enum RelocKind { kRelocNone, kRelocInternal, kRelocExternal }; + + void assignFileOffsets(); + void synthesizeStubs(); + void partitionIntoSections(); + bool addBranchIslands(); + bool addPPCBranchIslands(); + uint8_t branch24Reference(); + void adjustLoadCommandsAndPadding(); + void createDynamicLinkerCommand(); + void createDylibCommands(); + void buildLinkEdit(); + uint64_t writeAtoms(); + void writeNoOps(uint32_t from, uint32_t to); + void collectExportedAndImportedAndLocalAtoms(); + void setNlistRange(std::vector& atoms, uint32_t startIndex, uint32_t count); + void buildSymbolTable(); + void setExportNlist(const ObjectFile::Atom* atom, macho_nlist

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

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

* entry); + uint64_t getAtomLoadAddress(const ObjectFile::Atom* atom); + uint8_t ordinalForLibrary(ObjectFile::Reader* file); + bool shouldExport(const ObjectFile::Atom& atom) const; + void buildFixups(); + void adjustLinkEditSections(); + void buildObjectFileFixups(); + void buildExecutableFixups(); + bool referenceRequiresRuntimeFixUp(const ObjectFile::Reference* ref, bool slideable) const; + void fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const; + void fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const; + void fixUpReference_powerpc(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, + uint8_t buffer[], bool finalLinkedImage) const; + uint32_t symbolIndex(ObjectFile::Atom& atom); + uint32_t addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref); + uint32_t addObjectRelocs_powerpc(ObjectFile::Atom* atom, ObjectFile::Reference* ref); + uint8_t getRelocPointerSize(); + bool stubableReferenceKind(uint8_t kind); + bool GOTReferenceKind(uint8_t kind); + bool weakImportReferenceKind(uint8_t kind); + unsigned int collectStabs(); + uint64_t valueForStab(const ObjectFile::Reader::Stab& stab); + uint32_t stringOffsetForStab(const ObjectFile::Reader::Stab& stab); + uint8_t sectionIndexForStab(const ObjectFile::Reader::Stab& stab); + void addStabs(uint32_t startIndex); + RelocKind relocationNeededInFinalLinkedImage(const ObjectFile::Atom& target) const; + bool illegalRelocInFinalLinkedImage(uint8_t kind, bool slideable); + + + struct DirectLibrary { + class ObjectFile::Reader* fLibrary; + bool fWeak; + bool fReExport; + }; + + friend class WriterAtom; + friend class PageZeroAtom; + friend class CustomStackAtom; + friend class MachHeaderAtom; + friend class SegmentLoadCommandsAtom; + friend class SymbolTableLoadCommandsAtom; + friend class ThreadsLoadCommandsAtom; + friend class DylibIDLoadCommandsAtom; + friend class RoutinesLoadCommandsAtom; + friend class DyldLoadCommandsAtom; + friend class UUIDLoadCommandAtom; + friend class LinkEditAtom; + friend class SectionRelocationsLinkEditAtom; + friend class LocalRelocationsLinkEditAtom; + friend class ExternalRelocationsLinkEditAtom; + friend class SymbolTableLinkEditAtom; +// friend class IndirectTableLinkEditAtom; + friend class StringsLinkEditAtom; + friend class LoadCommandsPaddingAtom; + friend class StubAtom; + friend class StubHelperAtom; + friend class LazyPointerAtom; + friend class NonLazyPointerAtom; + + const char* fFilePath; + Options& fOptions; + int fFileDescriptor; + std::vector* fAllAtoms; + std::vector* fStabs; + class SectionInfo* fLoadCommandsSection; + class SegmentInfo* fLoadCommandsSegment; + class SegmentLoadCommandsAtom* fSegmentCommands; + class SymbolTableLoadCommandsAtom* fSymbolTableCommands; + class LoadCommandsPaddingAtom* fHeaderPadding; + class UUIDLoadCommandAtom* fUUIDAtom; + std::vector fWriterSynthesizedAtoms; + std::vector fSegmentInfos; + class ObjectFile::Atom* fEntryPoint; + class ObjectFile::Atom* fDyldHelper; + std::vector fDirectLibraries; + std::map fLibraryToOrdinal; + std::vector fExportedAtoms; + std::vector fImportedAtoms; + std::vector fLocalSymbolAtoms; + class SectionRelocationsLinkEditAtom* fSectionRelocationsAtom; + class LocalRelocationsLinkEditAtom* fLocalRelocationsAtom; + class ExternalRelocationsLinkEditAtom* fExternalRelocationsAtom; + class SymbolTableLinkEditAtom* fSymbolTableAtom; + class IndirectTableLinkEditAtom* fIndirectTableAtom; + class StringsLinkEditAtom* fStringsAtom; + macho_nlist

* fSymbolTable; + std::vector > fSectionRelocs; + std::vector > fInternalRelocs; + std::vector > fExternalRelocs; + std::map fStubsMap; + std::map fGOTMap; + std::vector*> fAllSynthesizedStubs; + std::vector fAllSynthesizedStubHelpers; + std::vector*> fAllSynthesizedLazyPointers; + std::vector*> fAllSynthesizedNonLazyPointers; + uint32_t fSymbolTableCount; + uint32_t fSymbolTableStabsCount; + uint32_t fSymbolTableStabsStartIndex; + uint32_t fSymbolTableLocalCount; + uint32_t fSymbolTableLocalStartIndex; + uint32_t fSymbolTableExportCount; + uint32_t fSymbolTableExportStartIndex; + uint32_t fSymbolTableImportCount; + uint32_t fSymbolTableImportStartIndex; + uint32_t fLargestAtomSize; + bool fEmitVirtualSections; + bool fHasWeakExports; + bool fReferencesWeakImports; + bool fSeenFollowOnReferences; + std::map fWeakImportMap; +}; + + +class Segment : public ObjectFile::Segment +{ +public: + Segment(const char* name, bool readable, bool writable, bool executable, bool fixedAddress) + : fName(name), fReadable(readable), fWritable(writable), fExecutable(executable), fFixedAddress(fixedAddress) {} + virtual const char* getName() const { return fName; } + virtual bool isContentReadable() const { return fReadable; } + virtual bool isContentWritable() const { return fWritable; } + virtual bool isContentExecutable() const { return fExecutable; } + virtual bool hasFixedAddress() const { return fFixedAddress; } + + static Segment fgTextSegment; + static Segment fgPageZeroSegment; + static Segment fgLinkEditSegment; + static Segment fgStackSegment; + static Segment fgImportSegment; + static Segment fgDataSegment; +private: + const char* fName; + const bool fReadable; + const bool fWritable; + const bool fExecutable; + const bool fFixedAddress; +}; + +Segment Segment::fgPageZeroSegment("__PAGEZERO", false, false, false, true); +Segment Segment::fgTextSegment("__TEXT", true, false, true, false); +Segment Segment::fgLinkEditSegment("__LINKEDIT", true, false, false, false); +Segment Segment::fgStackSegment("__UNIXSTACK", true, true, false, true); +Segment Segment::fgImportSegment("__IMPORT", true, true, true, false); +Segment Segment::fgDataSegment("__DATA", true, true, false, false); + + +template +class WriterAtom : public ObjectFile::Atom +{ +public: + enum Kind { zeropage, machHeaderApp, machHeaderDylib, machHeaderBundle, machHeaderObject, loadCommands, undefinedProxy }; + WriterAtom(Writer& writer, Segment& segment) : fWriter(writer), fSegment(segment) { setDontDeadStrip(); } + + virtual ObjectFile::Reader* getFile() const { return &fWriter; } + virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; } + virtual const char* getName() const { return NULL; } + virtual const char* getDisplayName() const { return this->getName(); } + virtual Scope getScope() const { return ObjectFile::Atom::scopeTranslationUnit; } + virtual DefinitionKind getDefinitionKind() const { return kRegularDefinition; } + virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } + virtual bool isZeroFill() const { return false; } + virtual std::vector& getReferences() const { return fgEmptyReferenceList; } + virtual bool mustRemainInSection() const { return true; } + virtual ObjectFile::Segment& getSegment() const { return fSegment; } + virtual bool requiresFollowOnAtom() const { return false; } + virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } + virtual std::vector* getLineInfo() const { return NULL; } + virtual uint8_t getAlignment() const { return 2; } + virtual void copyRawContent(uint8_t buffer[]) const { throw "don't use copyRawContent"; } + virtual void setScope(Scope) { } + + +protected: + virtual ~WriterAtom() {} + typedef typename A::P P; + typedef typename A::P::E E; + + static std::vector fgEmptyReferenceList; + + Writer& fWriter; + Segment& fSegment; +}; + +template std::vector WriterAtom::fgEmptyReferenceList; + + +template +class PageZeroAtom : public WriterAtom +{ +public: + PageZeroAtom(Writer& writer) : WriterAtom(writer, Segment::fgPageZeroSegment) {} + virtual const char* getDisplayName() const { return "page zero content"; } + virtual bool isZeroFill() const { return true; } + virtual uint64_t getSize() const { return fWriter.fOptions.zeroPageSize(); } + virtual const char* getSectionName() const { return "._zeropage"; } + virtual uint8_t getAlignment() const { return 12; } +private: + using WriterAtom::fWriter; + typedef typename A::P P; +}; + +template +class DsoHandleAtom : public WriterAtom +{ +public: + DsoHandleAtom(Writer& writer) : WriterAtom(writer, Segment::fgTextSegment) {} + virtual const char* getName() const { return "___dso_handle"; } + virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } + virtual uint64_t getSize() const { return 0; } + virtual uint8_t getAlignment() const { return 12; } + virtual const char* getSectionName() const { return "._mach_header"; } + virtual void copyRawContent(uint8_t buffer[]) const {} +}; + + +template +class MachHeaderAtom : public WriterAtom +{ +public: + MachHeaderAtom(Writer& writer) : WriterAtom(writer, Segment::fgTextSegment) {} + virtual const char* getName() const; + virtual const char* getDisplayName() const; + virtual ObjectFile::Atom::Scope getScope() const; + virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const; + virtual uint64_t getSize() const { return sizeof(macho_header); } + virtual uint8_t getAlignment() const { return 12; } + virtual const char* getSectionName() const { return "._mach_header"; } + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; + void setHeaderInfo(macho_header& header) const; +}; + +template +class CustomStackAtom : public WriterAtom +{ +public: + CustomStackAtom(Writer& writer); + virtual const char* getDisplayName() const { return "custom stack content"; } + virtual bool isZeroFill() const { return true; } + virtual uint64_t getSize() const { return fWriter.fOptions.customStackSize(); } + virtual const char* getSectionName() const { return "._stack"; } + virtual uint8_t getAlignment() const { return 12; } +private: + using WriterAtom::fWriter; + typedef typename A::P P; + static bool stackGrowsDown(); +}; + +template +class LoadCommandAtom : public WriterAtom +{ +protected: + LoadCommandAtom(Writer& writer, Segment& segment) : WriterAtom(writer, segment) {} + static uint64_t alignedSize(uint64_t size); +}; + + +template +class SegmentLoadCommandsAtom : public LoadCommandAtom +{ +public: + SegmentLoadCommandsAtom(Writer& writer) + : LoadCommandAtom(writer, Segment::fgTextSegment), fCommandCount(0), fSize(0) + { writer.fSegmentCommands = this; } + virtual const char* getDisplayName() const { return "segment load commands"; } + virtual uint64_t getSize() const { return fSize; } + virtual uint8_t getAlignment() const { return 2; } + virtual const char* getSectionName() const { return "._load_commands"; } + virtual void copyRawContent(uint8_t buffer[]) const; + + void computeSize(); + void setup(); + unsigned int commandCount() { return fCommandCount; } + void assignFileOffsets(); +private: + using WriterAtom::fWriter; + typedef typename A::P P; + unsigned int fCommandCount; + uint32_t fSize; +}; + +template +class SymbolTableLoadCommandsAtom : public LoadCommandAtom +{ +public: + SymbolTableLoadCommandsAtom(Writer&); + virtual const char* getDisplayName() const { return "symbol table load commands"; } + virtual uint64_t getSize() const; + virtual uint8_t getAlignment() const { return 2; } + virtual const char* getSectionName() const { return "._load_commands"; } + virtual void copyRawContent(uint8_t buffer[]) const; + unsigned int commandCount(); + +private: + using WriterAtom::fWriter; + typedef typename A::P P; + macho_symtab_command fSymbolTable; + macho_dysymtab_command fDynamicSymbolTable; +}; + +template +class ThreadsLoadCommandsAtom : public LoadCommandAtom +{ +public: + ThreadsLoadCommandsAtom(Writer& writer) + : LoadCommandAtom(writer, Segment::fgTextSegment) {} + virtual const char* getDisplayName() const { return "thread load commands"; } + virtual uint64_t getSize() const; + virtual uint8_t getAlignment() const { return 2; } + virtual const char* getSectionName() const { return "._load_commands"; } + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; + uint8_t* fBuffer; + uint32_t fBufferSize; +}; + +template +class DyldLoadCommandsAtom : public LoadCommandAtom +{ +public: + DyldLoadCommandsAtom(Writer& writer) : LoadCommandAtom(writer, Segment::fgTextSegment) {} + virtual const char* getDisplayName() const { return "dyld load command"; } + virtual uint64_t getSize() const; + virtual uint8_t getAlignment() const { return 2; } + virtual const char* getSectionName() const { return "._load_commands"; } + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; +}; + +template +class AllowableClientLoadCommandsAtom : public LoadCommandAtom +{ +public: + AllowableClientLoadCommandsAtom(Writer& writer, const char* client) : + LoadCommandAtom(writer, Segment::fgTextSegment), clientString(client) {} + virtual const char* getDisplayName() const { return "allowable_client load command"; } + virtual uint64_t getSize() const; + virtual uint8_t getAlignment() const { return 2; } + virtual const char* getSectionName() const { return "._load_commands"; } + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; + const char* clientString; +}; + +template +class DylibLoadCommandsAtom : public LoadCommandAtom +{ +public: + DylibLoadCommandsAtom(Writer& writer, ExecutableFile::DyLibUsed& info) + : LoadCommandAtom(writer, Segment::fgTextSegment), fInfo(info) {} + virtual const char* getDisplayName() const { return "dylib load command"; } + virtual uint64_t getSize() const; + virtual uint8_t getAlignment() const { return 2; } + virtual const char* getSectionName() const { return "._load_commands"; } + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; + ExecutableFile::DyLibUsed& fInfo; +}; + +template +class DylibIDLoadCommandsAtom : public LoadCommandAtom +{ +public: + DylibIDLoadCommandsAtom(Writer& writer) : LoadCommandAtom(writer, Segment::fgTextSegment) {} + virtual const char* getDisplayName() const { return "dylib ID load command"; } + virtual uint64_t getSize() const; + virtual uint8_t getAlignment() const { return 2; } + virtual const char* getSectionName() const { return "._load_commands"; } + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; +}; + +template +class RoutinesLoadCommandsAtom : public LoadCommandAtom +{ +public: + RoutinesLoadCommandsAtom(Writer& writer) : LoadCommandAtom(writer, Segment::fgTextSegment) {} + virtual const char* getDisplayName() const { return "routines load command"; } + virtual uint64_t getSize() const { return sizeof(macho_routines_command); } + virtual uint8_t getAlignment() const { return 2; } + virtual const char* getSectionName() const { return "._load_commands"; } + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; +}; + +template +class SubUmbrellaLoadCommandsAtom : public LoadCommandAtom +{ +public: + SubUmbrellaLoadCommandsAtom(Writer& writer, const char* name) + : LoadCommandAtom(writer, Segment::fgTextSegment), fName(name) {} + virtual const char* getDisplayName() const { return "sub-umbrella load command"; } + virtual uint64_t getSize() const; + virtual uint8_t getAlignment() const { return 2; } + virtual const char* getSectionName() const { return "._load_commands"; } + virtual void copyRawContent(uint8_t buffer[]) const; +private: + typedef typename A::P P; + const char* fName; +}; + +template +class SubLibraryLoadCommandsAtom : public LoadCommandAtom +{ +public: + SubLibraryLoadCommandsAtom(Writer& writer, const char* nameStart, int nameLen) + : LoadCommandAtom(writer, Segment::fgTextSegment), fNameStart(nameStart), fNameLength(nameLen) {} + virtual const char* getDisplayName() const { return "sub-library load command"; } + virtual uint64_t getSize() const; + virtual uint8_t getAlignment() const { return 2; } + virtual const char* getSectionName() const { return "._load_commands"; } + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; + const char* fNameStart; + int fNameLength; +}; + +template +class UmbrellaLoadCommandsAtom : public LoadCommandAtom +{ +public: + UmbrellaLoadCommandsAtom(Writer& writer, const char* name) + : LoadCommandAtom(writer, Segment::fgTextSegment), fName(name) {} + virtual const char* getDisplayName() const { return "umbrella load command"; } + virtual uint64_t getSize() const; + virtual uint8_t getAlignment() const { return 2; } + virtual const char* getSectionName() const { return "._load_commands"; } + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; + const char* fName; +}; + +template +class UUIDLoadCommandAtom : public LoadCommandAtom +{ +public: + UUIDLoadCommandAtom(Writer& writer) + : LoadCommandAtom(writer, Segment::fgTextSegment), fEmit(false) { ::uuid_generate_random(fUUID);} + virtual const char* getDisplayName() const { return "uuid load command"; } + virtual uint64_t getSize() const { return fEmit ? sizeof(macho_uuid_command) : 0; } + virtual uint8_t getAlignment() const { return 2; } + virtual const char* getSectionName() const { return "._load_commands"; } + virtual void copyRawContent(uint8_t buffer[]) const; + virtual void emit() { fEmit = true; } +private: + using WriterAtom::fWriter; + typedef typename A::P P; + uuid_t fUUID; + bool fEmit; +}; + +template +class LoadCommandsPaddingAtom : public WriterAtom +{ +public: + LoadCommandsPaddingAtom(Writer& writer) + : WriterAtom(writer, Segment::fgTextSegment), fSize(0) {} + virtual const char* getDisplayName() const { return "header padding"; } + virtual uint64_t getSize() const { return fSize; } + virtual uint8_t getAlignment() const { return 2; } + virtual const char* getSectionName() const { return "._load_cmds_pad"; } + virtual void copyRawContent(uint8_t buffer[]) const; + + void setSize(uint64_t newSize); +private: + using WriterAtom::fWriter; + typedef typename A::P P; + uint64_t fSize; +}; + +template +class LinkEditAtom : public WriterAtom +{ +public: + LinkEditAtom(Writer& writer) : WriterAtom(writer, Segment::fgLinkEditSegment) {} + uint64_t getFileOffset() const; +private: + typedef typename A::P P; +}; + +template +class SectionRelocationsLinkEditAtom : public LinkEditAtom +{ +public: + SectionRelocationsLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } + virtual const char* getDisplayName() const { return "section relocations"; } + virtual uint64_t getSize() const; + virtual uint8_t getAlignment() const { return 3; } + virtual const char* getSectionName() const { return "._section_relocs"; } + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; +}; + +template +class LocalRelocationsLinkEditAtom : public LinkEditAtom +{ +public: + LocalRelocationsLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } + virtual const char* getDisplayName() const { return "local relocations"; } + virtual uint64_t getSize() const; + virtual uint8_t getAlignment() const { return 3; } + virtual const char* getSectionName() const { return "._local_relocs"; } + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; +}; + +template +class SymbolTableLinkEditAtom : public LinkEditAtom +{ +public: + SymbolTableLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } + virtual const char* getDisplayName() const { return "symbol table"; } + virtual uint64_t getSize() const; + virtual uint8_t getAlignment() const { return 2; } + virtual const char* getSectionName() const { return "._symbol_table"; } + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; +}; + +template +class ExternalRelocationsLinkEditAtom : public LinkEditAtom +{ +public: + ExternalRelocationsLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } + virtual const char* getDisplayName() const { return "external relocations"; } + virtual uint64_t getSize() const; + virtual uint8_t getAlignment() const { return 3; } + virtual const char* getSectionName() const { return "._extern_relocs"; } + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; +}; + +struct IndirectEntry { + uint32_t indirectIndex; + uint32_t symbolIndex; +}; + +template +class IndirectTableLinkEditAtom : public LinkEditAtom +{ +public: + IndirectTableLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } + virtual const char* getDisplayName() const { return "indirect symbol table"; } + virtual uint64_t getSize() const; + virtual uint8_t getAlignment() const { return 2; } + virtual const char* getSectionName() const { return "._indirect_syms"; } + virtual void copyRawContent(uint8_t buffer[]) const; + + std::vector fTable; + +private: + using WriterAtom::fWriter; + typedef typename A::P P; +}; + +class CStringEquals +{ +public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } +}; + +template +class StringsLinkEditAtom : public LinkEditAtom +{ +public: + StringsLinkEditAtom(Writer& writer); + virtual const char* getDisplayName() const { return "string pool"; } + virtual uint64_t getSize() const; + virtual uint8_t getAlignment() const { return 2; } + virtual const char* getSectionName() const { return "._string_pool"; } + virtual void copyRawContent(uint8_t buffer[]) const; + + int32_t add(const char* name); + int32_t addUnique(const char* name); + int32_t emptyString() { return 1; } + +private: + using WriterAtom::fWriter; + typedef typename A::P P; + enum { kBufferSize = 0x01000000 }; + class CStringComparor { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) < 0); } + }; + typedef __gnu_cxx::hash_map, CStringEquals> StringToOffset; + + std::vector fFullBuffers; + char* fCurrentBuffer; + uint32_t fCurrentBufferUsed; + StringToOffset fUniqueStrings; +}; + + + +template +class UndefinedSymbolProxyAtom : public WriterAtom +{ +public: + UndefinedSymbolProxyAtom(Writer& writer, const char* name) : WriterAtom(writer, Segment::fgLinkEditSegment), fName(name) {} + virtual const char* getName() const { return fName; } + virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeGlobal; } + virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ObjectFile::Atom::kExternalDefinition; } + virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableIn; } + virtual uint64_t getSize() const { return 0; } + virtual const char* getSectionName() const { return "._imports"; } +private: + using WriterAtom::fWriter; + typedef typename A::P P; + const char* fName; +}; + +template +class BranchIslandAtom : public WriterAtom +{ +public: + BranchIslandAtom(Writer& writer, const char* name, int islandRegion, ObjectFile::Atom& target, uint32_t targetOffset); + virtual const char* getName() const { return fName; } + virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual uint64_t getSize() const; + virtual const char* getSectionName() const { return "__text"; } + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + const char* fName; + ObjectFile::Atom& fTarget; + uint32_t fTargetOffset; +}; + +template +class StubAtom : public WriterAtom +{ +public: + StubAtom(Writer& writer, ObjectFile::Atom& target); + virtual const char* getName() const { return fName; } + virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual uint8_t getAlignment() const { return 2; } + virtual uint64_t getSize() const; + virtual const char* getSectionName() const { return "__symbol_stub1"; } + virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } + virtual void copyRawContent(uint8_t buffer[]) const; + ObjectFile::Atom* getTarget() { return &fTarget; } +private: + static const char* stubName(const char* importName); + bool pic() const; + using WriterAtom::fWriter; + const char* fName; + ObjectFile::Atom& fTarget; + std::vector fReferences; +}; + + +template +class LazyPointerAtom : public WriterAtom +{ +public: + LazyPointerAtom(Writer& writer, ObjectFile::Atom& target); + virtual const char* getName() const { return fName; } + virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual uint64_t getSize() const { return sizeof(typename A::P::uint_t); } + virtual const char* getSectionName() const { return "__la_symbol_ptr"; } + virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } + virtual void copyRawContent(uint8_t buffer[]) const; + ObjectFile::Atom* getTarget() { return &fTarget; } +private: + using WriterAtom::fWriter; + static const char* lazyPointerName(const char* importName); + const char* fName; + ObjectFile::Atom& fTarget; + std::vector fReferences; +}; + + +template +class NonLazyPointerAtom : public WriterAtom +{ +public: + NonLazyPointerAtom(Writer& writer, ObjectFile::Atom& target); + virtual const char* getName() const { return fName; } + virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual uint64_t getSize() const { return sizeof(typename A::P::uint_t); } + virtual const char* getSectionName() const { return "__nl_symbol_ptr"; } + virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } + virtual void copyRawContent(uint8_t buffer[]) const; + ObjectFile::Atom* getTarget() { return &fTarget; } +private: + using WriterAtom::fWriter; + static const char* nonlazyPointerName(const char* importName); + const char* fName; + ObjectFile::Atom& fTarget; + std::vector fReferences; +}; + + +template +class WriterReference : public ObjectFile::Reference +{ +public: + typedef typename A::ReferenceKinds Kinds; + + WriterReference(uint32_t offset, Kinds kind, ObjectFile::Atom* target, + uint32_t toOffset=0, ObjectFile::Atom* fromTarget=NULL, uint32_t fromOffset=0) + : fKind(kind), fFixUpOffsetInSrc(offset), fTarget(target), + fTargetOffset(toOffset), fFromTarget(fromTarget), fFromTargetOffset(fromOffset) {} + + virtual ~WriterReference() {} + + virtual bool isTargetUnbound() const { return false; } + virtual bool isFromTargetUnbound() const { return false; } + virtual uint8_t getKind() const { return (uint8_t)fKind; } + virtual uint64_t getFixUpOffset() const { return fFixUpOffsetInSrc; } + virtual const char* getTargetName() const { return fTarget->getName(); } + virtual ObjectFile::Atom& getTarget() const { return *fTarget; } + virtual uint64_t getTargetOffset() const { return fTargetOffset; } + virtual bool hasFromTarget() const { return (fFromTarget != NULL); } + virtual ObjectFile::Atom& getFromTarget() const { return *fFromTarget; } + virtual const char* getFromTargetName() const { return fFromTarget->getName(); } + virtual void setTarget(ObjectFile::Atom& target, uint64_t offset) { fTarget = ⌖ fTargetOffset = offset; } + virtual void setFromTarget(ObjectFile::Atom& target) { fFromTarget = ⌖ } + virtual void setFromTargetName(const char* name) { } + virtual void setFromTargetOffset(uint64_t offset) { fFromTargetOffset = offset; } + virtual const char* getDescription() const { return "writer refrence"; } + virtual uint64_t getFromTargetOffset() const { return fFromTargetOffset; } + +private: + Kinds fKind; + uint32_t fFixUpOffsetInSrc; + ObjectFile::Atom* fTarget; + uint32_t fTargetOffset; + ObjectFile::Atom* fFromTarget; + uint32_t fFromTargetOffset; +}; + + + +struct ExportSorter +{ + bool operator()(ObjectFile::Atom* left, ObjectFile::Atom* right) + { + return (strcmp(left->getName(), right->getName()) < 0); + } +}; + + + + +template +Writer::Writer(const char* path, Options& options, std::vector& dynamicLibraries) + : ExecutableFile::Writer(dynamicLibraries), fFilePath(strdup(path)), fOptions(options), fLoadCommandsSection(NULL), + fLoadCommandsSegment(NULL), fLargestAtomSize(1), + fEmitVirtualSections(false), fHasWeakExports(false), fReferencesWeakImports(false), + fSeenFollowOnReferences(false) +{ + int permissions = 0777; + if ( fOptions.outputKind() == Options::kObjectFile ) + permissions = 0666; + // Calling unlink first assures the file is gone so that open creates it with correct permissions + // It also handles the case where fFilePath file is not writeable but its directory is + // And it means we don't have to truncate the file when done writing (in case new is smaller than old) + (void)unlink(fFilePath); + fFileDescriptor = open(fFilePath, O_CREAT | O_WRONLY | O_TRUNC, permissions); + if ( fFileDescriptor == -1 ) { + throw "can't open file for writing"; + } + + switch ( fOptions.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + fWriterSynthesizedAtoms.push_back(new PageZeroAtom(*this)); + if ( fOptions.outputKind() == Options::kDynamicExecutable ) + fWriterSynthesizedAtoms.push_back(new DsoHandleAtom(*this)); + fWriterSynthesizedAtoms.push_back(new MachHeaderAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom(*this)); + if ( fOptions.outputKind() == Options::kDynamicExecutable ) + fWriterSynthesizedAtoms.push_back(new DyldLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom(*this)); + fWriterSynthesizedAtoms.push_back(new ThreadsLoadCommandsAtom(*this)); + if ( fOptions.hasCustomStack() ) + fWriterSynthesizedAtoms.push_back(new CustomStackAtom(*this)); + fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); + fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom(*this)); + break; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + fWriterSynthesizedAtoms.push_back(new DsoHandleAtom(*this)); + // fall through + case Options::kObjectFile: + fWriterSynthesizedAtoms.push_back(new MachHeaderAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom(*this)); + if ( fOptions.outputKind() == Options::kDynamicLibrary ) { + fWriterSynthesizedAtoms.push_back(new DylibIDLoadCommandsAtom(*this)); + if ( fOptions.initFunctionName() != NULL ) + fWriterSynthesizedAtoms.push_back(new RoutinesLoadCommandsAtom(*this)); + } + fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); + fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom(*this)); + break; + case Options::kDyld: + fWriterSynthesizedAtoms.push_back(new DsoHandleAtom(*this)); + fWriterSynthesizedAtoms.push_back(new MachHeaderAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(new DyldLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom(*this)); + fWriterSynthesizedAtoms.push_back(new ThreadsLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); + fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom(*this)); + break; + } + + // add extra commmands + uint8_t ordinal = 1; + switch ( fOptions.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + { + // add dylib load command atoms for all dynamic libraries + const unsigned int libCount = dynamicLibraries.size(); + for (unsigned int i=0; i < libCount; ++i) { + ExecutableFile::DyLibUsed& dylibInfo = dynamicLibraries[i]; + if ( dylibInfo.indirect ) { + // find ordinal of direct reader + if ( fOptions.nameSpace() == Options::kTwoLevelNameSpace ) { + bool found = false; + for (std::map::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) { + if ( it->first == dylibInfo.directReader ) { + //fprintf(stderr, "ordinal %d for indirect %s\n", it->second, dylibInfo.reader->getPath()); + fLibraryToOrdinal[dylibInfo.reader] = it->second; + found = true; + break; + } + } + if ( ! found ) + fprintf(stderr, "ld64 warning: ordinal not found for %s, parent %s\n", dylibInfo.reader->getPath(), dylibInfo.directReader != NULL ? dylibInfo.directReader->getPath() : NULL); + } + } + else { + // see if a DylibLoadCommandsAtom has already been created for this install path + bool newDylib = true; + const char* dylibInstallPath = dylibInfo.reader->getInstallPath(); + if ( dylibInfo.options.fInstallPathOverride != NULL ) + dylibInstallPath = dylibInfo.options.fInstallPathOverride; + for (unsigned int seenLib=0; seenLib < i; ++seenLib) { + ExecutableFile::DyLibUsed& seenDylibInfo = dynamicLibraries[seenLib]; + if ( !seenDylibInfo.indirect ) { + const char* seenDylibInstallPath = seenDylibInfo.reader->getInstallPath(); + if ( seenDylibInfo.options.fInstallPathOverride != NULL ) + seenDylibInstallPath = dylibInfo.options.fInstallPathOverride; + if ( strcmp(seenDylibInstallPath, dylibInstallPath) == 0 ) { + fLibraryToOrdinal[dylibInfo.reader] = fLibraryToOrdinal[seenDylibInfo.reader]; + newDylib = false; + break; + } + } + } + + if ( newDylib ) { + // assign new ordinal and check for other paired load commands + fLibraryToOrdinal[dylibInfo.reader] = ordinal++; + fWriterSynthesizedAtoms.push_back(new DylibLoadCommandsAtom(*this, dylibInfo)); + if ( dylibInfo.options.fReExport ) { + // this dylib also needs a sub_x load command + bool isFrameworkReExport = false; + const char* lastSlash = strrchr(dylibInstallPath, '/'); + if ( lastSlash != NULL ) { + char frameworkName[strlen(lastSlash)+20]; + sprintf(frameworkName, "/%s.framework/", &lastSlash[1]); + isFrameworkReExport = (strstr(dylibInstallPath, frameworkName) != NULL); + } + if ( isFrameworkReExport ) { + // needs a LC_SUB_UMBRELLA command + fWriterSynthesizedAtoms.push_back(new SubUmbrellaLoadCommandsAtom(*this, &lastSlash[1])); + } + else { + // needs a LC_SUB_LIBRARY command + const char* nameStart = &lastSlash[1]; + if ( lastSlash == NULL ) + nameStart = dylibInstallPath; + int len = strlen(nameStart); + const char* dot = strchr(nameStart, '.'); + if ( dot != NULL ) + len = dot - nameStart; + fWriterSynthesizedAtoms.push_back(new SubLibraryLoadCommandsAtom(*this, nameStart, len)); + } + } + } + } + } + // add umbrella command if needed + if ( fOptions.umbrellaName() != NULL ) { + fWriterSynthesizedAtoms.push_back(new UmbrellaLoadCommandsAtom(*this, fOptions.umbrellaName())); + } + std::vector& allowableClients = fOptions.allowableClients(); + if ( allowableClients.size() != 0 ) { + for (std::vector::iterator it=allowableClients.begin(); + it != allowableClients.end(); + it++) + fWriterSynthesizedAtoms.push_back(new AllowableClientLoadCommandsAtom(*this, *it)); + } + } + break; + case Options::kStaticExecutable: + case Options::kObjectFile: + case Options::kDyld: + break; + } + + //fprintf(stderr, "ordinals table:\n"); + //for (std::map::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) { + // fprintf(stderr, "%d <== %s\n", it->second, it->first->getPath()); + //} +} + +template +Writer::~Writer() +{ + if ( fFilePath != NULL ) + free((void*)fFilePath); + if ( fSymbolTable != NULL ) + delete [] fSymbolTable; +} + + +template +ObjectFile::Atom* Writer::getUndefinedProxyAtom(const char* name) +{ + if ( (fOptions.outputKind() == Options::kObjectFile) + || (fOptions.undefinedTreatment() != Options::kUndefinedError) ) + return new UndefinedSymbolProxyAtom(*this, name); + else + return NULL; +} + +template +uint8_t Writer::ordinalForLibrary(ObjectFile::Reader* lib) +{ + // flat namespace images use zero for all ordinals + if ( fOptions.nameSpace() != Options::kTwoLevelNameSpace ) + return 0; + + // is an UndefinedSymbolProxyAtom + if ( lib == this ) + if ( fOptions.nameSpace() == Options::kTwoLevelNameSpace ) + return DYNAMIC_LOOKUP_ORDINAL; + + std::map::iterator pos = fLibraryToOrdinal.find(lib); + if ( pos != fLibraryToOrdinal.end() ) + return pos->second; + + throw "can't find ordinal for imported symbol"; +} + + +template +uint64_t Writer::write(std::vector& atoms, + std::vector& stabs, + class ObjectFile::Atom* entryPointAtom, class ObjectFile::Atom* dyldHelperAtom, + bool createUUID) +{ + fAllAtoms = &atoms; + fStabs = &stabs; + fEntryPoint = entryPointAtom; + fDyldHelper = dyldHelperAtom; + + // Set for create UUID + if (createUUID) + fUUIDAtom->emit(); + + // create inter-library stubs + synthesizeStubs(); + + // create SegmentInfo and SectionInfo objects and assign all atoms to a section + partitionIntoSections(); + + // segment load command can now be sized and padding can be set + adjustLoadCommandsAndPadding(); + + // assign each section a file offset + assignFileOffsets(); + + // if need to add branch islands, reassign file offsets + if ( addBranchIslands() ) + assignFileOffsets(); + + // build symbol table and relocations + buildLinkEdit(); + + // write everything + return writeAtoms(); +} + +template +void Writer::buildLinkEdit() +{ + this->collectExportedAndImportedAndLocalAtoms(); + this->buildSymbolTable(); + this->buildFixups(); + this->adjustLinkEditSections(); +} + + + +template +uint64_t Writer::getAtomLoadAddress(const ObjectFile::Atom* atom) +{ + return atom->getAddress(); +// SectionInfo* info = (SectionInfo*)atom->getSection(); +// return info->getBaseAddress() + atom->getSectionOffset(); +} + +template +void Writer::setExportNlist(const ObjectFile::Atom* atom, macho_nlist

* entry) +{ + // set n_type + entry->set_n_type(N_EXT | N_SECT); + if ( (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit) && (fOptions.outputKind() == Options::kObjectFile) ) { + if ( fOptions.keepPrivateExterns() ) + entry->set_n_type(N_EXT | N_SECT | N_PEXT); + } + + // set n_sect (section number of implementation ) + uint8_t sectionIndex = atom->getSection()->getIndex(); + entry->set_n_sect(sectionIndex); + + // the __mh_execute_header is magic and must be an absolute symbol + if ( (fOptions.outputKind() == Options::kDynamicExecutable) && (sectionIndex==0) + && (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip )) + entry->set_n_type(N_EXT | N_ABS); + + // set n_desc + uint16_t desc = 0; + if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip ) + desc |= REFERENCED_DYNAMICALLY; + if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) { + desc |= N_WEAK_DEF; + fHasWeakExports = true; + } + entry->set_n_desc(desc); + + // set n_value ( address this symbol will be at if this executable is loaded at it preferred address ) + entry->set_n_value(this->getAtomLoadAddress(atom)); +} + +template +void Writer::setImportNlist(const ObjectFile::Atom* atom, macho_nlist

* entry) +{ + // set n_type + entry->set_n_type(N_UNDF | N_EXT); + if ( fOptions.keepPrivateExterns() + && (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit) + && (fOptions.outputKind() == Options::kObjectFile) ) + entry->set_n_type(N_UNDF | N_EXT | N_PEXT); + + // set n_sect + entry->set_n_sect(0); + + uint16_t desc = 0; + if ( fOptions.outputKind() != Options::kObjectFile ) { + // set n_desc ( high byte is library ordinal, low byte is reference type ) + desc = REFERENCE_FLAG_UNDEFINED_LAZY; // FIXME + try { + uint8_t ordinal = this->ordinalForLibrary(atom->getFile()); + SET_LIBRARY_ORDINAL(desc, ordinal); + } + catch (const char* msg) { + throwf("%s %s from %s", msg, atom->getDisplayName(), atom->getFile()->getPath()); + } + } + if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip ) + desc |= REFERENCED_DYNAMICALLY; + if ( ( fOptions.outputKind() != Options::kObjectFile) && (atom->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { + desc |= N_REF_TO_WEAK; + fReferencesWeakImports = true; + } + // set weak_import attribute + if ( fWeakImportMap[atom] ) + desc |= N_WEAK_REF; + entry->set_n_desc(desc); + + // set n_value, zero for import proxy and size for tentative definition + entry->set_n_value(atom->getSize()); +} + +template +void Writer::setLocalNlist(const ObjectFile::Atom* atom, macho_nlist

* entry) +{ + // set n_type + uint8_t type = N_SECT; + if ( atom->getScope() == ObjectFile::Atom::scopeLinkageUnit ) + type |= N_PEXT; + entry->set_n_type(type); + + // set n_sect (section number of implementation ) + uint8_t sectIndex = atom->getSection()->getIndex(); + if ( sectIndex == 0 ) { + // see synthesized lable for mach_header needs special section number... + if ( strcmp(atom->getSectionName(), "._mach_header") == 0 ) + sectIndex = 1; + } + entry->set_n_sect(sectIndex); + + // set n_desc + uint16_t desc = 0; + if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) + desc |= N_WEAK_DEF; + entry->set_n_desc(desc); + + // set n_value ( address this symbol will be at if this executable is loaded at it preferred address ) + entry->set_n_value(this->getAtomLoadAddress(atom)); +} + + +template +void Writer::setNlistRange(std::vector& atoms, uint32_t startIndex, uint32_t count) +{ + macho_nlist

* entry = &fSymbolTable[startIndex]; + for (uint32_t i=0; i < count; ++i, ++entry) { + ObjectFile::Atom* atom = atoms[i]; + entry->set_n_strx(this->fStringsAtom->add(atom->getName())); + if ( &atoms == &fExportedAtoms ) { + this->setExportNlist(atom, entry); + } + else if ( &atoms == &fImportedAtoms ) { + this->setImportNlist(atom, entry); + } + else { + this->setLocalNlist(atom, entry); + } + } +} + +template +void Writer::buildSymbolTable() +{ + fSymbolTableStabsStartIndex = 0; + fSymbolTableStabsCount = fStabs->size(); + fSymbolTableLocalStartIndex = fSymbolTableStabsStartIndex + fSymbolTableStabsCount; + fSymbolTableLocalCount = fLocalSymbolAtoms.size(); + fSymbolTableExportStartIndex = fSymbolTableLocalStartIndex + fSymbolTableLocalCount; + fSymbolTableExportCount = fExportedAtoms.size(); + fSymbolTableImportStartIndex = fSymbolTableExportStartIndex + fSymbolTableExportCount; + fSymbolTableImportCount = fImportedAtoms.size(); + + // allocate symbol table + fSymbolTableCount = fSymbolTableStabsCount + fSymbolTableLocalCount + fSymbolTableExportCount + fSymbolTableImportCount; + fSymbolTable = new macho_nlist

[fSymbolTableCount]; + + // fill in symbol table and string pool (do stabs last so strings are at end of pool) + setNlistRange(fLocalSymbolAtoms, fSymbolTableLocalStartIndex, fSymbolTableLocalCount); + setNlistRange(fExportedAtoms, fSymbolTableExportStartIndex, fSymbolTableExportCount); + setNlistRange(fImportedAtoms, fSymbolTableImportStartIndex, fSymbolTableImportCount); + addStabs(fSymbolTableStabsStartIndex); +} + + + +template +bool Writer::shouldExport(const ObjectFile::Atom& atom) const +{ + if ( atom.getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn ) + return false; + switch ( atom.getScope() ) { + case ObjectFile::Atom::scopeGlobal: + return true; + case ObjectFile::Atom::scopeLinkageUnit: + return ( (fOptions.outputKind() == Options::kObjectFile) && fOptions.keepPrivateExterns() ); + default: + return false; + } +} + +template +void Writer::collectExportedAndImportedAndLocalAtoms() +{ + const int atomCount = fAllAtoms->size(); + // guess at sizes of each bucket to minimize re-allocations + fImportedAtoms.reserve(100); + fExportedAtoms.reserve(atomCount/2); + fLocalSymbolAtoms.reserve(atomCount); + for (int i=0; i < atomCount; ++i) { + ObjectFile::Atom* atom = (*fAllAtoms)[i]; + // only named atoms go in symbol table + if ( atom->getName() != NULL ) { + // put atom into correct bucket: imports, exports, locals + //printf("collectExportedAndImportedAndLocalAtoms() name=%s\n", atom->getDisplayName()); + switch ( atom->getDefinitionKind() ) { + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + fImportedAtoms.push_back(atom); + break; + case ObjectFile::Atom::kTentativeDefinition: + if ( fOptions.outputKind() == Options::kObjectFile ) { + fImportedAtoms.push_back(atom); + break; + } + // else fall into + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + if ( this->shouldExport(*atom) ) + fExportedAtoms.push_back(atom); + else if ( !fOptions.stripLocalSymbols() + && (atom->getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn) ) + fLocalSymbolAtoms.push_back(atom); + break; + } + } + } + + // sort exported atoms by name + std::sort(fExportedAtoms.begin(), fExportedAtoms.end(), ExportSorter()); + // sort imported atoms by name (not required by runtime, but helps make generated files binary diffable) + std::sort(fImportedAtoms.begin(), fImportedAtoms.end(), ExportSorter()); +} + + +template +uint64_t Writer::valueForStab(const ObjectFile::Reader::Stab& stab) +{ + switch ( stab.type ) { + case N_FUN: + if ( stab.other == 0 ) { + // end of function N_FUN has size + return stab.atom->getSize(); + } + else { + // start of function N_FUN has address + return getAtomLoadAddress(stab.atom); + } + case N_LBRAC: + case N_RBRAC: + case N_SLINE: + if ( stab.atom == NULL ) + // some weird assembly files have slines not associated with a function + return stab.value; + else + // all these stab types need their value changed from an offset in the atom to an address + return getAtomLoadAddress(stab.atom) + stab.value; + case N_STSYM: + case N_LCSYM: + case N_BNSYM: + // all these need address of atom + return getAtomLoadAddress(stab.atom);; + case N_ENSYM: + return stab.atom->getSize(); + case N_SO: + return 0; + default: + return stab.value; + } +} + +template +uint32_t Writer::stringOffsetForStab(const ObjectFile::Reader::Stab& stab) +{ + switch (stab.type) { + case N_SO: + if ( (stab.string == NULL) || stab.string[0] == '\0' ) { + return this->fStringsAtom->emptyString(); + break; + } + // fall into uniquing case + case N_SOL: + case N_BINCL: + case N_EXCL: + return this->fStringsAtom->addUnique(stab.string); + break; + default: + if ( stab.string == NULL ) + return 0; + else if ( stab.string[0] == '\0' ) + return this->fStringsAtom->emptyString(); + else + return this->fStringsAtom->add(stab.string); + } + return 0; +} + +template +uint8_t Writer::sectionIndexForStab(const ObjectFile::Reader::Stab& stab) +{ + if ( stab.atom != NULL ) + return stab.atom->getSection()->getIndex(); + else + return stab.other; +} + +template +void Writer::addStabs(uint32_t startIndex) +{ + macho_nlist

* entry = &fSymbolTable[startIndex]; + for(std::vector::iterator it = fStabs->begin(); it != fStabs->end(); ++it, ++entry) { + const ObjectFile::Reader::Stab& stab = *it; + entry->set_n_type(stab.type); + entry->set_n_sect(sectionIndexForStab(stab)); + entry->set_n_desc(stab.desc); + entry->set_n_value(valueForStab(stab)); + entry->set_n_strx(stringOffsetForStab(stab)); + } +} + + + +template +uint32_t Writer::symbolIndex(ObjectFile::Atom& atom) +{ + // search imports + int i = 0; + for(std::vector::iterator it=fImportedAtoms.begin(); it != fImportedAtoms.end(); ++it) { + if ( &atom == *it ) + return i + fSymbolTableImportStartIndex; + ++i; + } + + // search locals + i = 0; + for(std::vector::iterator it=fLocalSymbolAtoms.begin(); it != fLocalSymbolAtoms.end(); ++it) { + if ( &atom == *it ) + return i + fSymbolTableLocalStartIndex; + ++i; + } + + // search exports + i = 0; + for(std::vector::iterator it=fExportedAtoms.begin(); it != fExportedAtoms.end(); ++it) { + if ( &atom == *it ) + return i + fSymbolTableExportStartIndex; + ++i; + } + + throwf("atom not found in symbolIndex(%s) for %s", atom.getDisplayName(), atom.getFile()->getPath()); +} + + +template +void Writer::buildFixups() +{ + if ( fOptions.outputKind() == Options::kObjectFile ) { + this->buildObjectFileFixups(); + } + else { + if ( fOptions.keepRelocations() ) + this->buildObjectFileFixups(); + this->buildExecutableFixups(); + } +} + + +template <> +uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) +{ + ObjectFile::Atom& target = ref->getTarget(); + bool isExtern = false; + switch ( target.getDefinitionKind() ) { + case ObjectFile::Atom::kRegularDefinition: + isExtern = false; + break; + case ObjectFile::Atom::kWeakDefinition: + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + isExtern = shouldExport(target); + break; + } + uint32_t symbolIndex = 0; + if ( isExtern ) + symbolIndex = this->symbolIndex(target); + uint32_t sectionNum = target.getSection()->getIndex(); + uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset(); + macho_relocation_info

reloc1; + macho_relocation_info

reloc2; + macho_scattered_relocation_info

* sreloc1 = (macho_scattered_relocation_info

*)&reloc1; + macho_scattered_relocation_info

* sreloc2 = (macho_scattered_relocation_info

*)&reloc2; + x86::ReferenceKinds kind = (x86::ReferenceKinds)ref->getKind(); + + switch ( kind ) { + case x86::kNoFixUp: + case x86::kFollowOn: + return 0; + + case x86::kPointer: + case x86::kPointerWeakImport: + if ( !isExtern && (ref->getTargetOffset() != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(GENERIC_RELOC_VANILLA); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(isExtern); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + } + fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + return 1; + + case x86::kPointerDiff: + { + pint_t fromAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset(); + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + if ( ref->getTarget().getScope() == ObjectFile::Atom::scopeTranslationUnit ) + sreloc1->set_r_type(GENERIC_RELOC_LOCAL_SECTDIFF); + else + sreloc1->set_r_type(GENERIC_RELOC_SECTDIFF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(2); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address(0); + sreloc2->set_r_value(fromAddr); + fSectionRelocs.insert(fSectionRelocs.begin(), reloc2); + fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + return 2; + } + + case x86::kPCRel32WeakImport: + case x86::kPCRel32: + if ( !isExtern && (ref->getTargetOffset() != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(2); + sreloc1->set_r_type(GENERIC_RELOC_VANILLA); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(isExtern); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + } + fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + return 1; + + } + return 0; +} + + + +template <> +uint8_t Writer::getRelocPointerSize() +{ + return 2; +} + +template <> +uint8_t Writer::getRelocPointerSize() +{ + return 3; +} + +template <> +uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) +{ + return addObjectRelocs_powerpc(atom, ref); +} + +template <> +uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) +{ + return addObjectRelocs_powerpc(atom, ref); +} + +// +// addObjectRelocs and addObjectRelocs are almost exactly the same, so +// they use a common addObjectRelocs_powerpc() method. +// +template +uint32_t Writer::addObjectRelocs_powerpc(ObjectFile::Atom* atom, ObjectFile::Reference* ref) +{ + ObjectFile::Atom& target = ref->getTarget(); + bool isExtern = false; + switch ( target.getDefinitionKind() ) { + case ObjectFile::Atom::kRegularDefinition: + isExtern = false; + break; + case ObjectFile::Atom::kWeakDefinition: + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + isExtern = shouldExport(target); + break; + } + + uint32_t symbolIndex = 0; + if ( isExtern ) + symbolIndex = this->symbolIndex(target); + uint32_t sectionNum = target.getSection()->getIndex(); + uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset(); + macho_relocation_info

reloc1; + macho_relocation_info

reloc2; + macho_scattered_relocation_info

* sreloc1 = (macho_scattered_relocation_info

*)&reloc1; + macho_scattered_relocation_info

* sreloc2 = (macho_scattered_relocation_info

*)&reloc2; + typename A::ReferenceKinds kind = (typename A::ReferenceKinds)ref->getKind(); + + switch ( kind ) { + case A::kNoFixUp: + case A::kFollowOn: + return 0; + + case A::kPointer: + case A::kPointerWeakImport: + if ( !isExtern && (ref->getTargetOffset() >= target.getSize()) ) { + // use scattered reloc is target offset is outside target + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(getRelocPointerSize()); + sreloc1->set_r_type(GENERIC_RELOC_VANILLA); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + else { + reloc1.set_r_address(address); + if ( isExtern ) + reloc1.set_r_symbolnum(symbolIndex); + else + reloc1.set_r_symbolnum(sectionNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(getRelocPointerSize()); + reloc1.set_r_extern(isExtern); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + } + fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + return 1; + + case A::kPointerDiff32: + case A::kPointerDiff64: + { + pint_t toAddr = target.getAddress() + ref->getTargetOffset(); + pint_t fromAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset(); + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length( (kind == A::kPointerDiff32) ? 2 : 3); + sreloc1->set_r_type(ref->getTargetOffset() != 0 ? PPC_RELOC_LOCAL_SECTDIFF : PPC_RELOC_SECTDIFF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(toAddr); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length( (kind == A::kPointerDiff32) ? 2 : 3); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address(0); + sreloc2->set_r_value(fromAddr); + fSectionRelocs.insert(fSectionRelocs.begin(), reloc2); + fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + return 2; + } + + case A::kBranch24WeakImport: + case A::kBranch24: + if ( (ref->getTargetOffset() == 0) || isExtern ) { + reloc1.set_r_address(address); + if ( isExtern ) + reloc1.set_r_symbolnum(symbolIndex); + else + reloc1.set_r_symbolnum(sectionNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_type(PPC_RELOC_BR24); + reloc1.set_r_extern(isExtern); + } + else { + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_BR24); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + return 1; + + case A::kBranch14: + if ( (ref->getTargetOffset() == 0) || isExtern ) { + reloc1.set_r_address(address); + if ( isExtern ) + reloc1.set_r_symbolnum(symbolIndex); + else + reloc1.set_r_symbolnum(sectionNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_type(PPC_RELOC_BR14); + reloc1.set_r_extern(isExtern); + } + else { + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_BR14); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + return 1; + + case A::kPICBaseLow16: + case A::kPICBaseLow14: + { + pint_t fromAddr = atom->getAddress() + ref->getFromTargetOffset(); + pint_t toAddr = target.getAddress() + ref->getTargetOffset(); + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(kind == A::kPICBaseLow16 ? PPC_RELOC_LO16_SECTDIFF : PPC_RELOC_LO14_SECTDIFF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(2); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address(((toAddr-fromAddr) >> 16)); + sreloc2->set_r_value(fromAddr); + fSectionRelocs.insert(fSectionRelocs.begin(), reloc2); + fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + return 2; + } + + case A::kPICBaseHigh16: + { + pint_t fromAddr = atom->getAddress() + ref->getFromTargetOffset(); + pint_t toAddr = target.getAddress() + ref->getTargetOffset(); + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_HA16_SECTDIFF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(2); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address((toAddr-fromAddr) & 0xFFFF); + sreloc2->set_r_value(fromAddr); + fSectionRelocs.insert(fSectionRelocs.begin(), reloc2); + fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + return 2; + } + + case A::kAbsLow14: + case A::kAbsLow16: + { + pint_t toAddr = target.getAddress() + ref->getTargetOffset(); + if ( (ref->getTargetOffset() == 0) || isExtern ) { + reloc1.set_r_address(address); + if ( isExtern ) + reloc1.set_r_symbolnum(symbolIndex); + else + reloc1.set_r_symbolnum(sectionNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(isExtern); + reloc1.set_r_type(kind==A::kAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); + } + else { + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(kind==A::kAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + if ( isExtern ) + reloc2.set_r_address(ref->getTargetOffset() >> 16); + else + reloc2.set_r_address(toAddr >> 16); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + fSectionRelocs.insert(fSectionRelocs.begin(), reloc2); + fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + return 2; + } + + case A::kAbsHigh16: + { + pint_t toAddr = target.getAddress() + ref->getTargetOffset(); + if ( (ref->getTargetOffset() == 0) || isExtern ) { + reloc1.set_r_address(address); + if ( isExtern ) + reloc1.set_r_symbolnum(symbolIndex); + else + reloc1.set_r_symbolnum(sectionNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(isExtern); + reloc1.set_r_type(PPC_RELOC_HI16); + } + else { + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_HI16); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + if ( isExtern ) + reloc2.set_r_address(ref->getTargetOffset() & 0xFFFF); + else + reloc2.set_r_address(toAddr & 0xFFFF); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + fSectionRelocs.insert(fSectionRelocs.begin(), reloc2); + fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + return 2; + } + + case A::kAbsHigh16AddLow: + { + pint_t toAddr = target.getAddress() + ref->getTargetOffset(); + uint32_t overflow = 0; + if ( (toAddr & 0x00008000) != 0 ) + overflow = 0x10000; + if ( (ref->getTargetOffset() == 0) || isExtern ) { + reloc1.set_r_address(address); + if ( isExtern ) + reloc1.set_r_symbolnum(symbolIndex); + else + reloc1.set_r_symbolnum(sectionNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(isExtern); + reloc1.set_r_type(PPC_RELOC_HA16); + } + else { + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_HA16); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + if ( isExtern ) + reloc2.set_r_address(ref->getTargetOffset() & 0xFFFF); + else + reloc2.set_r_address(toAddr & 0xFFFF); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + fSectionRelocs.insert(fSectionRelocs.begin(), reloc2); + fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + return 2; + } + + } + return 0; +} + + +template +void Writer::buildObjectFileFixups() +{ + uint32_t relocIndex = 0; + std::vector& segmentInfos = fSegmentInfos; + const int segCount = segmentInfos.size(); + for(int i=0; i < segCount; ++i) { + SegmentInfo* curSegment = segmentInfos[i]; + std::vector& sectionInfos = curSegment->fSections; + const int sectionCount = sectionInfos.size(); + for(int j=0; j < sectionCount; ++j) { + SectionInfo* curSection = sectionInfos[j]; + //fprintf(stderr, "buildObjectFileFixups(): starting section %s\n", curSection->fSectionName); + std::vector& sectionAtoms = curSection->fAtoms; + if ( ! curSection->fAllZeroFill ) { + if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers || curSection->fAllStubs ) + curSection->fIndirectSymbolOffset = fIndirectTableAtom->fTable.size(); + curSection->fRelocOffset = relocIndex; + const int atomCount = sectionAtoms.size(); + for (int k=0; k < atomCount; ++k) { + ObjectFile::Atom* atom = sectionAtoms[k]; + //fprintf(stderr, "buildObjectFileFixups(): atom %s\n", atom->getDisplayName()); + std::vector& refs = atom->getReferences(); + const int refCount = refs.size(); + for (int l=0; l < refCount; ++l) { + ObjectFile::Reference* ref = refs[l]; + if ( ref->getKind() == A::kFollowOn ) + fSeenFollowOnReferences = true; + if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers || curSection->fAllStubs ) { + uint32_t offsetInSection = atom->getSectionOffset(); + uint32_t indexInSection = offsetInSection / atom->getSize(); + uint32_t undefinedSymbolIndex; + if ( curSection->fAllStubs ) { + ObjectFile::Atom& stubTarget =ref->getTarget(); + ObjectFile::Atom& stubTargetTarget = stubTarget.getReferences()[0]->getTarget(); + undefinedSymbolIndex = this->symbolIndex(stubTargetTarget); + //fprintf(stderr, "stub %s ==> %s ==> %s ==> index:%u\n", atom->getDisplayName(), stubTarget.getDisplayName(), stubTargetTarget.getDisplayName(), undefinedSymbolIndex); + } + else { + if ( curSection->fAllNonLazyPointers + && (ref->getTarget().getScope() == ObjectFile::Atom::scopeLinkageUnit) + && !fOptions.keepPrivateExterns() ) + undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL; + else + undefinedSymbolIndex = this->symbolIndex(ref->getTarget()); + } + uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset; + IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex }; + //printf("fIndirectTableAtom->fTable.add(sectionIndex=%u, indirectTableIndex=%u => %u), size=%lld\n", indexInSection, indirectTableIndex, undefinedSymbolIndex, atom->getSize()); + fIndirectTableAtom->fTable.push_back(entry); + if ( curSection->fAllLazyPointers ) { + ObjectFile::Atom& target = ref->getTarget(); + ObjectFile::Atom& fromTarget = ref->getFromTarget(); + if ( &fromTarget == NULL ) { + fprintf(stderr, "lazy pointer %s missing initial binding\n", atom->getDisplayName()); + } + else { + bool isExtern = ( ((target.getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) + || (target.getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition)) + && (target.getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn) ); + macho_relocation_info

reloc1; + reloc1.set_r_address(atom->getSectionOffset()); + reloc1.set_r_symbolnum(isExtern ? this->symbolIndex(target) : target.getSection()->getIndex()); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(); + reloc1.set_r_extern(isExtern); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + ++relocIndex; + } + } + else if ( curSection->fAllStubs ) { + relocIndex += this->addObjectRelocs(atom, ref); + } + } + else { + relocIndex += this->addObjectRelocs(atom, ref); + } + } + } + curSection->fRelocCount = relocIndex - curSection->fRelocOffset; + } + } + } + + // now reverse reloc entries + for(int i=0; i < segCount; ++i) { + SegmentInfo* curSegment = segmentInfos[i]; + std::vector& sectionInfos = curSegment->fSections; + const int sectionCount = sectionInfos.size(); + for(int j=0; j < sectionCount; ++j) { + SectionInfo* curSection = sectionInfos[j]; + curSection->fRelocOffset = relocIndex - curSection->fRelocOffset - curSection->fRelocCount; + } + } + +} + +template <> +bool Writer::illegalRelocInFinalLinkedImage(uint8_t kind, bool slideable) +{ + switch ( kind ) { + case ppc::kAbsLow16: + case ppc::kAbsLow14: + case ppc::kAbsHigh16: + case ppc::kAbsHigh16AddLow: + if ( slideable ) + return true; + } + return false; +} + + +template <> +bool Writer::illegalRelocInFinalLinkedImage(uint8_t kind, bool slideable) +{ + switch ( kind ) { + case ppc::kAbsLow16: + case ppc::kAbsLow14: + case ppc::kAbsHigh16: + case ppc::kAbsHigh16AddLow: + if ( slideable ) + return true; + } + return false; +} + +template <> +bool Writer::illegalRelocInFinalLinkedImage(uint8_t kind, bool slideable) +{ + return false; +} + + + +template +typename Writer::RelocKind Writer::relocationNeededInFinalLinkedImage(const ObjectFile::Atom& target) const +{ + const bool slideable = (fOptions.outputKind() != Options::kDynamicExecutable) && (fOptions.outputKind() != Options::kStaticExecutable); + + switch ( target.getDefinitionKind() ) { + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + // for flat-namespace or interposable two-level-namespace + // all references to exported symbols get indirected + if ( this->shouldExport(target) && + ((fOptions.nameSpace() == Options::kFlatNameSpace) + || (fOptions.nameSpace() == Options::kForceFlatNameSpace) + || fOptions.interposable()) ) + return kRelocExternal; + else if ( slideable ) + return kRelocInternal; + else + return kRelocNone; + case ObjectFile::Atom::kWeakDefinition: + // all calls to global weak definitions get indirected + if ( this->shouldExport(target) ) + return kRelocExternal; + else if ( slideable ) + return kRelocInternal; + else + return kRelocNone; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + return kRelocExternal; + } + return kRelocNone; +} + +template +void Writer::buildExecutableFixups() +{ + const bool slideable = (fOptions.outputKind() != Options::kDynamicExecutable) && (fOptions.outputKind() != Options::kStaticExecutable); + fIndirectTableAtom->fTable.reserve(50); // minimize reallocations + std::vector& segmentInfos = fSegmentInfos; + const int segCount = segmentInfos.size(); + for(int i=0; i < segCount; ++i) { + SegmentInfo* curSegment = segmentInfos[i]; + std::vector& sectionInfos = curSegment->fSections; + const int sectionCount = sectionInfos.size(); + for(int j=0; j < sectionCount; ++j) { + SectionInfo* curSection = sectionInfos[j]; + //fprintf(stderr, "starting section %p\n", curSection->fSectionName); + std::vector& sectionAtoms = curSection->fAtoms; + if ( ! curSection->fAllZeroFill ) { + if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers || curSection->fAllStubs || curSection->fAllSelfModifyingStubs ) + curSection->fIndirectSymbolOffset = fIndirectTableAtom->fTable.size(); + const int atomCount = sectionAtoms.size(); + for (int k=0; k < atomCount; ++k) { + ObjectFile::Atom* atom = sectionAtoms[k]; + std::vector& refs = atom->getReferences(); + const int refCount = refs.size(); + //fprintf(stderr, "atom %s has %d references in section %s, %p\n", atom->getDisplayName(), refCount, curSection->fSectionName, atom->getSection()); + for (int l=0; l < refCount; ++l) { + ObjectFile::Reference* ref = refs[l]; + if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers ) { + // if atom is in (non)lazy_pointer section, this is encoded as an indirect symbol + if ( atom->getSize() != sizeof(pint_t) ) { + printf("wrong size pointer atom %s from file %s\n", atom->getDisplayName(), atom->getFile()->getPath()); + } + ObjectFile::Atom* pointerTarget = &(ref->getTarget()); + if ( curSection->fAllLazyPointers ) { + pointerTarget = ((LazyPointerAtom*)atom)->getTarget(); + } + uint32_t offsetInSection = atom->getSectionOffset(); + uint32_t indexInSection = offsetInSection / sizeof(pint_t); + uint32_t undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL; + if ( this->relocationNeededInFinalLinkedImage(*pointerTarget) == kRelocExternal ) + undefinedSymbolIndex = this->symbolIndex(*pointerTarget); + uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset; + IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex }; + //fprintf(stderr,"fIndirectTableAtom->fTable.add(%d-%d => 0x%X-%s), size=%lld\n", indexInSection, indirectTableIndex, undefinedSymbolIndex, ref->getTarget().getName(), atom->getSize()); + fIndirectTableAtom->fTable.push_back(entry); + if ( slideable && curSection->fAllLazyPointers ) { + // if this is a dylib/bundle, need vanilla internal relocation to fix up binding handler if image slides + macho_relocation_info

pblaReloc; + uint32_t sectionNum = 1; + if ( fDyldHelper != NULL ) + sectionNum = ((SectionInfo*)(fDyldHelper->getSection()))->getIndex(); + //fprintf(stderr, "lazy pointer reloc, section index=%u, section name=%s\n", sectionNum, curSection->fSectionName); + pblaReloc.set_r_address(atom->getAddress()-fOptions.baseAddress()); + pblaReloc.set_r_symbolnum(sectionNum); + pblaReloc.set_r_pcrel(false); + pblaReloc.set_r_length(); + pblaReloc.set_r_extern(false); + pblaReloc.set_r_type(GENERIC_RELOC_VANILLA); + fInternalRelocs.push_back(pblaReloc); + } + } + else if ( ref->getKind() == A::kPointer ) { + if ( slideable && ((curSegment->fInitProtection & VM_PROT_WRITE) == 0) ) { + throwf("pointer in read-only segment not allowed in slidable image, used in %s from %s", atom->getDisplayName(), atom->getFile()->getPath()); + } + switch ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) ) { + case kRelocNone: + // no reloc needed + break; + case kRelocInternal: + { + macho_relocation_info

internalReloc; + SectionInfo* sectInfo = (SectionInfo*)ref->getTarget().getSection(); + uint32_t sectionNum = sectInfo->getIndex(); + // special case _mh_dylib_header and friends which are not in any real section + if ( (sectionNum ==0) && sectInfo->fVirtualSection && (strcmp(sectInfo->fSectionName, "._mach_header") == 0) ) + sectionNum = 1; + internalReloc.set_r_address(atom->getAddress()+ref->getFixUpOffset()-fOptions.baseAddress()); + internalReloc.set_r_symbolnum(sectionNum); + internalReloc.set_r_pcrel(false); + internalReloc.set_r_length(); + internalReloc.set_r_extern(false); + internalReloc.set_r_type(GENERIC_RELOC_VANILLA); + fInternalRelocs.push_back(internalReloc); + } + break; + case kRelocExternal: + { + macho_relocation_info

externalReloc; + externalReloc.set_r_address(atom->getAddress()+ref->getFixUpOffset()-fOptions.baseAddress()); + externalReloc.set_r_symbolnum(this->symbolIndex(ref->getTarget())); + externalReloc.set_r_pcrel(false); + externalReloc.set_r_length(); + externalReloc.set_r_extern(true); + externalReloc.set_r_type(GENERIC_RELOC_VANILLA); + fExternalRelocs.push_back(externalReloc); + } + break; + } + } + else if ( this->illegalRelocInFinalLinkedImage(ref->getKind(), slideable) ) { + throwf("absolute addressing (perhaps -mdynamic-no-pic) used in %s from %s not allowed in slidable image", atom->getDisplayName(), atom->getFile()->getPath()); + } + } + if ( curSection->fAllSelfModifyingStubs || curSection->fAllStubs ) { + ObjectFile::Atom* stubTarget = ((StubAtom*)atom)->getTarget(); + uint32_t undefinedSymbolIndex = this->symbolIndex(*stubTarget); + uint32_t offsetInSection = atom->getSectionOffset(); + uint32_t indexInSection = offsetInSection / atom->getSize(); + uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset; + IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex }; + //fprintf(stderr,"for stub: fIndirectTableAtom->fTable.add(%d-%d => 0x%X-%s), size=%lld\n", indexInSection, indirectTableIndex, undefinedSymbolIndex, stubTarget->getName(), atom->getSize()); + fIndirectTableAtom->fTable.push_back(entry); + } + } + } + } + } +} + + +template <> +void Writer::writeNoOps(uint32_t from, uint32_t to) +{ + uint32_t ppcNop; + OSWriteBigInt32(&ppcNop, 0, 0x60000000); + for (uint32_t p=from; p < to; p += 4) + ::pwrite(fFileDescriptor, &ppcNop, 4, p); +} + +template <> +void Writer::writeNoOps(uint32_t from, uint32_t to) +{ + uint32_t ppcNop; + OSWriteBigInt32(&ppcNop, 0, 0x60000000); + for (uint32_t p=from; p < to; p += 4) + ::pwrite(fFileDescriptor, &ppcNop, 4, p); +} + +template <> +void Writer::writeNoOps(uint32_t from, uint32_t to) +{ + uint8_t x86Nop = 0x90; + for (uint32_t p=from; p < to; ++p) + ::pwrite(fFileDescriptor, &x86Nop, 1, p); +} + + +template +uint64_t Writer::writeAtoms() +{ + uint32_t end = 0; + uint8_t* buffer = new uint8_t[(fLargestAtomSize+4095) & (-4096)]; + std::vector& segmentInfos = fSegmentInfos; + const int segCount = segmentInfos.size(); + for(int i=0; i < segCount; ++i) { + SegmentInfo* curSegment = segmentInfos[i]; + bool isTextSeg = ((curSegment->fInitProtection & VM_PROT_EXECUTE) != 0); + std::vector& sectionInfos = curSegment->fSections; + const int sectionCount = sectionInfos.size(); + for(int j=0; j < sectionCount; ++j) { + SectionInfo* curSection = sectionInfos[j]; + std::vector& sectionAtoms = curSection->fAtoms; + //printf("writing with max atom size 0x%X\n", fLargestAtomSize); + //fprintf(stderr, "writing %d atoms for section %s\n", (int)sectionAtoms.size(), curSection->fSectionName); + if ( ! curSection->fAllZeroFill ) { + const int atomCount = sectionAtoms.size(); + end = curSection->fFileOffset; + bool needsNops = isTextSeg && (strcmp(curSection->fSectionName, "__cstring") != 0); + for (int k=0; k < atomCount; ++k) { + ObjectFile::Atom* atom = sectionAtoms[k]; + if ( (atom->getDefinitionKind() != ObjectFile::Atom::kExternalDefinition) + && (atom->getDefinitionKind() != ObjectFile::Atom::kExternalWeakDefinition) ) { + uint32_t offset = curSection->fFileOffset + atom->getSectionOffset(); + if ( offset != end ) { + if ( needsNops ) { + // fill gaps with no-ops + writeNoOps(end, offset); + } + else { + // zero fill gaps + if ( (offset-end) == 4 ) { + uint32_t zero = 0; + ::pwrite(fFileDescriptor, &zero, 4, end); + } + else { + uint8_t zero = 0x00; + for (uint32_t p=end; p < offset; ++p) + ::pwrite(fFileDescriptor, &zero, 1, p); + } + } + } + uint64_t atomSize = atom->getSize(); + if ( atomSize > fLargestAtomSize ) { + throwf("ld64 internal error: atom \"%s\"is larger than expected 0x%X > 0x%llX", + atom->getDisplayName(), atomSize, fLargestAtomSize); + } + end = offset+atomSize; + // copy raw bytes + atom->copyRawContent(buffer); + // apply any fix-ups + try { + std::vector& references = atom->getReferences(); + for (std::vector::iterator it=references.begin(); it != references.end(); it++) { + ObjectFile::Reference* ref = *it; + if ( fOptions.outputKind() == Options::kObjectFile ) { + // doing ld -r + // skip fix-ups for undefined targets + if ( &(ref->getTarget()) != NULL ) + this->fixUpReferenceRelocatable(ref, atom, buffer); + } + else { + // producing final linked image + this->fixUpReferenceFinal(ref, atom, buffer); + } + } + } + catch (const char* msg) { + throwf("%s in %s from %s", msg, atom->getDisplayName(), atom->getFile()->getPath()); + } + //fprintf(stderr, "writing 0x%08X -> 0x%08X (addr=0x%llX, size=0x%llX), atom %s\n", offset, end, atom->getAddress(), atom->getSize(), atom->getDisplayName()); + // write out + ::pwrite(fFileDescriptor, buffer, atom->getSize(), offset); + } + } + } + } + } + delete [] buffer; + return end; +} + + +template <> +void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; + switch ( (x86::ReferenceKinds)(ref->getKind()) ) { + case x86::kNoFixUp: + case x86::kFollowOn: + // do nothing + break; + case x86::kPointerWeakImport: + case x86::kPointer: + { + if ( ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kRegularDefinition ) { + // external realocation ==> pointer contains addend + LittleEndian::set32(*fixUp, ref->getTargetOffset()); + } + else { + // internal relocation => pointer contains target address + //printf("Atom::fixUpReferenceFinal() target.name=%s, target.address=0x%08llX\n", target.getDisplayName(), target.getAddress()); + LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); + } + } + break; + case x86::kPointerDiff: + LittleEndian::set32(*fixUp, + (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); + break; + case x86::kPCRel32WeakImport: + case x86::kPCRel32: + int64_t displacement = 0; + switch ( ref->getTarget().getDefinitionKind() ) { + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); + break; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + throw "codegen problem, can't use rel32 to external symbol"; + case ObjectFile::Atom::kTentativeDefinition: + displacement = 0; + break; + } + const int64_t bl_twoGigLimit = 0x7FFFFFFF; + if ( (displacement > bl_twoGigLimit) || (displacement < (-bl_twoGigLimit)) ) { + //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); + throw "rel32 out of range"; + } + LittleEndian::set32(*fixUp, (int32_t)displacement); + break; + } +} + +template <> +void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; + bool isExternal = ( (ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kRegularDefinition) + && shouldExport(ref->getTarget()) ); + switch (ref->getKind()) { + case x86::kNoFixUp: + case x86::kFollowOn: + // do nothing + break; + case x86::kPointer: + { + if ( isExternal ) { + // external realocation ==> pointer contains addend + LittleEndian::set32(*fixUp, ref->getTargetOffset()); + } + else { + // internal relocation + if ( ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition ) { + // pointer contains target address + LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); + } + else { + // pointer contains addend + LittleEndian::set32(*fixUp, ref->getTargetOffset()); + } + } + } + break; + case x86::kPointerDiff: + LittleEndian::set32(*fixUp, + (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); + break; + case x86::kPCRel32: + int64_t displacement = 0; + if ( isExternal ) + displacement = ref->getTargetOffset() - (inAtom->getAddress() + ref->getFixUpOffset() + 4); + else + displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); + const int64_t bl_twoGigLimit = 0x7FFFFFFF; + if ( (displacement > bl_twoGigLimit) || (displacement < (-bl_twoGigLimit)) ) { + //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); + throw "rel32 out of range"; + } + LittleEndian::set32(*fixUp, (int32_t)displacement); + break; + } +} + + +template <> +void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + fixUpReference_powerpc(ref, inAtom, buffer, true); +} + +template <> +void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + fixUpReference_powerpc(ref, inAtom, buffer, true); +} + +template <> +void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + fixUpReference_powerpc(ref, inAtom, buffer, false); +} + +template <> +void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + fixUpReference_powerpc(ref, inAtom, buffer, false); +} + +// +// ppc and ppc64 are mostly the same, so they share a template specialzation +// +template +void Writer::fixUpReference_powerpc(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[], bool finalLinkedImage) const +{ + uint32_t instruction; + uint32_t newInstruction; + int64_t displacement; + uint64_t targetAddr = ref->getTarget().getAddress() + ref->getTargetOffset(); + uint64_t picBaseAddr; + uint16_t instructionLowHalf; + uint16_t instructionHighHalf; + uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; + pint_t* fixUpPointer = (pint_t*)&buffer[ref->getFixUpOffset()]; + bool relocateableExternal; + + if ( finalLinkedImage ) + relocateableExternal = (relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal); + else + relocateableExternal = ( (ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kRegularDefinition) + && shouldExport(ref->getTarget()) ); + + const int64_t picbase_twoGigLimit = 0x80000000; + + switch ( (typename A::ReferenceKinds)(ref->getKind()) ) { + case A::kNoFixUp: + case A::kFollowOn: + // do nothing + break; + case A::kPointerWeakImport: + case A::kPointer: + { + //fprintf(stderr, "fixUpReferenceFinal: %s reference to %s\n", this->getDisplayName(), target.getDisplayName()); + if ( finalLinkedImage && ((SectionInfo*)inAtom->getSection())->fAllLazyPointers ) { + // lazy-symbol ==> pointer contains address of dyld_stub_binding_helper (stored in "from" target) + if ( fDyldHelper == NULL ) + throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; + P::setP(*fixUpPointer, fDyldHelper->getAddress()); + } + else if ( relocateableExternal ) { + // external realocation ==> pointer contains addend + P::setP(*fixUpPointer, ref->getTargetOffset()); + } + else { + // internal relocation + if ( finalLinkedImage || (ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition) ) { + // pointer contains target address + //printf("Atom::fixUpReference_powerpc() target.name=%s, target.address=0x%08llX\n", target.getDisplayName(), target.getAddress()); + P::setP(*fixUpPointer, targetAddr); + } + else { + // pointer contains addend + P::setP(*fixUpPointer, ref->getTargetOffset()); + } + } + } + break; + case A::kPointerDiff64: + P::setP(*fixUpPointer, targetAddr - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); + break; + case A::kPointerDiff32: + P::E::set32(*fixUp, targetAddr - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); + break; + case A::kBranch24WeakImport: + case A::kBranch24: + { + //fprintf(stderr, "bl fixup to %s at 0x%08llX, ", target.getDisplayName(), target.getAddress()); + int64_t displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset()); + if ( relocateableExternal ) { + // doing "ld -r" to an external symbol + // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target + displacement -= ref->getTarget().getAddress(); + } + else { + const int64_t bl_eightMegLimit = 0x00FFFFFF; + if ( (displacement > bl_eightMegLimit) || (displacement < (-bl_eightMegLimit)) ) { + //fprintf(stderr, "bl out of range (%lld max is +/-16M) from %s in %s to %s in %s\n", displacement, this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); + throwf("bl out of range (%lld max is +/-16M) from %s in %s to %s in %s", + displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), + ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); + } + } + instruction = BigEndian::get32(*fixUp); + newInstruction = (instruction & 0xFC000003) | ((uint32_t)displacement & 0x03FFFFFC); + //fprintf(stderr, "bl fixup: 0x%08X -> 0x%08X\n", instruction, newInstruction); + BigEndian::set32(*fixUp, newInstruction); + } + break; + case A::kBranch14: + { + //fprintf(stderr, "bc fixup %p to %s+0x%08X == 0x%08llX\n", this, ref->getTarget().getDisplayName(), ref->getTargetOffset(), targetAddr); + int64_t displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset()); + if ( relocateableExternal ) { + // doing "ld -r" to an external symbol + // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target + displacement -= ref->getTarget().getAddress(); + } + else { + const int64_t b_sixtyFourKiloLimit = 0x0000FFFF; + if ( (displacement > b_sixtyFourKiloLimit) || (displacement < (-b_sixtyFourKiloLimit)) ) { + //fprintf(stderr, "bl out of range (%lld max is +/-16M) from %s in %s to %s in %s\n", displacement, this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); + throwf("bc out of range (%lld max is +/-64K) from %s in %s to %s in %s", + displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), + ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); + } + } + //fprintf(stderr, "bc fixup displacement=0x%08llX, atom.addr=0x%08llX, atom.offset=0x%08X\n", displacement, inAtom->getAddress(), (uint32_t)ref->getFixUpOffset()); + instruction = BigEndian::get32(*fixUp); + newInstruction = (instruction & 0xFFFF0003) | ((uint32_t)displacement & 0x0000FFFC); + //fprintf(stderr, "bc fixup: 0x%08X -> 0x%08X\n", instruction, newInstruction); + BigEndian::set32(*fixUp, newInstruction); + } + break; + case A::kPICBaseLow16: + picBaseAddr = inAtom->getAddress() + ref->getFromTargetOffset(); + displacement = targetAddr - picBaseAddr; + if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) ) + throw "32-bit pic-base out of range"; + instructionLowHalf = (displacement & 0xFFFF); + instruction = BigEndian::get32(*fixUp); + newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; + BigEndian::set32(*fixUp, newInstruction); + break; + case A::kPICBaseLow14: + picBaseAddr = inAtom->getAddress() + ref->getFromTargetOffset(); + displacement = targetAddr - picBaseAddr; + if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) ) + throw "32-bit pic-base out of range"; + if ( (displacement & 0x3) != 0 ) + throwf("bad offset (0x%08X) for lo14 instruction pic-base fix-up", (uint32_t)displacement); + instructionLowHalf = (displacement & 0xFFFC); + instruction = BigEndian::get32(*fixUp); + newInstruction = (instruction & 0xFFFF0003) | instructionLowHalf; + BigEndian::set32(*fixUp, newInstruction); + break; + case A::kPICBaseHigh16: + picBaseAddr = inAtom->getAddress() + ref->getFromTargetOffset(); + displacement = targetAddr - picBaseAddr; + if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) ) + throw "32-bit pic-base out of range"; + instructionLowHalf = displacement >> 16; + if ( (displacement & 0x00008000) != 0 ) + ++instructionLowHalf; + instruction = BigEndian::get32(*fixUp); + newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; + BigEndian::set32(*fixUp, newInstruction); + break; + case A::kAbsLow16: + if ( relocateableExternal ) + targetAddr -= ref->getTarget().getAddress(); + instructionLowHalf = (targetAddr & 0xFFFF); + instruction = BigEndian::get32(*fixUp); + newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; + BigEndian::set32(*fixUp, newInstruction); + break; + case A::kAbsLow14: + if ( relocateableExternal ) + targetAddr -= ref->getTarget().getAddress(); + if ( (targetAddr & 0x3) != 0 ) + throw "bad address for absolute lo14 instruction fix-up"; + instructionLowHalf = (targetAddr & 0xFFFF); + instruction = BigEndian::get32(*fixUp); + newInstruction = (instruction & 0xFFFF0003) | instructionLowHalf; + BigEndian::set32(*fixUp, newInstruction); + break; + case A::kAbsHigh16: + if ( relocateableExternal ) + targetAddr -= ref->getTarget().getAddress(); + instructionHighHalf = (targetAddr >> 16); + instruction = BigEndian::get32(*fixUp); + newInstruction = (instruction & 0xFFFF0000) | instructionHighHalf; + BigEndian::set32(*fixUp, newInstruction); + break; + case A::kAbsHigh16AddLow: + if ( relocateableExternal ) + targetAddr -= ref->getTarget().getAddress(); + if ( targetAddr & 0x00008000 ) + targetAddr += 0x00010000; + instruction = BigEndian::get32(*fixUp); + newInstruction = (instruction & 0xFFFF0000) | (targetAddr >> 16); + BigEndian::set32(*fixUp, newInstruction); + break; + } +} + +template <> +bool Writer::stubableReferenceKind(uint8_t kind) +{ + return (kind == ppc::kBranch24 || kind == ppc::kBranch24WeakImport); +} + +template <> +bool Writer::stubableReferenceKind(uint8_t kind) +{ + return (kind == ppc64::kBranch24 || kind == ppc64::kBranch24WeakImport); +} + +template <> +bool Writer::stubableReferenceKind(uint8_t kind) +{ + return (kind == x86::kPCRel32 || kind == x86::kPCRel32WeakImport); +} + + +template <> +bool Writer::weakImportReferenceKind(uint8_t kind) +{ + return (kind == ppc::kBranch24WeakImport || kind == ppc::kPointerWeakImport); +} + +template <> +bool Writer::weakImportReferenceKind(uint8_t kind) +{ + return (kind == ppc64::kBranch24WeakImport || kind == ppc64::kPointerWeakImport); +} + +template <> +bool Writer::weakImportReferenceKind(uint8_t kind) +{ + return (kind == x86::kPCRel32WeakImport || kind == x86::kPointerWeakImport); +} + + + +template <> +bool Writer::GOTReferenceKind(uint8_t kind) +{ + return false; +} + +template <> +bool Writer::GOTReferenceKind(uint8_t kind) +{ + return false; +} + +template <> +bool Writer::GOTReferenceKind(uint8_t kind) +{ + return false; +} + + + +template +void Writer::synthesizeStubs() +{ + switch ( fOptions.outputKind() ) { + case Options::kStaticExecutable: + case Options::kDyld: + case Options::kObjectFile: + // these output kinds never have stubs + return; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kDynamicExecutable: + // try to synthesize stubs for these + break; + } + + // walk every atom and reference + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* ref = *rit; + ObjectFile::Atom& target = ref->getTarget(); + // build map of which symbols need weak importing + if ( (target.getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) + || (target.getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { + bool weakImport = this->weakImportReferenceKind(ref->getKind()); + std::map::iterator pos = fWeakImportMap.find(&target); + if ( pos == fWeakImportMap.end() ) { + // target not in fWeakImportMap, so add + fWeakImportMap[&target] = weakImport; + } + else { + // target in fWeakImportMap, check for weakness mismatch + if ( pos->second != weakImport ) { + // found mismatch + switch ( fOptions.weakReferenceMismatchTreatment() ) { + case Options::kWeakReferenceMismatchError: + throwf("mismatching weak references for symbol: %s", target.getName()); + case Options::kWeakReferenceMismatchWeak: + pos->second = true; + break; + case Options::kWeakReferenceMismatchNonWeak: + pos->second = false; + break; + } + } + } + } + // create stubs as needed + if ( this->stubableReferenceKind(ref->getKind()) + && this->relocationNeededInFinalLinkedImage(target) == kRelocExternal ) { + ObjectFile::Atom* stub = NULL; + std::map::iterator pos = fStubsMap.find(&target); + if ( pos == fStubsMap.end() ) { + stub = new StubAtom(*this, target); + fStubsMap[&target] = stub; + } + else { + stub = pos->second; + } + // alter reference to use stub instead + ref->setTarget(*stub, 0); + } + // create GOT slots (non-lazy pointers) as needed + else if ( this->GOTReferenceKind(ref->getKind()) ) { + ObjectFile::Atom* nlp = NULL; + std::map::iterator pos = fGOTMap.find(&target); + if ( pos == fGOTMap.end() ) { + nlp = new NonLazyPointerAtom(*this, target); + fGOTMap[&target] = nlp; + } + else { + nlp = pos->second; + } + // alter reference to use non lazy pointer instead + ref->setTarget(*nlp, 0); + } + } + } + + // sort stubs + + // sort lazy pointers + + // add stubs to fAllAtoms + if ( fAllSynthesizedStubs.size() != 0 ) { + std::vector* stubs = (std::vector*)&fAllSynthesizedStubs; + std::vector mergedStubs; + if ( fAllSynthesizedStubHelpers.size() != 0 ) { + // when we have stubs and helpers, insert both into fAllAtoms + mergedStubs.insert(mergedStubs.end(), fAllSynthesizedStubs.begin(), fAllSynthesizedStubs.end()); + mergedStubs.insert(mergedStubs.end(), fAllSynthesizedStubHelpers.begin(), fAllSynthesizedStubHelpers.end()); + stubs = &mergedStubs; + } + ObjectFile::Section* curSection = NULL; + ObjectFile::Atom* prevAtom = NULL; + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + ObjectFile::Section* nextSection = atom->getSection(); + if ( nextSection != curSection ) { + // HACK HACK for i386 where stubs are not in _TEXT segment + if ( strcmp(fAllSynthesizedStubs[0]->getSegment().getName(), "__IMPORT") == 0 ) { + if ( ((prevAtom != NULL) && (strcmp(prevAtom->getSegment().getName(), "__IMPORT") == 0)) + || (strcmp(atom->getSegment().getName(), "__LINKEDIT") == 0) ) { + // insert stubs at end of __IMPORT segment, or before __LINKEDIT + fAllAtoms->insert(it, fAllSynthesizedStubs.begin(), fAllSynthesizedStubs.end()); + break; + } + } + else { + if ( (prevAtom != NULL) && (strcmp(prevAtom->getSectionName(), "__text") == 0) ) { + // found end of __text section, insert stubs here + fAllAtoms->insert(it, stubs->begin(), stubs->end()); + break; + } + } + curSection = nextSection; + } + prevAtom = atom; + } + } + + + // add lazy pointers to fAllAtoms + if ( fAllSynthesizedLazyPointers.size() != 0 ) { + ObjectFile::Section* curSection = NULL; + ObjectFile::Atom* prevAtom = NULL; + bool inserted = false; + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + ObjectFile::Section* nextSection = atom->getSection(); + if ( nextSection != curSection ) { + if ( (prevAtom != NULL) && (strcmp(prevAtom->getSectionName(), "__dyld") == 0) ) { + // found end of __dyld section, insert lazy pointers here + fAllAtoms->insert(it, fAllSynthesizedLazyPointers.begin(), fAllSynthesizedLazyPointers.end()); + inserted = true; + break; + } + curSection = nextSection; + } + prevAtom = atom; + } + if ( !inserted ) { + throw "can't insert lazy pointers, __dyld section not found"; + } + } + + // add non-lazy pointers to fAllAtoms + if ( fAllSynthesizedNonLazyPointers.size() != 0 ) { + ObjectFile::Section* curSection = NULL; + ObjectFile::Atom* prevAtom = NULL; + bool inserted = false; + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + ObjectFile::Section* nextSection = atom->getSection(); + if ( nextSection != curSection ) { + if ( (prevAtom != NULL) && (strcmp(prevAtom->getSectionName(), "__dyld") == 0) ) { + // found end of __dyld section, insert lazy pointers here + fAllAtoms->insert(it, fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end()); + inserted = true; + break; + } + curSection = nextSection; + } + prevAtom = atom; + } + if ( !inserted ) { + throw "can't insert non-lazy pointers, __dyld section not found"; + } + } +} + + +template +void Writer::partitionIntoSections() +{ + const bool oneSegmentCommand = (fOptions.outputKind() == Options::kObjectFile); + + // for every atom, set its sectionInfo object and section offset + // build up fSegmentInfos along the way + ObjectFile::Section* curSection = NULL; + SectionInfo* currentSectionInfo = NULL; + SegmentInfo* currentSegmentInfo = NULL; + unsigned int sectionIndex = 1; + fSegmentInfos.reserve(8); + for (unsigned int i=0; i < fAllAtoms->size(); ++i) { + ObjectFile::Atom* atom = (*fAllAtoms)[i]; + if ( (atom->getSection() != curSection) || ((curSection==NULL) && (strcmp(atom->getSectionName(),currentSectionInfo->fSectionName) != 0)) ) { + if ( oneSegmentCommand ) { + if ( currentSegmentInfo == NULL ) { + currentSegmentInfo = new SegmentInfo(); + currentSegmentInfo->fInitProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; + currentSegmentInfo->fMaxProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; + this->fSegmentInfos.push_back(currentSegmentInfo); + } + currentSectionInfo = new SectionInfo(); + strcpy(currentSectionInfo->fSectionName, atom->getSectionName()); + strcpy(currentSectionInfo->fSegmentName, atom->getSegment().getName()); + currentSectionInfo->fAlignment = atom->getAlignment(); + currentSectionInfo->fAllZeroFill = atom->isZeroFill(); + currentSectionInfo->fVirtualSection = ( (currentSectionInfo->fSectionName[0] == '.') || (oneSegmentCommand && (atom->getDefinitionKind()==ObjectFile::Atom::kTentativeDefinition)) ); + if ( !currentSectionInfo->fVirtualSection || fEmitVirtualSections ) + currentSectionInfo->setIndex(sectionIndex++); + currentSegmentInfo->fSections.push_back(currentSectionInfo); + } + else { + if ( (currentSegmentInfo == NULL) || (strcmp(currentSegmentInfo->fName, atom->getSegment().getName()) != 0) ) { + currentSegmentInfo = new SegmentInfo(); + strcpy(currentSegmentInfo->fName, atom->getSegment().getName()); + uint32_t initprot = 0; + if ( atom->getSegment().isContentReadable() ) + initprot |= VM_PROT_READ; + if ( atom->getSegment().isContentWritable() ) + initprot |= VM_PROT_WRITE; + if ( atom->getSegment().isContentExecutable() ) + initprot |= VM_PROT_EXECUTE; + currentSegmentInfo->fInitProtection = initprot; + if ( initprot == 0 ) + currentSegmentInfo->fMaxProtection = 0; // pagezero should have maxprot==initprot==0 + else + currentSegmentInfo->fMaxProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; + currentSegmentInfo->fBaseAddress = atom->getSegment().getBaseAddress(); + currentSegmentInfo->fFixedAddress = atom->getSegment().hasFixedAddress(); + this->fSegmentInfos.push_back(currentSegmentInfo); + } + currentSectionInfo = new SectionInfo(); + currentSectionInfo->fAtoms.reserve(fAllAtoms->size()/4); // reduce reallocations by starting large + strcpy(currentSectionInfo->fSectionName, atom->getSectionName()); + strcpy(currentSectionInfo->fSegmentName, atom->getSegment().getName()); + currentSectionInfo->fAlignment = atom->getAlignment(); + // check for -sectalign override + std::vector& alignmentOverrides = fOptions.sectionAlignments(); + for(std::vector::iterator it=alignmentOverrides.begin(); it != alignmentOverrides.end(); ++it) { + if ( (strcmp(it->segmentName, currentSectionInfo->fSegmentName) == 0) && (strcmp(it->sectionName, currentSectionInfo->fSectionName) == 0) ) + currentSectionInfo->fAlignment = it->alignment; + } + currentSectionInfo->fAllZeroFill = atom->isZeroFill(); + currentSectionInfo->fVirtualSection = ( currentSectionInfo->fSectionName[0] == '.'); + if ( !currentSectionInfo->fVirtualSection || fEmitVirtualSections ) + currentSectionInfo->setIndex(sectionIndex++); + currentSegmentInfo->fSections.push_back(currentSectionInfo); + } + if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "._load_commands") == 0) ) { + fLoadCommandsSection = currentSectionInfo; + fLoadCommandsSegment = currentSegmentInfo; + } + if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__la_symbol_ptr") == 0) ) + currentSectionInfo->fAllLazyPointers = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__la_sym_ptr2") == 0) ) + currentSectionInfo->fAllLazyPointers = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__nl_symbol_ptr") == 0) ) + currentSectionInfo->fAllNonLazyPointers = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__IMPORT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__pointers") == 0) ) + currentSectionInfo->fAllNonLazyPointers = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__picsymbolstub1") == 0) ) + currentSectionInfo->fAllStubs = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__symbol_stub1") == 0) ) + currentSectionInfo->fAllStubs = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__picsymbolstub2") == 0) ) + currentSectionInfo->fAllStubs = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__symbol_stub") == 0) ) + currentSectionInfo->fAllStubs = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__IMPORT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__jump_table") == 0) ) + currentSectionInfo->fAllSelfModifyingStubs = true; + curSection = atom->getSection(); + } + // any non-zero fill atoms make whole section marked not-zero-fill + if ( currentSectionInfo->fAllZeroFill && ! atom->isZeroFill() ) + currentSectionInfo->fAllZeroFill = false; + // change section object to be Writer's SectionInfo object + atom->setSection(currentSectionInfo); + // section alignment is that of a contained atom with the greatest alignment + uint8_t atomAlign = atom->getAlignment(); + if ( currentSectionInfo->fAlignment < atomAlign ) + currentSectionInfo->fAlignment = atomAlign; + // calculate section offset for this atom + uint64_t offset = currentSectionInfo->fSize; + uint64_t alignment = 1 << atomAlign; + offset = ( (offset+alignment-1) & (-alignment) ); + atom->setSectionOffset(offset); + uint64_t curAtomSize = atom->getSize(); + currentSectionInfo->fSize = offset + curAtomSize; + // add atom to section vector + currentSectionInfo->fAtoms.push_back(atom); + // update largest size + if ( !currentSectionInfo->fAllZeroFill && (curAtomSize > fLargestAtomSize) ) + fLargestAtomSize = curAtomSize; + } +} + + +struct TargetAndOffset { ObjectFile::Atom* atom; uint32_t offset; }; +class TargetAndOffsetComparor +{ +public: + bool operator()(const TargetAndOffset& left, const TargetAndOffset& right) const + { + if ( left.atom != right.atom ) + return ( left.atom < right.atom ); + return ( left.offset < right.offset ); + } +}; + +template <> +bool Writer::addBranchIslands() +{ + return this->addPPCBranchIslands(); +} + +template <> +bool Writer::addBranchIslands() +{ + return this->addPPCBranchIslands(); +} + +template <> +bool Writer::addBranchIslands() +{ + // x86 branches can reach entire 4G address space, so no need for branch islands + return false; +} + + +template <> +inline uint8_t Writer::branch24Reference() +{ + return ppc::kBranch24; +} + +template <> +inline uint8_t Writer::branch24Reference() +{ + return ppc64::kBranch24; +} + +// +// PowerPC can do PC relative branches as far as +/-16MB. +// If a branch target is >16MB then we insert one or more +// "branch islands" between the branch and its target that +// allows island hoping to the target. +// +// Branch Island Algorithm +// +// If the __TEXT segment < 16MB, then no branch islands needed +// Otherwise, every 15MB into the __TEXT segment is region is +// added which can contain branch islands. Every out of range +// bl instruction is checked. If it crosses a region, an island +// is added to that region with the same target and the bl is +// adjusted to target the island instead. +// +// In theory, if too many islands are added to one region, it +// could grow the __TEXT enough that other previously in-range +// bl branches could be pushed out of range. We reduce the +// probability this could happen by placing the ranges every +// 15MB which means the region would have to be 1MB (256K islands) +// before any branches could be pushed out of range. +// +template +bool Writer::addPPCBranchIslands() +{ + bool result = false; + // Can only possibly need branch islands if __TEXT segment > 16M + if ( fLoadCommandsSegment->fSize > 16000000 ) { + //fprintf(stderr, "ld64: checking for branch islands, __TEXT segment size=%llu\n", fLoadCommandsSegment->fSize); + const uint32_t kBetweenRegions = 15000000; // place regions of islands every 15MB in __text section + SectionInfo* textSection = NULL; + for (std::vector::iterator it=fLoadCommandsSegment->fSections.begin(); it != fLoadCommandsSegment->fSections.end(); it++) { + if ( strcmp((*it)->fSectionName, "__text") == 0 ) { + textSection = *it; + //fprintf(stderr, "ld64: checking for branch islands, __text section size=%llu\n", textSection->fSize); + break; + } + } + const int kIslandRegionsCount = fLoadCommandsSegment->fSize / kBetweenRegions; + typedef std::map AtomToIsland; + AtomToIsland regionsMap[kIslandRegionsCount]; + std::vector regionsIslands[kIslandRegionsCount]; + unsigned int islandCount = 0; + + // create islands for branch references that are out of range + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* ref = *rit; + if ( ref->getKind() == this->branch24Reference() ) { + ObjectFile::Atom& target = ref->getTarget(); + int64_t srcAddr = atom->getAddress() + ref->getFixUpOffset(); + int64_t dstAddr = target.getAddress() + ref->getTargetOffset(); + int64_t displacement = dstAddr - srcAddr; + const int64_t kFifteenMegLimit = kBetweenRegions; + if ( (displacement > kFifteenMegLimit) || (displacement < (-kFifteenMegLimit)) ) { + for (int i=0; i < kIslandRegionsCount; ++i) { + AtomToIsland* region=®ionsMap[i]; + int64_t islandRegionAddr = kBetweenRegions * (i+1); + if ( ((srcAddr < islandRegionAddr) && (dstAddr > islandRegionAddr)) + ||((dstAddr < islandRegionAddr) && (srcAddr > islandRegionAddr)) ) { + TargetAndOffset islandTarget = { &target, ref->getTargetOffset() }; + AtomToIsland::iterator pos = region->find(islandTarget); + if ( pos == region->end() ) { + BranchIslandAtom* island = new BranchIslandAtom(*this, target.getDisplayName(), i, target, ref->getTargetOffset()); + island->setSection(textSection); + (*region)[islandTarget] = island; + regionsIslands[i].push_back(island); + ++islandCount; + ref->setTarget(*island, 0); + } + else { + ref->setTarget(*(pos->second), 0); + } + } + } + } + } + } + } + + // insert islands into __text section and adjust section offsets + if ( islandCount > 0 ) { + //fprintf(stderr, "ld64: %u branch islands required\n", islandCount); + std::vector newAtomList; + newAtomList.reserve(textSection->fAtoms.size()+islandCount); + uint64_t islandRegionAddr = kBetweenRegions; + int regionIndex = 0; + uint64_t sectionOffset = 0; + for (std::vector::iterator it=textSection->fAtoms.begin(); it != textSection->fAtoms.end(); it++) { + ObjectFile::Atom* atom = *it; + newAtomList.push_back(atom); + if ( atom->getAddress() > islandRegionAddr ) { + std::vector* regionIslands = ®ionsIslands[regionIndex]; + for (std::vector::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) { + ObjectFile::Atom* islandAtom = *rit; + newAtomList.push_back(islandAtom); + uint64_t alignment = 1 << (islandAtom->getAlignment()); + sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) ); + islandAtom->setSectionOffset(sectionOffset); + sectionOffset += islandAtom->getSize(); + } + ++regionIndex; + islandRegionAddr += kBetweenRegions; + } + uint64_t alignment = 1 << (atom->getAlignment()); + sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) ); + atom->setSectionOffset(sectionOffset); + sectionOffset += atom->getSize(); + } + // put any remaining islands at end of __text section + if ( regionIndex < kIslandRegionsCount ) { + sectionOffset = textSection->fSize; + std::vector* regionIslands = ®ionsIslands[regionIndex]; + for (std::vector::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) { + ObjectFile::Atom* islandAtom = *rit; + newAtomList.push_back(islandAtom); + uint64_t alignment = 1 << (islandAtom->getAlignment()); + sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) ); + islandAtom->setSectionOffset(sectionOffset); + sectionOffset += islandAtom->getSize(); + } + } + + textSection->fAtoms = newAtomList; + textSection->fSize = sectionOffset; + result = true; + } + + } + return result; +} + + +template +void Writer::adjustLoadCommandsAndPadding() +{ + fSegmentCommands->computeSize(); + + // recompute load command section offsets + uint64_t offset = 0; + std::vector& loadCommandAtoms = fLoadCommandsSection->fAtoms; + const unsigned int atomCount = loadCommandAtoms.size(); + for (unsigned int i=0; i < atomCount; ++i) { + ObjectFile::Atom* atom = loadCommandAtoms[i]; + uint64_t alignment = 1 << atom->getAlignment(); + offset = ( (offset+alignment-1) & (-alignment) ); + atom->setSectionOffset(offset); + uint32_t atomSize = atom->getSize(); + if ( atomSize > fLargestAtomSize ) + fLargestAtomSize = atomSize; + offset += atomSize; + fLoadCommandsSection->fSize = offset; + } + + std::vector& sectionInfos = fLoadCommandsSegment->fSections; + const int sectionCount = sectionInfos.size(); + uint64_t paddingSize = 0; + if ( fOptions.outputKind() == Options::kDyld ) { + // dyld itself has special padding requirements. We want the beginning __text section to start at a stable address + uint32_t totalSizeOfHeaderAndLoadCommands = 0; + for(int j=0; j < sectionCount; ++j) { + SectionInfo* curSection = sectionInfos[j]; + totalSizeOfHeaderAndLoadCommands += curSection->fSize; + if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 ) + break; + } + paddingSize = 4096 - (totalSizeOfHeaderAndLoadCommands % 4096); + } + else if ( fOptions.outputKind() == Options::kObjectFile ) { + // mach-o .o files need no padding between load commands and first section + paddingSize = 0; + } + else { + // calculate max padding to keep segment size same, but all free space at end of load commands + uint64_t totalSize = 0; + uint64_t worstCaseAlignmentPadding = 0; + for(int j=0; j < sectionCount; ++j) { + SectionInfo* curSection = sectionInfos[j]; + totalSize += curSection->fSize; + if ( j != 0 ) // don't count aligment of mach_header which is page-aligned + worstCaseAlignmentPadding += (1 << curSection->fAlignment) - 1; + } + uint64_t segmentSize = ((totalSize+worstCaseAlignmentPadding+4095) & (-4096)); + // don't know exactly how it will layout, but we can inflate padding atom this big and still keep aligment constraints + paddingSize = segmentSize - (totalSize+worstCaseAlignmentPadding); + + // if command line requires more padding than this + if ( paddingSize < fOptions.minimumHeaderPad() ) { + int extraPages = (fOptions.minimumHeaderPad() - paddingSize + 4095)/4096; + paddingSize += extraPages * 4096; + } + } + + // adjust atom size and update section size + fHeaderPadding->setSize(paddingSize); + for(int j=0; j < sectionCount; ++j) { + SectionInfo* curSection = sectionInfos[j]; + if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 ) + curSection->fSize = paddingSize; + } +} + +// assign file offsets and logical address to all segments +template +void Writer::assignFileOffsets() +{ + bool finalLinkedImage = (fOptions.outputKind() != Options::kObjectFile); + bool haveFixedSegments = false; + uint64_t fileOffset = 0; + uint64_t nextContiguousAddress = fOptions.baseAddress(); + + // Run through the segments and each segment's sections to assign addresses + for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { + SegmentInfo* curSegment = *segit; + + fileOffset = (fileOffset+4095) & (-4096); + curSegment->fFileOffset = fileOffset; + + // Set the segment base address + if ( curSegment->fFixedAddress ) + haveFixedSegments = true; + else + curSegment->fBaseAddress = nextContiguousAddress; + + // We've set the segment address, now run through each section. + uint64_t address = curSegment->fBaseAddress; + SectionInfo* firstZeroFillSection = NULL; + SectionInfo* prevSection = NULL; + + std::vector& sectionInfos = curSegment->fSections; + + for (std::vector::iterator it = sectionInfos.begin(); it != sectionInfos.end(); ++it) { + SectionInfo* curSection = *it; + + // adjust section address based on alignment + uint64_t alignment = 1 << curSection->fAlignment; + address = ( (address+alignment-1) & (-alignment) ); + + // adjust file offset to match address + if ( prevSection != NULL ) { + fileOffset = (address - prevSection->getBaseAddress()) + prevSection->fFileOffset; + } + + // update section info + curSection->fFileOffset = fileOffset; + curSection->setBaseAddress(address); + + // keep track of trailing zero fill sections + if ( curSection->fAllZeroFill && (firstZeroFillSection == NULL) ) + firstZeroFillSection = curSection; + if ( !curSection->fAllZeroFill && (firstZeroFillSection != NULL) && finalLinkedImage ) + throwf("zero-fill section %s not at end of segment", curSection->fSectionName); + + // update running pointers + address += curSection->fSize; + fileOffset += curSection->fSize; + + // update segment info + curSegment->fFileSize = fileOffset - curSegment->fFileOffset; + curSegment->fSize = curSegment->fFileSize; + prevSection = curSection; + } + + if ( fOptions.outputKind() == Options::kObjectFile ) { + // don't page align .o files + } + else { + // optimize trailing zero-fill sections to not occupy disk space + if ( firstZeroFillSection != NULL ) { + curSegment->fFileSize = firstZeroFillSection->fFileOffset - curSegment->fFileOffset; + fileOffset = firstZeroFillSection->fFileOffset; + } + // page align segment size + curSegment->fFileSize = (curSegment->fFileSize+4095) & (-4096); + curSegment->fSize = (curSegment->fSize+4095) & (-4096); + if ( curSegment->fBaseAddress == nextContiguousAddress ) + nextContiguousAddress = (curSegment->fBaseAddress+curSegment->fSize+4095) & (-4096); + } + } + + // check for segment overlaps caused by user specified fixed segments (e.g. __PAGEZERO, __UNIXSTACK) + if ( haveFixedSegments ) { + int segCount = fSegmentInfos.size(); + + for(int i=0; i < segCount; ++i) { + SegmentInfo* segment1 = fSegmentInfos[i]; + + for(int j=0; j < segCount; ++j) { + if ( i != j ) { + SegmentInfo* segment2 = fSegmentInfos[j]; + + if ( segment1->fBaseAddress < segment2->fBaseAddress ) { + if ( (segment1->fBaseAddress+segment1->fSize) > segment2->fBaseAddress ) + throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)", + segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize); + } + else if ( segment1->fBaseAddress > segment2->fBaseAddress ) { + if ( (segment2->fBaseAddress+segment2->fSize) > segment1->fBaseAddress ) + throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)", + segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize); + } + else { + throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)", + segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize); + } + } + } + } + } +} + +template +void Writer::adjustLinkEditSections() +{ + // link edit content is always in last segment + SegmentInfo* lastSeg = fSegmentInfos[fSegmentInfos.size()-1]; + unsigned int firstLinkEditSectionIndex = 0; + while ( strcmp(lastSeg->fSections[firstLinkEditSectionIndex]->fSegmentName, "__LINKEDIT") != 0 ) + ++firstLinkEditSectionIndex; + + const unsigned int sectionCount = lastSeg->fSections.size(); + uint64_t fileOffset = lastSeg->fSections[firstLinkEditSectionIndex]->fFileOffset; + uint64_t address = lastSeg->fSections[firstLinkEditSectionIndex]->getBaseAddress(); + for (unsigned int i=firstLinkEditSectionIndex; i < sectionCount; ++i) { + std::vector& atoms = lastSeg->fSections[i]->fAtoms; + const unsigned int atomCount = atoms.size(); + uint64_t sectionOffset = 0; + lastSeg->fSections[i]->fFileOffset = fileOffset; + lastSeg->fSections[i]->setBaseAddress(address); + for (unsigned int j=0; j < atomCount; ++j) { + ObjectFile::Atom* atom = atoms[j]; + uint64_t alignment = 1 << atom->getAlignment(); + sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) ); + atom->setSectionOffset(sectionOffset); + uint64_t size = atom->getSize(); + sectionOffset += size; + if ( size > fLargestAtomSize ) + fLargestAtomSize = size; + } + lastSeg->fSections[i]->fSize = sectionOffset; + fileOffset += sectionOffset; + address += sectionOffset; + } + if ( fOptions.outputKind() == Options::kObjectFile ) { + //lastSeg->fBaseAddress = 0; + //lastSeg->fSize = lastSeg->fSections[firstLinkEditSectionIndex]-> + //lastSeg->fFileOffset = 0; + //lastSeg->fFileSize = + } + else { + lastSeg->fFileSize = fileOffset - lastSeg->fFileOffset; + lastSeg->fSize = (address - lastSeg->fBaseAddress+4095) & (-4096); + } +} + + +template +ObjectFile::Atom::Scope MachHeaderAtom::getScope() const +{ + switch ( fWriter.fOptions.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + return ObjectFile::Atom::scopeGlobal; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kDyld: + case Options::kObjectFile: + return ObjectFile::Atom::scopeLinkageUnit; + } + throw "unknown header type"; +} + +template +ObjectFile::Atom::SymbolTableInclusion MachHeaderAtom::getSymbolTableInclusion() const +{ + switch ( fWriter.fOptions.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + return ObjectFile::Atom::kSymbolTableInAndNeverStrip; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kDyld: + return ObjectFile::Atom::kSymbolTableIn; + case Options::kObjectFile: + return ObjectFile::Atom::kSymbolTableNotIn; + } + throw "unknown header type"; +} + +template +const char* MachHeaderAtom::getName() const +{ + switch ( fWriter.fOptions.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + return "__mh_execute_header"; + case Options::kDynamicLibrary: + return "__mh_dylib_header"; + case Options::kDynamicBundle: + return "__mh_bundle_header"; + case Options::kObjectFile: + return NULL; + case Options::kDyld: + return "__mh_dylinker_header"; + } + throw "unknown header type"; +} + +template +const char* MachHeaderAtom::getDisplayName() const +{ + switch ( fWriter.fOptions.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kDyld: + return this->getName(); + case Options::kObjectFile: + return "mach header"; + } + throw "unknown header type"; +} + +template +void MachHeaderAtom::copyRawContent(uint8_t buffer[]) const +{ + // get file type + uint32_t fileType = 0; + switch ( fWriter.fOptions.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + fileType = MH_EXECUTE; + break; + case Options::kDynamicLibrary: + fileType = MH_DYLIB; + break; + case Options::kDynamicBundle: + fileType = MH_BUNDLE; + break; + case Options::kObjectFile: + fileType = MH_OBJECT; + break; + case Options::kDyld: + fileType = MH_DYLINKER; + break; + } + + // get flags + uint32_t flags = 0; + if ( fWriter.fOptions.outputKind() == Options::kObjectFile ) { + if ( ! fWriter.fSeenFollowOnReferences ) + flags = MH_SUBSECTIONS_VIA_SYMBOLS; + } + else { + if ( fWriter.fOptions.outputKind() == Options::kStaticExecutable ) { + flags |= MH_NOUNDEFS; + } + else { + flags = MH_DYLDLINK; + if ( fWriter.fOptions.bindAtLoad() ) + flags |= MH_BINDATLOAD; + switch ( fWriter.fOptions.nameSpace() ) { + case Options::kTwoLevelNameSpace: + flags |= MH_TWOLEVEL | MH_NOUNDEFS; + break; + case Options::kFlatNameSpace: + break; + case Options::kForceFlatNameSpace: + flags |= MH_FORCE_FLAT; + break; + } + if ( fWriter.fHasWeakExports ) + flags |= MH_WEAK_DEFINES; + if ( fWriter.fReferencesWeakImports || fWriter.fHasWeakExports ) + flags |= MH_BINDS_TO_WEAK; + } + if ( fWriter.fOptions.hasExecutableStack() ) + flags |= MH_ALLOW_STACK_EXECUTION; + } + + // get commands info + uint32_t commandsSize = 0; + uint32_t commandsCount = 0; + + std::vector& loadCommandAtoms = fWriter.fLoadCommandsSection->fAtoms; + const unsigned int atomCount = loadCommandAtoms.size(); + for (unsigned int i=0; i < atomCount; ++i) { + ObjectFile::Atom* atom = loadCommandAtoms[i]; + commandsSize += atom->getSize(); + // segment and symbol table atoms can contain more than one load command + if ( atom == fWriter.fSegmentCommands ) + commandsCount += fWriter.fSegmentCommands->commandCount(); + else if ( atom == fWriter.fSymbolTableCommands ) + commandsCount += fWriter.fSymbolTableCommands->commandCount(); + else if ( atom->getSize() != 0) + ++commandsCount; + } + + // fill out mach_header + macho_header* mh = (macho_header*)buffer; + setHeaderInfo(*mh); + mh->set_filetype(fileType); + mh->set_ncmds(commandsCount); + mh->set_sizeofcmds(commandsSize); + mh->set_flags(flags); +} + +template <> +void MachHeaderAtom::setHeaderInfo(macho_header& header) const +{ + header.set_magic(MH_MAGIC); + header.set_cputype(CPU_TYPE_POWERPC); + header.set_cpusubtype(CPU_SUBTYPE_POWERPC_ALL); +} + +template <> +void MachHeaderAtom::setHeaderInfo(macho_header& header) const +{ + header.set_magic(MH_MAGIC_64); + header.set_cputype(CPU_TYPE_POWERPC64); + header.set_cpusubtype(CPU_SUBTYPE_POWERPC_ALL); + header.set_reserved(0); +} + +template <> +void MachHeaderAtom::setHeaderInfo(macho_header& header) const +{ + header.set_magic(MH_MAGIC); + header.set_cputype(CPU_TYPE_I386); + header.set_cpusubtype(CPU_SUBTYPE_I386_ALL); +} + + +template +CustomStackAtom::CustomStackAtom(Writer& writer) + : WriterAtom(writer, Segment::fgStackSegment) +{ + if ( stackGrowsDown() ) + Segment::fgStackSegment.setBaseAddress(writer.fOptions.customStackAddr() - writer.fOptions.customStackSize()); + else + Segment::fgStackSegment.setBaseAddress(writer.fOptions.customStackAddr()); +} + + +template <> +bool CustomStackAtom::stackGrowsDown() +{ + return true; +} + +template <> +bool CustomStackAtom::stackGrowsDown() +{ + return true; +} + +template <> +bool CustomStackAtom::stackGrowsDown() +{ + return true; +} + + +template +void SegmentLoadCommandsAtom::computeSize() +{ + uint64_t size = 0; + std::vector& segmentInfos = fWriter.fSegmentInfos; + const int segCount = segmentInfos.size(); + for(int i=0; i < segCount; ++i) { + size += sizeof(macho_segment_command

); + std::vector& sectionInfos = segmentInfos[i]->fSections; + const int sectionCount = sectionInfos.size(); + for(int j=0; j < sectionCount; ++j) { + if ( fWriter.fEmitVirtualSections || ! sectionInfos[j]->fVirtualSection ) + size += sizeof(macho_section

); + } + } + fSize = size; + fCommandCount = segCount; +} + +template <> +uint64_t LoadCommandAtom::alignedSize(uint64_t size) +{ + return ((size+3) & (-4)); // 4-byte align all load commands for 32-bit mach-o +} + +template <> +uint64_t LoadCommandAtom::alignedSize(uint64_t size) +{ + return ((size+7) & (-8)); // 8-byte align all load commands for 64-bit mach-o +} + +template <> +uint64_t LoadCommandAtom::alignedSize(uint64_t size) +{ + return ((size+3) & (-4)); // 4-byte align all load commands for 32-bit mach-o +} + + + +template +void SegmentLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + const bool oneSegment =( fWriter.fOptions.outputKind() == Options::kObjectFile ); + bzero(buffer, size); + uint8_t* p = buffer; + typename std::vector& segmentInfos = fWriter.fSegmentInfos; + const int segCount = segmentInfos.size(); + for(int i=0; i < segCount; ++i) { + SegmentInfo* segInfo = segmentInfos[i]; + const int sectionCount = segInfo->fSections.size(); + macho_segment_command

* cmd = (macho_segment_command

*)p; + cmd->set_cmd(macho_segment_command

::CMD); + cmd->set_segname(segInfo->fName); + cmd->set_vmaddr(segInfo->fBaseAddress); + cmd->set_vmsize(segInfo->fSize); + cmd->set_fileoff(segInfo->fFileOffset); + cmd->set_filesize(segInfo->fFileSize); + cmd->set_maxprot(segInfo->fMaxProtection); + cmd->set_initprot(segInfo->fInitProtection); + // add sections array + macho_section

* const sections = (macho_section

*)&p[sizeof(macho_segment_command

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

* sect = §ions[sectionsEmitted++]; + if ( oneSegment ) { + // .o file segment does not cover load commands, so recalc at first real section + if ( sectionsEmitted == 1 ) { + cmd->set_vmaddr(sectInfo->getBaseAddress()); + cmd->set_fileoff(sectInfo->fFileOffset); + } + cmd->set_filesize((sectInfo->fFileOffset+sectInfo->fSize)-cmd->fileoff()); + cmd->set_vmsize(sectInfo->getBaseAddress() + sectInfo->fSize); + } + sect->set_sectname(sectInfo->fSectionName); + sect->set_segname(sectInfo->fSegmentName); + sect->set_addr(sectInfo->getBaseAddress()); + sect->set_size(sectInfo->fSize); + sect->set_offset(sectInfo->fFileOffset); + sect->set_align(sectInfo->fAlignment); + if ( sectInfo->fRelocCount != 0 ) { + sect->set_reloff(sectInfo->fRelocOffset * sizeof(macho_relocation_info

) + fWriter.fSectionRelocationsAtom->getFileOffset()); + sect->set_nreloc(sectInfo->fRelocCount); + } + if ( sectInfo->fAllZeroFill ) { + sect->set_flags(S_ZEROFILL); + sect->set_offset(0); + } + else if ( sectInfo->fAllLazyPointers ) { + sect->set_flags(S_LAZY_SYMBOL_POINTERS); + sect->set_reserved1(sectInfo->fIndirectSymbolOffset); + } + else if ( sectInfo->fAllNonLazyPointers ) { + sect->set_flags(S_NON_LAZY_SYMBOL_POINTERS); + sect->set_reserved1(sectInfo->fIndirectSymbolOffset); + } + else if ( sectInfo->fAllStubs ) { + sect->set_flags(S_SYMBOL_STUBS); + sect->set_reserved1(sectInfo->fIndirectSymbolOffset); + sect->set_reserved2(sectInfo->fSize / sectInfo->fAtoms.size()); + } + else if ( sectInfo->fAllSelfModifyingStubs ) { + sect->set_flags(S_SYMBOL_STUBS | S_ATTR_SELF_MODIFYING_CODE); + sect->set_reserved1(sectInfo->fIndirectSymbolOffset); + sect->set_reserved2(sectInfo->fSize / sectInfo->fAtoms.size()); + } + else if ( (strcmp(sectInfo->fSectionName, "__mod_init_func") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { + sect->set_flags(S_MOD_INIT_FUNC_POINTERS); + } + else if ( (strcmp(sectInfo->fSectionName, "__mod_term_func") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { + sect->set_flags(S_MOD_TERM_FUNC_POINTERS); + } + else if ( (strcmp(sectInfo->fSectionName, "__eh_frame") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { + sect->set_flags(S_COALESCED); + } + else if ( (strcmp(sectInfo->fSectionName, "__textcoal_nt") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { + sect->set_flags(S_COALESCED); + } + else if ( (strcmp(sectInfo->fSectionName, "__const_coal") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { + sect->set_flags(S_COALESCED); + } + else if ( (strcmp(sectInfo->fSectionName, "__interpose") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { + sect->set_flags(S_INTERPOSING); + } + else if ( (strcmp(sectInfo->fSectionName, "__cstring") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { + sect->set_flags(S_CSTRING_LITERALS); + } + else if ( (strcmp(sectInfo->fSectionName, "__literal4") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { + sect->set_flags(S_4BYTE_LITERALS); + } + else if ( (strcmp(sectInfo->fSectionName, "__literal8") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { + sect->set_flags(S_8BYTE_LITERALS); + } + else if ( (strcmp(sectInfo->fSectionName, "__message_refs") == 0) && (strcmp(sectInfo->fSegmentName, "__OBJC") == 0) ) { + sect->set_flags(S_LITERAL_POINTERS); + } + } + } + p = &p[sizeof(macho_segment_command

) + sectionsEmitted*sizeof(macho_section

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

) + sectionsEmitted*sizeof(macho_section

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

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

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

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

) + sizeof(macho_dysymtab_command

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

* symbolTableCmd = (macho_symtab_command

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

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

)); + symbolTableCmd->set_nsyms(fWriter.fSymbolTableCount); + symbolTableCmd->set_symoff(fWriter.fSymbolTableAtom->getFileOffset()); + symbolTableCmd->set_stroff(fWriter.fStringsAtom->getFileOffset()); + symbolTableCmd->set_strsize(fWriter.fStringsAtom->getSize()); + + // build LC_DYSYMTAB command + if ( fWriter.fOptions.outputKind() != Options::kStaticExecutable ) { + macho_dysymtab_command

* dynamicSymbolTableCmd = (macho_dysymtab_command

*)&buffer[sizeof(macho_symtab_command

)]; + bzero(dynamicSymbolTableCmd, sizeof(macho_dysymtab_command

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

)); + dynamicSymbolTableCmd->set_ilocalsym(fWriter.fSymbolTableStabsStartIndex); + dynamicSymbolTableCmd->set_nlocalsym(fWriter.fSymbolTableStabsCount + fWriter.fSymbolTableLocalCount); + dynamicSymbolTableCmd->set_iextdefsym(fWriter.fSymbolTableExportStartIndex); + dynamicSymbolTableCmd->set_nextdefsym(fWriter.fSymbolTableExportCount); + dynamicSymbolTableCmd->set_iundefsym(fWriter.fSymbolTableImportStartIndex); + dynamicSymbolTableCmd->set_nundefsym(fWriter.fSymbolTableImportCount); + dynamicSymbolTableCmd->set_indirectsymoff(fWriter.fIndirectTableAtom->getFileOffset()); + dynamicSymbolTableCmd->set_nindirectsyms(fWriter.fIndirectTableAtom->fTable.size()); + if ( fWriter.fOptions.outputKind() != Options::kObjectFile ) { + dynamicSymbolTableCmd->set_extreloff((fWriter.fExternalRelocs.size()==0) ? 0 : fWriter.fExternalRelocationsAtom->getFileOffset()); + dynamicSymbolTableCmd->set_nextrel(fWriter.fExternalRelocs.size()); + dynamicSymbolTableCmd->set_locreloff((fWriter.fInternalRelocs.size()==0) ? 0 : fWriter.fLocalRelocationsAtom->getFileOffset()); + dynamicSymbolTableCmd->set_nlocrel(fWriter.fInternalRelocs.size()); + } + } +} + +template +unsigned int SymbolTableLoadCommandsAtom::commandCount() +{ + return (fWriter.fOptions.outputKind() == Options::kStaticExecutable) ? 1 : 2; +} + +template +uint64_t DyldLoadCommandsAtom::getSize() const +{ + return this->alignedSize(sizeof(macho_dylinker_command

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

* cmd = (macho_dylinker_command

*)buffer; + if ( fWriter.fOptions.outputKind() == Options::kDyld ) + cmd->set_cmd(LC_ID_DYLINKER); + else + cmd->set_cmd(LC_LOAD_DYLINKER); + cmd->set_cmdsize(this->getSize()); + cmd->set_name_offset(); + strcpy((char*)&buffer[sizeof(macho_dylinker_command

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

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

* cmd = (macho_sub_client_command

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

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

) + strlen(path) + 1); +} + +template +void DylibLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + bzero(buffer, size); + const char* path = fInfo.reader->getInstallPath(); + if ( fInfo.options.fInstallPathOverride != NULL ) + path = fInfo.options.fInstallPathOverride; + macho_dylib_command

* cmd = (macho_dylib_command

*)buffer; + if ( fInfo.options.fWeakImport ) + cmd->set_cmd(LC_LOAD_WEAK_DYLIB); + else + cmd->set_cmd(LC_LOAD_DYLIB); + cmd->set_cmdsize(this->getSize()); + cmd->set_timestamp(fInfo.reader->getTimestamp()); + cmd->set_current_version(fInfo.reader->getCurrentVersion()); + cmd->set_compatibility_version(fInfo.reader->getCompatibilityVersion()); + cmd->set_name_offset(); + strcpy((char*)&buffer[sizeof(macho_dylib_command

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

) + strlen(fWriter.fOptions.installPath()) + 1); +} + +template +void DylibIDLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + struct timeval currentTime = { 0 , 0 }; + gettimeofday(¤tTime, NULL); + time_t timestamp = currentTime.tv_sec; + uint64_t size = this->getSize(); + bzero(buffer, size); + macho_dylib_command

* cmd = (macho_dylib_command

*)buffer; + cmd->set_cmd(LC_ID_DYLIB); + cmd->set_cmdsize(this->getSize()); + cmd->set_name_offset(); + cmd->set_timestamp(timestamp); + cmd->set_current_version(fWriter.fOptions.currentVersion()); + cmd->set_compatibility_version(fWriter.fOptions.compatibilityVersion()); + strcpy((char*)&buffer[sizeof(macho_dylib_command

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

)); + macho_routines_command

* cmd = (macho_routines_command

*)buffer; + cmd->set_cmd(macho_routines_command

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

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

* cmd = (macho_sub_umbrella_command

*)buffer; + cmd->set_cmd(LC_SUB_UMBRELLA); + cmd->set_cmdsize(this->getSize()); + cmd->set_sub_umbrella_offset(); + strcpy((char*)&buffer[sizeof(macho_sub_umbrella_command

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

* cmd = (macho_uuid_command

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

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

* cmd = (macho_sub_library_command

*)buffer; + cmd->set_cmd(LC_SUB_LIBRARY); + cmd->set_cmdsize(this->getSize()); + cmd->set_sub_library_offset(); + strncpy((char*)&buffer[sizeof(macho_sub_library_command

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

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

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

* cmd = (macho_sub_framework_command

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

)], fName); +} + +template <> +uint64_t ThreadsLoadCommandsAtom::getSize() const +{ + return this->alignedSize(16 + 40*4); // base size + PPC_THREAD_STATE_COUNT * 4 +} + +template <> +uint64_t ThreadsLoadCommandsAtom::getSize() const +{ + return this->alignedSize(16 + 76*4); // base size + PPC_THREAD_STATE64_COUNT * 4 +} + +template <> +uint64_t ThreadsLoadCommandsAtom::getSize() const +{ + return this->alignedSize(16 + 16*4); // base size + i386_THREAD_STATE_COUNT * 4 +} + + +template <> +void ThreadsLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); + bzero(buffer, size); + macho_thread_command* cmd = (macho_thread_command*)buffer; + cmd->set_cmd(LC_UNIXTHREAD); + cmd->set_cmdsize(size); + cmd->set_flavor(1); // PPC_THREAD_STATE + cmd->set_count(40); // PPC_THREAD_STATE_COUNT; + cmd->set_thread_register(0, start); + if ( fWriter.fOptions.hasCustomStack() ) + cmd->set_thread_register(3, fWriter.fOptions.customStackAddr()); // r1 +} + + +template <> +void ThreadsLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); + bzero(buffer, size); + macho_thread_command* cmd = (macho_thread_command*)buffer; + cmd->set_cmd(LC_UNIXTHREAD); + cmd->set_cmdsize(size); + cmd->set_flavor(5); // PPC_THREAD_STATE64 + cmd->set_count(76); // PPC_THREAD_STATE64_COUNT; + cmd->set_thread_register(0, start); + if ( fWriter.fOptions.hasCustomStack() ) + cmd->set_thread_register(6, fWriter.fOptions.customStackAddr()); // r1 +} + +template <> +void ThreadsLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); + bzero(buffer, size); + macho_thread_command* cmd = (macho_thread_command*)buffer; + cmd->set_cmd(LC_UNIXTHREAD); + cmd->set_cmdsize(size); + cmd->set_flavor(1); // i386_THREAD_STATE + cmd->set_count(16); // i386_THREAD_STATE_COUNT; + cmd->set_thread_register(10, start); + if ( fWriter.fOptions.hasCustomStack() ) + cmd->set_thread_register(15, fWriter.fOptions.customStackAddr()); // uesp +} + + + + +template +void LoadCommandsPaddingAtom::copyRawContent(uint8_t buffer[]) const +{ + bzero(buffer, fSize); +} + +template +void LoadCommandsPaddingAtom::setSize(uint64_t newSize) +{ + fSize = newSize; + // this resizing by-passes the way fLargestAtomSize is set, so re-check here + if ( fWriter.fLargestAtomSize < newSize ) + fWriter.fLargestAtomSize = newSize; +} + +template +uint64_t LinkEditAtom::getFileOffset() const +{ + return ((SectionInfo*)this->getSection())->fFileOffset + this->getSectionOffset(); +} + + +template +uint64_t SectionRelocationsLinkEditAtom::getSize() const +{ + return fWriter.fSectionRelocs.size() * sizeof(macho_relocation_info

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

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

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

); +} + +template +void ExternalRelocationsLinkEditAtom::copyRawContent(uint8_t buffer[]) const +{ + memcpy(buffer, &fWriter.fExternalRelocs[0], this->getSize()); +} + + + +template +uint64_t IndirectTableLinkEditAtom::getSize() const +{ + return fTable.size() * sizeof(uint32_t); +} + +template +void IndirectTableLinkEditAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + bzero(buffer, size); + const uint32_t indirectTableSize = fTable.size(); + uint32_t* indirectTable = (uint32_t*)buffer; + for(std::vector::const_iterator it = fTable.begin(); it != fTable.end(); ++it) { + if ( it->indirectIndex < indirectTableSize ) { + A::P::E::set32(indirectTable[it->indirectIndex], it->symbolIndex); + } + else { + throwf("malformed indirect table. size=%d, index=%d", indirectTableSize, it->indirectIndex); + } + } +} + + + +template +StringsLinkEditAtom::StringsLinkEditAtom(Writer& writer) + : LinkEditAtom(writer), fCurrentBuffer(NULL), fCurrentBufferUsed(0) +{ + fCurrentBuffer = new char[kBufferSize]; + // burn first byte of string pool (so zero is never a valid string offset) + fCurrentBuffer[fCurrentBufferUsed++] = ' '; + // make offset 1 always point to an empty string + fCurrentBuffer[fCurrentBufferUsed++] = '\0'; +} + +template +uint64_t StringsLinkEditAtom::getSize() const +{ + return kBufferSize * fFullBuffers.size() + fCurrentBufferUsed; +} + +template +void StringsLinkEditAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t offset = 0; + for (unsigned int i=0; i < fFullBuffers.size(); ++i) { + memcpy(&buffer[offset], fFullBuffers[i], kBufferSize); + offset += kBufferSize; + } + memcpy(&buffer[offset], fCurrentBuffer, fCurrentBufferUsed); +} + +template +int32_t StringsLinkEditAtom::add(const char* name) +{ + int32_t offset = kBufferSize * fFullBuffers.size() + fCurrentBufferUsed; + int lenNeeded = strlcpy(&fCurrentBuffer[fCurrentBufferUsed], name, kBufferSize-fCurrentBufferUsed)+1; + if ( (fCurrentBufferUsed+lenNeeded) < kBufferSize ) { + fCurrentBufferUsed += lenNeeded; + } + else { + int copied = kBufferSize-fCurrentBufferUsed-1; + // change trailing '\0' that strlcpy added to real char + fCurrentBuffer[kBufferSize-1] = name[copied]; + // alloc next buffer + fFullBuffers.push_back(fCurrentBuffer); + fCurrentBuffer = new char[kBufferSize]; + fCurrentBufferUsed = 0; + // append rest of string + this->add(&name[copied+1]); + } + return offset; +} + + +template +int32_t StringsLinkEditAtom::addUnique(const char* name) +{ + StringToOffset::iterator pos = fUniqueStrings.find(name); + if ( pos != fUniqueStrings.end() ) { + return pos->second; + } + else { + int32_t offset = this->add(name); + fUniqueStrings[name] = offset; + return offset; + } +} + + +template +BranchIslandAtom::BranchIslandAtom(Writer& writer, const char* name, int islandRegion, ObjectFile::Atom& target, uint32_t targetOffset) + : WriterAtom(writer, Segment::fgTextSegment), fTarget(target), fTargetOffset(targetOffset) +{ + char* buf = new char[strlen(name)+32]; + if ( targetOffset == 0 ) { + if ( islandRegion == 0 ) + sprintf(buf, "%s$island", name); + else + sprintf(buf, "%s$island_%d", name, islandRegion); + } + else { + sprintf(buf, "%s_plus_%d$island_%d", name, targetOffset, islandRegion); + } + fName = buf; +} + + +template <> +void BranchIslandAtom::copyRawContent(uint8_t buffer[]) const +{ + int64_t displacement = fTarget.getAddress() + fTargetOffset - this->getAddress(); + int32_t branchInstruction = 0x48000000 | ((uint32_t)displacement & 0x03FFFFFC); + OSWriteBigInt32(buffer, 0, branchInstruction); +} + +template <> +void BranchIslandAtom::copyRawContent(uint8_t buffer[]) const +{ + int64_t displacement = fTarget.getAddress() + fTargetOffset - this->getAddress(); + int32_t branchInstruction = 0x48000000 | ((uint32_t)displacement & 0x03FFFFFC); + OSWriteBigInt32(buffer, 0, branchInstruction); +} + +template <> +uint64_t BranchIslandAtom::getSize() const +{ + return 4; +} + +template <> +uint64_t BranchIslandAtom::getSize() const +{ + return 4; +} + + +template <> +bool StubAtom::pic() const +{ + // no-pic stubs for ppc64 don't work if lazy pointer is above low 2GB. + // This usually only happens when a large zero-page is requested + switch ( fWriter.fOptions.outputKind() ) { + case Options::kDynamicExecutable: + return (fWriter.fOptions.zeroPageSize() > 4096); + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + return true; + case Options::kObjectFile: + case Options::kDyld: + case Options::kStaticExecutable: + break; + } + throw "internal ld64 error: file type does not use stubs"; +} + +template <> +bool StubAtom::pic() const +{ + return ( fWriter.fOptions.outputKind() != Options::kDynamicExecutable ); +} + + +template <> +StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target) + : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target) +{ + writer.fAllSynthesizedStubs.push_back(this); + + LazyPointerAtom* lp = new LazyPointerAtom(writer, target); + if ( pic() ) { + // picbase is 8 bytes into atom + fReferences.push_back(new WriterReference(12, ppc::kPICBaseHigh16, lp, 0, NULL, 8)); + fReferences.push_back(new WriterReference(20, ppc::kPICBaseLow16, lp, 0, NULL, 8)); + } + else { + fReferences.push_back(new WriterReference(0, ppc::kAbsHigh16AddLow, lp)); + fReferences.push_back(new WriterReference(4, ppc::kAbsLow16, lp)); + } +} + +template <> +StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target) + : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target) +{ + writer.fAllSynthesizedStubs.push_back(this); + + LazyPointerAtom* lp = new LazyPointerAtom(writer, target); + if ( pic() ) { + // picbase is 8 bytes into atom + fReferences.push_back(new WriterReference(12, ppc64::kPICBaseHigh16, lp, 0, NULL, 8)); + fReferences.push_back(new WriterReference(20, ppc64::kPICBaseLow14, lp, 0, NULL, 8)); + } + else { + fReferences.push_back(new WriterReference(0, ppc64::kAbsHigh16AddLow, lp)); + fReferences.push_back(new WriterReference(4, ppc64::kAbsLow14, lp)); + } +} + +// specialize to put x86 fast stub in __IMPORT segment with no lazy pointer +template <> +StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target) + : WriterAtom(writer, Segment::fgImportSegment), fName(stubName(target.getName())), fTarget(target) +{ + writer.fAllSynthesizedStubs.push_back(this); +} + + +template +const char* StubAtom::stubName(const char* name) +{ + char* buf; + asprintf(&buf, "%s$stub", name); + return buf; +} + +template <> +uint64_t StubAtom::getSize() const +{ + return ( pic() ? 32 : 16 ); +} + +template <> +uint64_t StubAtom::getSize() const +{ + return ( pic() ? 32 : 16 ); +} + +template <> +uint64_t StubAtom::getSize() const +{ + return 5; +} + + +template <> +uint8_t StubAtom::getAlignment() const +{ + // special case x86 fast stubs to be byte aligned + return 0; +} + +template <> +void StubAtom::copyRawContent(uint8_t buffer[]) const +{ + if ( pic() ) { + OSWriteBigInt32(&buffer [0], 0, 0x7c0802a6); // mflr r0 + OSWriteBigInt32(&buffer[ 4], 0, 0x429f0005); // bcl 20,31,Lpicbase + OSWriteBigInt32(&buffer[ 8], 0, 0x7d6802a6); // Lpicbase: mflr r11 + OSWriteBigInt32(&buffer[12], 0, 0x3d6b0000); // addis r11,r11,ha16(L_fwrite$lazy_ptr-Lpicbase) + OSWriteBigInt32(&buffer[16], 0, 0x7c0803a6); // mtlr r0 + OSWriteBigInt32(&buffer[20], 0, 0xe98b0001); // ldu r12,lo16(L_fwrite$lazy_ptr-Lpicbase)(r11) + OSWriteBigInt32(&buffer[24], 0, 0x7d8903a6); // mtctr r12 + OSWriteBigInt32(&buffer[28], 0, 0x4e800420); // bctr + } + else { + OSWriteBigInt32(&buffer[ 0], 0, 0x3d600000); // lis r11,ha16(L_fwrite$lazy_ptr) + OSWriteBigInt32(&buffer[ 4], 0, 0xe98b0001); // ldu r12,lo16(L_fwrite$lazy_ptr)(r11) + OSWriteBigInt32(&buffer[ 8], 0, 0x7d8903a6); // mtctr r12 + OSWriteBigInt32(&buffer[12], 0, 0x4e800420); // bctr + } +} + +template <> +void StubAtom::copyRawContent(uint8_t buffer[]) const +{ + if ( pic() ) { + OSWriteBigInt32(&buffer[ 0], 0, 0x7c0802a6); // mflr r0 + OSWriteBigInt32(&buffer[ 4], 0, 0x429f0005); // bcl 20,31,Lpicbase + OSWriteBigInt32(&buffer[ 8], 0, 0x7d6802a6); // Lpicbase: mflr r11 + OSWriteBigInt32(&buffer[12], 0, 0x3d6b0000); // addis r11,r11,ha16(L_fwrite$lazy_ptr-Lpicbase) + OSWriteBigInt32(&buffer[16], 0, 0x7c0803a6); // mtlr r0 + OSWriteBigInt32(&buffer[20], 0, 0x858b0000); // lwzu r12,lo16(L_fwrite$lazy_ptr-Lpicbase)(r11) + OSWriteBigInt32(&buffer[24], 0, 0x7d8903a6); // mtctr r12 + OSWriteBigInt32(&buffer[28], 0, 0x4e800420); // bctr + } + else { + OSWriteBigInt32(&buffer[ 0], 0, 0x3d600000); // lis r11,ha16(L_fwrite$lazy_ptr) + OSWriteBigInt32(&buffer[ 4], 0, 0x858b0000); // lwzu r12,lo16(L_fwrite$lazy_ptr)(r11) + OSWriteBigInt32(&buffer[ 8], 0, 0x7d8903a6); // mtctr r12 + OSWriteBigInt32(&buffer[12], 0, 0x4e800420); // bctr + } +} + +template <> +void StubAtom::copyRawContent(uint8_t buffer[]) const +{ + buffer[0] = 0xF4; + buffer[1] = 0xF4; + buffer[2] = 0xF4; + buffer[3] = 0xF4; + buffer[4] = 0xF4; +} + + +template <> +const char* StubAtom::getSectionName() const +{ + return ( pic() ? "__picsymbolstub1" : "__symbol_stub1"); +} + +template <> +const char* StubAtom::getSectionName() const +{ + return ( pic() ? "__picsymbolstub1" : "__symbol_stub1"); +} + +template <> +const char* StubAtom::getSectionName() const +{ + return "__jump_table"; +} + + + + + +template +LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Atom& target) + : WriterAtom(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target) +{ + writer.fAllSynthesizedLazyPointers.push_back(this); + + fReferences.push_back(new WriterReference(0, A::kPointer, &target)); +} + + + +template +const char* LazyPointerAtom::lazyPointerName(const char* name) +{ + char* buf; + asprintf(&buf, "%s$lazy_pointer", name); + return buf; +} + +template +void LazyPointerAtom::copyRawContent(uint8_t buffer[]) const +{ + bzero(buffer, getSize()); +} + + +template +NonLazyPointerAtom::NonLazyPointerAtom(Writer& writer, ObjectFile::Atom& target) + : WriterAtom(writer, Segment::fgDataSegment), fName(nonlazyPointerName(target.getName())), fTarget(target) +{ + writer.fAllSynthesizedNonLazyPointers.push_back(this); + + fReferences.push_back(new WriterReference(0, A::kPointer, &target)); +} + +template +const char* NonLazyPointerAtom::nonlazyPointerName(const char* name) +{ + char* buf; + asprintf(&buf, "%s$non_lazy_pointer", name); + return buf; +} + +template +void NonLazyPointerAtom::copyRawContent(uint8_t buffer[]) const +{ + bzero(buffer, getSize()); +} + + + +}; // namespace executable +}; // namespace mach_o + + +#endif // __EXECUTABLE_MACH_O__ diff --git a/src/ObjDump.cpp b/src/ObjDump.cpp deleted file mode 100644 index e3d6082..0000000 --- a/src/ObjDump.cpp +++ /dev/null @@ -1,331 +0,0 @@ -/* - * 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@ - */ -#include -#include -#include -#include -#include -#include -#include -#include - - -#include "ObjectFile.h" -#include "ObjectFileMachO-all.h" - - __attribute__((noreturn)) -void throwf(const char* format, ...) -{ - va_list list; - char* p; - va_start(list, format); - vasprintf(&p, format, list); - va_end(list); - - const char* t = p; - throw t; -} - -static void dumpStabs(std::vector* stabs) -{ - // debug info - const int stabCount = stabs->size(); - printf("stabs: (%u)\n", stabCount); - for (int i=0; i < stabCount; ++i) { - ObjectFile::StabsInfo& stab = (*stabs)[i]; - const char* code = "?????"; - switch (stab.type) { - case N_GSYM: - code = " GSYM"; - break; - case N_FNAME: - code = "FNAME"; - break; - case N_FUN: - code = " FUN"; - break; - case N_STSYM: - code = "STSYM"; - break; - case N_LCSYM: - code = "LCSYM"; - break; - case N_BNSYM: - code = "BNSYM"; - break; - case N_OPT: - code = " OPT"; - break; - case N_RSYM: - code = " RSYM"; - break; - case N_SLINE: - code = "SLINE"; - break; - case N_ENSYM: - code = "ENSYM"; - break; - case N_SSYM: - code = " SSYM"; - break; - case N_SO: - code = " SO"; - break; - case N_LSYM: - code = " LSYM"; - break; - case N_BINCL: - code = "BINCL"; - break; - case N_SOL: - code = " SOL"; - break; - case N_PARAMS: - code = "PARMS"; - break; - case N_VERSION: - code = " VERS"; - break; - case N_OLEVEL: - code = "OLEVL"; - break; - case N_PSYM: - code = " PSYM"; - break; - case N_EINCL: - code = "EINCL"; - break; - case N_ENTRY: - code = "ENTRY"; - break; - case N_LBRAC: - code = "LBRAC"; - break; - case N_EXCL: - code = " EXCL"; - break; - case N_RBRAC: - code = "RBRAC"; - break; - case N_BCOMM: - code = "BCOMM"; - break; - case N_ECOMM: - code = "ECOMM"; - break; - case N_LENG: - code = "LENG"; - break; - } - printf(" %08X %02X %04X %s %s\n", (uint32_t)stab.atomOffset, stab.other, stab.desc, code, stab.string); - } -} - - -static void dumpAtom(ObjectFile::Atom* atom) -{ - //printf("atom: %p\n", atom); - - // name - printf("name: %s\n", atom->getDisplayName()); - - // scope - switch ( atom->getScope() ) { - case ObjectFile::Atom::scopeTranslationUnit: - printf("scope: translation unit\n"); - break; - case ObjectFile::Atom::scopeLinkageUnit: - printf("scope: linkage unit\n"); - break; - case ObjectFile::Atom::scopeGlobal: - printf("scope: global\n"); - break; - default: - printf("scope: unknown\n"); - } - - // segment and section - printf("section: %s,%s\n", atom->getSegment().getName(), atom->getSectionName()); - - // attributes - printf("attrs: "); - if ( atom->isWeakDefinition() ) - printf("weak "); - if ( atom->isCoalesableByName() ) - printf("coalesce-by-name "); - if ( atom->isCoalesableByValue() ) - printf("coalesce-by-value "); - if ( atom->dontDeadStrip() ) - printf("dont-dead-strip "); - if ( atom->isZeroFill() ) - printf("zero-fill "); - printf("\n"); - - // size - printf("size: 0x%012llX\n", atom->getSize()); - - // alignment - printf("align: %d\n", atom->getAlignment()); - - // content - uint64_t size = atom->getSize(); - if ( size < 4096 ) { - uint8_t content[size]; - atom->copyRawContent(content); - printf("content: "); - if ( strcmp(atom->getSectionName(), "__cstring") == 0 ) { - printf("\"%s\"", content); - } - else { - for (unsigned int i=0; i < size; ++i) - printf("%02X ", content[i]); - } - } - printf("\n"); - - // references - std::vector& references = atom->getReferences(); - const int refCount = references.size(); - printf("references: (%u)\n", refCount); - for (int i=0; i < refCount; ++i) { - ObjectFile::Reference* ref = references[i]; - printf(" %s\n", ref->getDescription()); - } - - // debug info - std::vector* stabs = atom->getStabsDebugInfo(); - if ( stabs != NULL ) - dumpStabs(stabs); -} - - -static void dumpFile(ObjectFile::Reader* reader) -{ -#if 0 - // debug info - std::vector* stabs = reader->getStabsDebugInfo(); - if ( stabs != NULL ) - dumpStabs(stabs); -#endif - // atom content - std::vector atoms = reader->getAtoms(); - const int atomCount = atoms.size(); - for(int i=0; i < atomCount; ++i) { - dumpAtom(atoms[i]); - printf("\n"); - } -} - - -static ObjectFile::Reader* createReader(const char* path, const ObjectFile::ReaderOptions& options) -{ - struct stat stat_buf; - - int fd = ::open(path, O_RDONLY, 0); - if ( fd == -1 ) - throw "cannot open file"; - ::fstat(fd, &stat_buf); - char* p = (char*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE, fd, 0); - ::close(fd); - const mach_header* mh = (mach_header*)p; - if ( mh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { - const struct fat_header* fh = (struct fat_header*)p; - const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); - for (unsigned long i=0; i < fh->nfat_arch; ++i) { - if ( archs[i].cputype == CPU_TYPE_POWERPC64 ) { - p = p + archs[i].offset; - mh = (struct mach_header*)p; - } - } - } - if ( mh->magic == MH_MAGIC ) { - if ( mh->filetype == MH_OBJECT ) { - switch ( mh->cputype ) { - case CPU_TYPE_I386: - return i386::ObjectFileMachO::MakeReader((class i386::macho_header*)mh, path, options); - case CPU_TYPE_POWERPC: - return ppc::ObjectFileMachO::MakeReader((class ppc::macho_header*)mh, path, options); - case CPU_TYPE_POWERPC64: - return ppc64::ObjectFileMachO::MakeReader((class ppc64::macho_header*)mh, path, options); - default: - throw "unknown mach-o cpu type"; - } - } - if ( mh->filetype == MH_DYLIB ) - return ppc::ObjectFileDylibMachO::MakeReader((class ppc::macho_header*)mh, path, options); - throw "unknown mach-o file type"; - } - else if ( mh->magic == MH_MAGIC_64 ) { - if ( mh->filetype == MH_OBJECT ) - return ppc64::ObjectFileMachO::MakeReader((class ppc64::macho_header*)mh, path, options); - if ( mh->filetype == MH_DYLIB ) - return ppc64::ObjectFileDylibMachO::MakeReader((class ppc64::macho_header*)mh, path, options); - throw "unknown mach-o file type"; - } - else if ( mh->magic == OSSwapInt32(MH_MAGIC) ) { - if ( mh->filetype == OSSwapInt32(MH_OBJECT) ) { - switch ( OSSwapInt32(mh->cputype) ) { - case CPU_TYPE_I386: - return i386::ObjectFileMachO::MakeReader((class i386::macho_header*)mh, path, options); - case CPU_TYPE_POWERPC: - return ppc::ObjectFileMachO::MakeReader((class ppc::macho_header*)mh, path, options); - case CPU_TYPE_POWERPC64: - return ppc64::ObjectFileMachO::MakeReader((class ppc64::macho_header*)mh, path, options); - default: - throw "unknown mach-o cpu type"; - } - } - if ( mh->filetype == OSSwapInt32(MH_DYLIB) ) - return ppc::ObjectFileDylibMachO::MakeReader((class ppc::macho_header*)mh, path, options); - throw "unknown mach-o file type"; - } - else if ( mh->magic == OSSwapInt32(MH_MAGIC_64) ) { - if ( mh->filetype == OSSwapInt32(MH_OBJECT) ) - return ppc64::ObjectFileMachO::MakeReader((class ppc64::macho_header*)mh, path, options); - if ( mh->filetype == OSSwapInt32(MH_DYLIB) ) - return ppc64::ObjectFileDylibMachO::MakeReader((class ppc64::macho_header*)mh, path, options); - throw "unknown mach-o file type"; - } - throw "unknown file type"; -} - - -int main(int argc, const char* argv[]) -{ - ObjectFile::ReaderOptions options; - //const char* path = argv[1]; - //ObjectFile::Reader* reader = ObjectFile::Reader::createReader(path); - try { - ObjectFile::Reader* reader = createReader("/tmp/gcov-1.o", options); - - dumpFile(reader); - } - catch (const char* msg) { - fprintf(stderr, "ObjDump failed: %s\n", msg); - } - - return 0; -} - - - diff --git a/src/ObjectDump.cpp b/src/ObjectDump.cpp new file mode 100644 index 0000000..7c51e0a --- /dev/null +++ b/src/ObjectDump.cpp @@ -0,0 +1,361 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "MachOReaderRelocatable.hpp" + +static bool sDumpContent= true; +static bool sDumpStabs = false; +static bool sSort = true; +static cpu_type_t sPreferredArch = CPU_TYPE_POWERPC64; + + + __attribute__((noreturn)) +void throwf(const char* format, ...) +{ + va_list list; + char* p; + va_start(list, format); + vasprintf(&p, format, list); + va_end(list); + + const char* t = p; + throw t; +} + +static void dumpStabs(std::vector* stabs) +{ + // debug info + printf("stabs: (%lu)\n", stabs->size()); + for (std::vector::iterator it = stabs->begin(); it != stabs->end(); ++it ) { + ObjectFile::Reader::Stab& stab = *it; + const char* code = "?????"; + switch (stab.type) { + case N_GSYM: + code = " GSYM"; + break; + case N_FNAME: + code = "FNAME"; + break; + case N_FUN: + code = " FUN"; + break; + case N_STSYM: + code = "STSYM"; + break; + case N_LCSYM: + code = "LCSYM"; + break; + case N_BNSYM: + code = "BNSYM"; + break; + case N_OPT: + code = " OPT"; + break; + case N_RSYM: + code = " RSYM"; + break; + case N_SLINE: + code = "SLINE"; + break; + case N_ENSYM: + code = "ENSYM"; + break; + case N_SSYM: + code = " SSYM"; + break; + case N_SO: + code = " SO"; + break; + case N_OSO: + code = " OSO"; + break; + case N_LSYM: + code = " LSYM"; + break; + case N_BINCL: + code = "BINCL"; + break; + case N_SOL: + code = " SOL"; + break; + case N_PARAMS: + code = "PARMS"; + break; + case N_VERSION: + code = " VERS"; + break; + case N_OLEVEL: + code = "OLEVL"; + break; + case N_PSYM: + code = " PSYM"; + break; + case N_EINCL: + code = "EINCL"; + break; + case N_ENTRY: + code = "ENTRY"; + break; + case N_LBRAC: + code = "LBRAC"; + break; + case N_EXCL: + code = " EXCL"; + break; + case N_RBRAC: + code = "RBRAC"; + break; + case N_BCOMM: + code = "BCOMM"; + break; + case N_ECOMM: + code = "ECOMM"; + break; + case N_LENG: + code = "LENG"; + break; + } + printf(" [atom=%20s] %02X %04X %s %s\n", ((stab.atom != NULL) ? stab.atom->getDisplayName() : ""), stab.other, stab.desc, code, stab.string); + } +} + + +static void dumpAtom(ObjectFile::Atom* atom) +{ + //printf("atom: %p\n", atom); + + // name + printf("name: %s\n", atom->getDisplayName()); + + // scope + switch ( atom->getScope() ) { + case ObjectFile::Atom::scopeTranslationUnit: + printf("scope: translation unit\n"); + break; + case ObjectFile::Atom::scopeLinkageUnit: + printf("scope: linkage unit\n"); + break; + case ObjectFile::Atom::scopeGlobal: + printf("scope: global\n"); + break; + default: + printf("scope: unknown\n"); + } + + // kind + switch ( atom->getDefinitionKind() ) { + case ObjectFile::Atom::kRegularDefinition: + printf("kind: regular\n"); + break; + case ObjectFile::Atom::kWeakDefinition: + printf("kind: weak\n"); + break; + case ObjectFile::Atom::kTentativeDefinition: + printf("kind: tentative\n"); + break; + case ObjectFile::Atom::kExternalDefinition: + printf("kind: import\n"); + break; + case ObjectFile::Atom::kExternalWeakDefinition: + printf("kind: weak import\n"); + break; + default: + printf("scope: unknown\n"); + } + + // segment and section + printf("section: %s,%s\n", atom->getSegment().getName(), atom->getSectionName()); + + // attributes + printf("attrs: "); + if ( atom->dontDeadStrip() ) + printf("dont-dead-strip "); + if ( atom->isZeroFill() ) + printf("zero-fill "); + printf("\n"); + + // size + printf("size: 0x%012llX\n", atom->getSize()); + + // alignment + printf("align: %d\n", atom->getAlignment()); + + // content + if ( sDumpContent ) { + uint64_t size = atom->getSize(); + if ( size < 4096 ) { + uint8_t content[size]; + atom->copyRawContent(content); + printf("content: "); + if ( strcmp(atom->getSectionName(), "__cstring") == 0 ) { + printf("\"%s\"", content); + } + else { + for (unsigned int i=0; i < size; ++i) + printf("%02X ", content[i]); + } + } + printf("\n"); + } + + // references + std::vector& references = atom->getReferences(); + const int refCount = references.size(); + printf("references: (%u)\n", refCount); + for (int i=0; i < refCount; ++i) { + ObjectFile::Reference* ref = references[i]; + printf(" %s\n", ref->getDescription()); + } + + // line info + std::vector* lineInfo = atom->getLineInfo(); + if ( (lineInfo != NULL) && (lineInfo->size() > 0) ) { + printf("line info: (%lu)\n", lineInfo->size()); + for (std::vector::iterator it = lineInfo->begin(); it != lineInfo->end(); ++it) { + printf(" offset 0x%04X, line %d, file %s\n", it->atomOffset, it->lineNumber, it->fileName); + } + } + +} + +struct AtomSorter +{ + bool operator()(ObjectFile::Atom* left, ObjectFile::Atom* right) + { + return (strcmp(left->getDisplayName(), right->getDisplayName()) < 0); + } +}; + + +static void dumpFile(ObjectFile::Reader* reader) +{ + // stabs debug info + if ( sDumpStabs && (reader->getDebugInfoKind() == ObjectFile::Reader::kDebugInfoStabs) ) { + std::vector* stabs = reader->getStabs(); + if ( stabs != NULL ) + dumpStabs(stabs); + } + + // get all atoms + std::vector atoms = reader->getAtoms(); + + // make copy of vector and sort (so output is canonical) + std::vector sortedAtoms(atoms); + if ( sSort ) + std::sort(sortedAtoms.begin(), sortedAtoms.end(), AtomSorter()); + + for(std::vector::iterator it=sortedAtoms.begin(); it != sortedAtoms.end(); ++it) { + dumpAtom(*it); + printf("\n"); + } +} + + +static ObjectFile::Reader* createReader(const char* path, const ObjectFile::ReaderOptions& options) +{ + struct stat stat_buf; + + int fd = ::open(path, O_RDONLY, 0); + if ( fd == -1 ) + throwf("cannot open file: %s", path); + ::fstat(fd, &stat_buf); + uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE, fd, 0); + ::close(fd); + const mach_header* mh = (mach_header*)p; + if ( mh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + const struct fat_header* fh = (struct fat_header*)p; + const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); + for (unsigned long i=0; i < fh->nfat_arch; ++i) { + if ( archs[i].cputype == sPreferredArch ) { + p = p + archs[i].offset; + mh = (struct mach_header*)p; + } + } + } + if ( mach_o::relocatable::Reader::validFile(p) ) + return mach_o::relocatable::Reader::make(p, path, 0, options); + else if ( mach_o::relocatable::Reader::validFile(p) ) + return mach_o::relocatable::Reader::make(p, path, 0, options); + else if ( mach_o::relocatable::Reader::validFile(p) ) + return mach_o::relocatable::Reader::make(p, path, 0, options); + throwf("not a mach-o object file: %s", path); +} + + +int main(int argc, const char* argv[]) +{ + ObjectFile::ReaderOptions options; + try { + for(int i=1; i < argc; ++i) { + const char* arg = argv[i]; + if ( arg[0] == '-' ) { + if ( strcmp(arg, "-no_content") == 0 ) { + sDumpContent = false; + } + else if ( strcmp(arg, "-stabs") == 0 ) { + sDumpStabs = true; + } + else if ( strcmp(arg, "-no_sort") == 0 ) { + sSort = false; + } + else if ( strcmp(arg, "-arch") == 0 ) { + const char* arch = argv[++i]; + if ( strcmp(arch, "ppc64") == 0 ) + sPreferredArch = CPU_TYPE_POWERPC64; + else if ( strcmp(arch, "ppc") == 0 ) + sPreferredArch = CPU_TYPE_POWERPC; + else if ( strcmp(arch, "i386") == 0 ) + sPreferredArch = CPU_TYPE_I386; + else + throwf("unknown architecture %s", arch); + } + else { + throwf("unknown option: %s\n", arg); + } + } + else { + ObjectFile::Reader* reader = createReader(arg, options); + dumpFile(reader); + } + } + } + catch (const char* msg) { + fprintf(stderr, "ObjDump failed: %s\n", msg); + return 1; + } + + return 0; +} + + + diff --git a/src/ObjectFile.h b/src/ObjectFile.h index 255c028..0be4910 100644 --- a/src/ObjectFile.h +++ b/src/ObjectFile.h @@ -1,16 +1,16 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ - * + * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. - * + * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -18,7 +18,7 @@ * 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@ */ @@ -32,43 +32,73 @@ +// +// These classes represent the abstract Atoms and References that are the basis of the linker. +// An Atom and a Reference correspond to a Node and Edge in graph theory. +// +// A Reader is a class which parses an object file and presents it as Atoms and References. +// All linking operations are done on Atoms and References. This makes the linker file +// format independent. +// +// A Writer takes a vector of Atoms with all References resolved and produces an executable file. +// +// + + namespace ObjectFile { -struct StabsInfo + +struct LineInfo { - uint64_t atomOffset; - const char* string; - uint8_t type; - uint8_t other; - uint16_t desc; + uint32_t atomOffset; + const char* fileName; + uint32_t lineNumber; }; + class ReaderOptions { public: - ReaderOptions() : fFullyLoadArchives(false), fLoadObjcClassesInArchives(false), fFlatNamespace(false), - fStripDebugInfo(false), fTraceDylibs(false), fTraceIndirectDylibs(false), fTraceArchives(false) {} + ReaderOptions() : fFullyLoadArchives(false), fLoadObjcClassesInArchives(false), fFlatNamespace(false), + fDebugInfoStripping(kDebugInfoFull), fTraceDylibs(false), fTraceIndirectDylibs(false), fTraceArchives(false), fTraceOutputFile(NULL) {} + enum DebugInfoStripping { kDebugInfoNone, kDebugInfoMinimal, kDebugInfoFull }; bool fFullyLoadArchives; bool fLoadObjcClassesInArchives; bool fFlatNamespace; - bool fStripDebugInfo; + DebugInfoStripping fDebugInfoStripping; bool fTraceDylibs; bool fTraceIndirectDylibs; bool fTraceArchives; + const char* fTraceOutputFile; }; class Reader { public: + enum DebugInfoKind { kDebugInfoNone=0, kDebugInfoStabs=1, kDebugInfoDwarf=2, kDebugInfoStabsUUID=3 }; + struct Stab + { + class Atom* atom; + uint8_t type; + uint8_t other; + uint16_t desc; + uint32_t value; + const char* string; + }; + static Reader* createReader(const char* path, const ReaderOptions& options); - + virtual const char* getPath() = 0; + virtual time_t getModificationTime() = 0; + virtual DebugInfoKind getDebugInfoKind() = 0; virtual std::vector& getAtoms() = 0; virtual std::vector* getJustInTimeAtomsFor(const char* name) = 0; - virtual std::vector* getStabsDebugInfo() = 0; - + virtual std::vector* getStabs() = 0; + unsigned int getSortOrder() const { return fSortOrder; } + void setSortOrder(unsigned int order) { fSortOrder=order; } + // For Dynamic Libraries only virtual const char* getInstallPath() { return NULL; } virtual uint32_t getTimestamp() { return 0; } @@ -76,13 +106,14 @@ public: virtual uint32_t getCompatibilityVersion() { return 0; } virtual std::vector* getDependentLibraryPaths() { return NULL; } virtual bool reExports(Reader*) { return false; } - virtual bool isDefinitionWeak(const Atom&){ return false; } - - - + virtual const char* parentUmbrella() { return NULL; } + virtual std::vector* getAllowableClients() { return NULL; } + protected: - Reader() {} + Reader() : fSortOrder(0) {} virtual ~Reader() {} + + unsigned int fSortOrder; }; class Segment @@ -92,7 +123,7 @@ public: virtual bool isContentReadable() const = 0; virtual bool isContentWritable() const = 0; virtual bool isContentExecutable() const = 0; - + uint64_t getBaseAddress() const { return fBaseAddress; } void setBaseAddress(uint64_t addr) { fBaseAddress = addr; } virtual bool hasFixedAddress() const { return false; } @@ -105,14 +136,14 @@ protected: class Reference; -class Section +class Section { public: unsigned int getIndex() { return fIndex; } uint64_t getBaseAddress() { return fBaseAddress; } void setBaseAddress(uint64_t addr) { fBaseAddress = addr; } void* fOther; - + protected: Section() : fOther(NULL), fBaseAddress(0), fIndex(0) {} uint64_t fBaseAddress; @@ -120,33 +151,55 @@ protected: }; -class ContentWriter -{ -public: - virtual void write(uint64_t atomOffset, const void* buffer, uint64_t size) = 0; -protected: - ContentWriter() {} - virtual ~ContentWriter() {} -}; -class Atom +// +// An atom is the fundamental unit of linking. A C function or global variable is an atom. +// An atom has content and some attributes. The content of a function atom is the instructions +// that implement the function. The content of a global variable atom is its initial bits. +// +// Name: +// The name of an atom is the label name generated by the compiler. A C compiler names foo() +// as _foo. A C++ compiler names foo() as __Z3foov. +// The name refers to the first byte of the content. An atom cannot have multiple entry points. +// Such code is modeled as multiple atoms, each having a "follow on" reference to the next. +// A "follow on" reference is a contraint to the linker to the atoms must be laid out contiguously. +// +// Scope: +// An atom is in one of three scopes: translation-unit, linkage-unit, or global. These correspond +// to the C visibility of static, hidden, default. +// +// DefinitionKind: +// An atom is one of five defintion kinds: +// regular Most atoms. +// weak C++ compiler makes some functions weak if there might be multiple copies +// that the linker needs to coalesce. +// tentative A straggler from ancient C when the extern did not exist. "int foo;" is ambiguous. +// It could be a prototype or it could be a definition. +// external This is a "proxy" atom produced by a dylib reader. It has no content. It exists +// so that all References can be resolved. +// external-weak Same as external, but the definition in the dylib is weak. +// +// SymbolTableInclusion: +// An atom may or may not be in the symbol table in an object file. +// in Most atoms for functions or global data +// not-in Anonymous atoms such literal c-strings, or other compiler generated data +// in-never-strip Atom whose name the strip tool should never remove (e.g. REFERENCED_DYNAMICALLY in mach-o) +// +class Atom { public: enum Scope { scopeTranslationUnit, scopeLinkageUnit, scopeGlobal }; - enum WeakImportSetting { kWeakUnset, kWeakImport, kNonWeakImport }; - + enum DefinitionKind { kRegularDefinition, kWeakDefinition, kTentativeDefinition, kExternalDefinition, kExternalWeakDefinition }; + enum SymbolTableInclusion { kSymbolTableNotIn, kSymbolTableIn, kSymbolTableInAndNeverStrip }; + virtual Reader* getFile() const = 0; + virtual bool getTranslationUnitSource(const char** dir, const char** name) const = 0; virtual const char* getName() const = 0; virtual const char* getDisplayName() const = 0; virtual Scope getScope() const = 0; - virtual bool isTentativeDefinition() const = 0; - virtual bool isWeakDefinition() const = 0; - virtual bool isCoalesableByName() const = 0; - virtual bool isCoalesableByValue() const = 0; + virtual DefinitionKind getDefinitionKind() const = 0; + virtual SymbolTableInclusion getSymbolTableInclusion() const = 0; virtual bool isZeroFill() const = 0; - virtual bool dontDeadStrip() const = 0; - virtual bool dontStripName() const = 0; // referenced dynamically - virtual bool isImportProxy() const = 0; virtual uint64_t getSize() const = 0; virtual std::vector& getReferences() const = 0; virtual bool mustRemainInSection() const = 0; @@ -154,34 +207,34 @@ public: virtual Segment& getSegment() const = 0; virtual bool requiresFollowOnAtom() const = 0; virtual Atom& getFollowOnAtom() const = 0; - virtual std::vector* getStabsDebugInfo() const = 0; + virtual std::vector* getLineInfo() const = 0; virtual uint8_t getAlignment() const = 0; - virtual WeakImportSetting getImportWeakness() const = 0; virtual void copyRawContent(uint8_t buffer[]) const = 0; - virtual void writeContent(bool finalLinkedImage, ContentWriter&) const = 0; virtual void setScope(Scope) = 0; - virtual void setImportWeakness(bool weakImport) = 0; - + uint64_t getSectionOffset() const { return fSectionOffset; } uint64_t getSegmentOffset() const { return fSegmentOffset; } uint64_t getAddress() const { return fSection->getBaseAddress() + fSectionOffset; } unsigned int getSortOrder() const { return fSortOrder; } class Section* getSection() const { return fSection; } + bool dontDeadStrip() const { return fDontDeadStrip; } void setSegmentOffset(uint64_t offset) { fSegmentOffset = offset; } void setSectionOffset(uint64_t offset) { fSectionOffset = offset; } - void setSection(class Section* sect) { fSection = sect; } + void setSection(class Section* sect) { fSection = sect; } unsigned int setSortOrder(unsigned int order); // recursively sets follow-on atoms + void setDontDeadStrip() { fDontDeadStrip = true; } protected: - Atom() : fSegmentOffset(0), fSectionOffset(0), fSortOrder(0), fSection(NULL) {} + Atom() : fSegmentOffset(0), fSectionOffset(0), fSortOrder(0), fSection(NULL), fDontDeadStrip(false) {} virtual ~Atom() {} - + uint64_t fSegmentOffset; uint64_t fSectionOffset; unsigned int fSortOrder; class Section* fSection; + bool fDontDeadStrip; }; @@ -201,20 +254,35 @@ inline unsigned int Atom::setSortOrder(unsigned int order) +// +// A Reference is a directed edge to another Atom. When an instruction in +// the content of an Atom refers to another Atom, that is represented by a +// Reference. +// +// There are two kinds of references: direct and by-name. With a direct Reference, +// the target is bound by the Reader that created it. For instance a reference to a +// static would produce a direct reference. A by-name reference requires the linker +// to find the target Atom with the required name in order to be bound. +// +// For a link to succeed all References must be bound. +// +// A Reference has an optional "from" target. This is used when the content to fix-up +// is the difference of two Atom address. For instance, if a pointer sized data Atom +// is to contain A - B, then the Atom would have on Reference with a target of "A" and +// a from-target of "B". +// +// A Reference also has a fix-up-offset. This is the offset into the content of the +// Atom holding the reference where the fix-up (relocation) will be applied. +// +// +// class Reference { public: - enum Kind { noFixUp, pointer, ppcFixupBranch24, ppcFixupBranch14, - ppcFixupPicBaseLow16, ppcFixupPicBaseLow14, ppcFixupPicBaseHigh16, - ppcFixupAbsLow16, ppcFixupAbsLow14, ppcFixupAbsHigh16, ppcFixupAbsHigh16AddLow, - pointer32Difference, pointer64Difference, x86FixupBranch32 }; virtual bool isTargetUnbound() const = 0; virtual bool isFromTargetUnbound() const = 0; - virtual bool requiresRuntimeFixUp(bool slideable) const = 0; - virtual bool isWeakReference() const = 0; - virtual bool isLazyReference() const = 0; - virtual Kind getKind() const = 0; + virtual uint8_t getKind() const = 0; virtual uint64_t getFixUpOffset() const = 0; virtual const char* getTargetName() const = 0; virtual Atom& getTarget() const = 0; @@ -227,7 +295,7 @@ public: virtual void setTarget(Atom&, uint64_t offset) = 0; virtual void setFromTarget(Atom&) = 0; virtual const char* getDescription() const = 0; - + protected: Reference() {} virtual ~Reference() {} @@ -238,9 +306,3 @@ protected: #endif // __OBJECTFILE__ - - - - - - diff --git a/src/Options.cpp b/src/Options.cpp index 350612b..664bc41 100644 --- a/src/Options.cpp +++ b/src/Options.cpp @@ -1,16 +1,16 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * * 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, @@ -18,43 +18,47 @@ * 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@ */ #include #include +#include #include #include #include "Options.h" - __attribute__((noreturn)) -void throwf(const char* format, ...) +void throwf(const char* format, ...) { va_list list; char* p; va_start(list, format); vasprintf(&p, format, list); va_end(list); - + const char* t = p; throw t; } - Options::Options(int argc, const char* argv[]) - : fOutputFile("a.out"), fArchitecture(CPU_TYPE_POWERPC64), fOutputKind(kDynamicExecutable), fBindAtLoad(false), - fStripLocalSymbols(false), fKeepPrivateExterns(false), - fInterposable(false), fIgnoreOtherArchFiles(false), fForceSubtypeAll(false), fNameSpace(kTwoLevelNameSpace), - fDylibCompatVersion(0), fDylibCurrentVersion(0), fDylibInstallName(NULL), fEntryName("start"), fBaseAddress(0), - fExportMode(kExportDefault), fLibrarySearchMode(kSearchAllDirsForDylibsThenAllDirsForArchives), - fUndefinedTreatment(kUndefinedError), fMessagesPrefixedWithArchitecture(false), fPICTreatment(kPICError), - fWeakReferenceMismatchTreatment(kWeakReferenceMismatchError), - fUmbrellaName(NULL), fInitFunctionName(NULL), fZeroPageSize(0x1000), fStackSize(0), fStackAddr(0), fMinimumHeaderPad(0), - fCommonsMode(kCommonsIgnoreDylibs), fWarnCommons(false), fVerbose(false) + : fOutputFile("a.out"), fArchitecture(0), fOutputKind(kDynamicExecutable), fBindAtLoad(false), + fStripLocalSymbols(false), fKeepPrivateExterns(false), + fInterposable(false), fIgnoreOtherArchFiles(false), fForceSubtypeAll(false), fDeadStrip(kDeadStripOff), + fVersionMin(k10_1),fNameSpace(kTwoLevelNameSpace), + fDylibCompatVersion(0), fDylibCurrentVersion(0), fDylibInstallName(NULL), fEntryName("start"), fBaseAddress(0), + fExportMode(kExportDefault), fLibrarySearchMode(kSearchAllDirsForDylibsThenAllDirsForArchives), + fUndefinedTreatment(kUndefinedError), fMessagesPrefixedWithArchitecture(false), fPICTreatment(kError), + fWeakReferenceMismatchTreatment(kWeakReferenceMismatchError), fMultiplyDefinedDynamic(kWarning), + fMultiplyDefinedUnused(kSuppress), fWarnOnMultiplyDefined(false), fClientName(NULL), + fUmbrellaName(NULL), fInitFunctionName(NULL), fDotOutputFile(NULL), fExecutablePath(NULL), + fZeroPageSize(0x1000), fStackSize(0), fStackAddr(0), fExecutableStack(false), fMinimumHeaderPad(0), + fCommonsMode(kCommonsIgnoreDylibs), fWarnCommons(false), fVerbose(false), fKeepRelocations(false), + fEmitUUID(true),fWarnStabs(false), + fTraceDylibSearching(false), fPause(false), fStatistics(false), fPrintOptions(false) { this->parsePreCommandLineEnvironmentSettings(); this->parse(argc, argv); @@ -66,8 +70,7 @@ Options::~Options() { } - -ObjectFile::ReaderOptions& Options::readerOptions() +const ObjectFile::ReaderOptions& Options::readerOptions() { return fReaderOptions; } @@ -77,13 +80,11 @@ cpu_type_t Options::architecture() return fArchitecture; } - const char* Options::getOutputFilePath() { return fOutputFile; } - std::vector& Options::getInputFiles() { return fInputFiles; @@ -99,14 +100,14 @@ bool Options::stripLocalSymbols() return fStripLocalSymbols; } -bool Options::stripDebugInfo() +bool Options::bindAtLoad() { - return fReaderOptions.fStripDebugInfo; + return fBindAtLoad; } -bool Options::bindAtLoad() +bool Options::prebind() { - return fBindAtLoad; + return fPrebind; } bool Options::fullyLoadArchives() @@ -121,7 +122,7 @@ Options::NameSpace Options::nameSpace() const char* Options::installPath() { - if ( fDylibInstallName != NULL ) + if ( fDylibInstallName != NULL ) return fDylibInstallName; else return fOutputFile; @@ -182,16 +183,46 @@ Options::UndefinedTreatment Options::undefinedTreatment() return fUndefinedTreatment; } +Options::VersionMin Options::macosxVersionMin() +{ + return fVersionMin; +} + Options::WeakReferenceMismatchTreatment Options::weakReferenceMismatchTreatment() { return fWeakReferenceMismatchTreatment; } +Options::Treatment Options::multipleDefinitionsInDylibs() +{ + return fMultiplyDefinedDynamic; +} + +Options::Treatment Options::overridingDefinitionInDependentDylib() +{ + return fMultiplyDefinedUnused; +} + +bool Options::warnOnMultipleDefinitionsInObjectFiles() +{ + return fWarnOnMultiplyDefined; +} + const char* Options::umbrellaName() { return fUmbrellaName; } +std::vector& Options::allowableClients() +{ + return fAllowableClients; +} + +const char* Options::clientName() +{ + return fClientName; +} + uint64_t Options::zeroPageSize() { return fZeroPageSize; @@ -201,7 +232,7 @@ bool Options::hasCustomStack() { return (fStackSize != 0); } - + uint64_t Options::customStackSize() { return fStackSize; @@ -212,16 +243,31 @@ uint64_t Options::customStackAddr() return fStackAddr; } +bool Options::hasExecutableStack() +{ + return fExecutableStack; +} + std::vector& Options::initialUndefines() { return fInitialUndefines; } +std::vector& Options::traceSymbols() +{ + return fTraceSymbols; +} + const char* Options::initFunctionName() { return fInitFunctionName; } +const char* Options::dotOutputFile() +{ + return fDotOutputFile; +} + bool Options::hasExportRestrictList() { return (fExportMode != kExportDefault); @@ -242,7 +288,6 @@ std::vector& Options::sectionAlignments() return fSectionAlignments; } - Options::CommonsMode Options::commonsMode() { return fCommonsMode; @@ -253,6 +298,31 @@ bool Options::warnCommons() return fWarnCommons; } +bool Options::keepRelocations() +{ + return fKeepRelocations; +} + +bool Options::emitUUID() +{ + return fEmitUUID; +} + +bool Options::warnStabs() +{ + return fWarnStabs; +} + +const char* Options::executablePath() +{ + return fExecutablePath; +} + +Options::DeadStripMode Options::deadStrip() +{ + return fDeadStrip; +} + bool Options::shouldExport(const char* symbolName) { switch (fExportMode) { @@ -266,7 +336,6 @@ bool Options::shouldExport(const char* symbolName) throw "internal error"; } - void Options::parseArch(const char* architecture) { if ( architecture == NULL ) @@ -277,22 +346,26 @@ void Options::parseArch(const char* architecture) fArchitecture = CPU_TYPE_POWERPC64; else if ( strcmp(architecture, "i386") == 0 ) fArchitecture = CPU_TYPE_I386; - else + else throw "-arch followed by unknown architecture name"; } bool Options::checkForFile(const char* format, const char* dir, const char* rootName, FileInfo& result) { struct stat statBuffer; - char possiblePath[strlen(dir)+strlen(rootName)+20]; + char possiblePath[strlen(dir)+strlen(rootName)+strlen(format)+8]; sprintf(possiblePath, format, dir, rootName); - if ( stat(possiblePath, &statBuffer) == 0 ) { + bool found = (stat(possiblePath, &statBuffer) == 0); + if ( fTraceDylibSearching ) + printf("[Logging for XBS]%sfound library: '%s'\n", (found ? " " : " not "), possiblePath); + if ( found ) { result.path = strdup(possiblePath); result.fileLen = statBuffer.st_size; + result.modTime = statBuffer.st_mtime; return true; } return false; -} +} Options::FileInfo Options::findLibrary(const char* rootName) @@ -301,7 +374,9 @@ Options::FileInfo Options::findLibrary(const char* rootName) const int rootNameLen = strlen(rootName); // if rootName ends in .o there is no .a vs .dylib choice if ( (rootNameLen > 3) && (strcmp(&rootName[rootNameLen-2], ".o") == 0) ) { - for (std::vector::iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { + for (std::vector::iterator it = fLibrarySearchPaths.begin(); + it != fLibrarySearchPaths.end(); + it++) { const char* dir = *it; if ( checkForFile("%s/%s", dir, rootName, result) ) return result; @@ -310,17 +385,21 @@ Options::FileInfo Options::findLibrary(const char* rootName) else { bool lookForDylibs = ( fOutputKind != Options::kDyld); switch ( fLibrarySearchMode ) { - case kSearchAllDirsForDylibsThenAllDirsForArchives: + case kSearchAllDirsForDylibsThenAllDirsForArchives: // first look in all directories for just for dylibs if ( lookForDylibs ) { - for (std::vector::iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { + for (std::vector::iterator it = fLibrarySearchPaths.begin(); + it != fLibrarySearchPaths.end(); + it++) { const char* dir = *it; if ( checkForFile("%s/lib%s.dylib", dir, rootName, result) ) return result; } } // next look in all directories for just for archives - for (std::vector::iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { + for (std::vector::iterator it = fLibrarySearchPaths.begin(); + it != fLibrarySearchPaths.end(); + it++) { const char* dir = *it; if ( checkForFile("%s/lib%s.a", dir, rootName, result) ) return result; @@ -329,7 +408,9 @@ Options::FileInfo Options::findLibrary(const char* rootName) case kSearchDylibAndArchiveInEachDir: // look in each directory for just for a dylib then for an archive - for (std::vector::iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { + for (std::vector::iterator it = fLibrarySearchPaths.begin(); + it != fLibrarySearchPaths.end(); + it++) { const char* dir = *it; if ( lookForDylibs && checkForFile("%s/lib%s.dylib", dir, rootName, result) ) return result; @@ -347,7 +428,11 @@ Options::FileInfo Options::findFramework(const char* rootName) { struct stat statBuffer; const int rootNameLen = strlen(rootName); - for (std::vector::iterator it = fFrameworkSearchPaths.begin(); it != fFrameworkSearchPaths.end(); it++) { + for (std::vector::iterator it = fFrameworkSearchPaths.begin(); + it != fFrameworkSearchPaths.end(); + it++) { + // ??? Shouldn't we be using String here and just initializing it? + // ??? Use str.c_str () to pull out the string for the stat call. const char* dir = *it; char possiblePath[strlen(dir)+2*rootNameLen+20]; strcpy(possiblePath, dir); @@ -355,10 +440,15 @@ Options::FileInfo Options::findFramework(const char* rootName) strcat(possiblePath, rootName); strcat(possiblePath, ".framework/"); strcat(possiblePath, rootName); - if ( stat(possiblePath, &statBuffer) == 0 ) { + bool found = (stat(possiblePath, &statBuffer) == 0); + if ( fTraceDylibSearching ) + printf("[Logging for XBS]%sfound framework: '%s'\n", + (found ? " " : " not "), possiblePath); + if ( found ) { FileInfo result; result.path = strdup(possiblePath); result.fileLen = statBuffer.st_size; + result.modTime = statBuffer.st_mtime; return result; } } @@ -369,11 +459,12 @@ Options::FileInfo Options::findFile(const char* path) { FileInfo result; struct stat statBuffer; - + // if absolute path and not a .o file, the use SDK prefix if ( (path[0] == '/') && (strcmp(&path[strlen(path)-2], ".o") != 0) ) { const int pathLen = strlen(path); for (std::vector::iterator it = fSDKPaths.begin(); it != fSDKPaths.end(); it++) { + // ??? Shouldn't we be using String here? const char* sdkPathDir = *it; const int sdkPathDirLen = strlen(sdkPathDir); char possiblePath[sdkPathDirLen+pathLen+4]; @@ -384,6 +475,7 @@ Options::FileInfo Options::findFile(const char* path) if ( stat(possiblePath, &statBuffer) == 0 ) { result.path = strdup(possiblePath); result.fileLen = statBuffer.st_size; + result.modTime = statBuffer.st_mtime; return result; } } @@ -392,8 +484,27 @@ Options::FileInfo Options::findFile(const char* path) if ( stat(path, &statBuffer) == 0 ) { result.path = strdup(path); result.fileLen = statBuffer.st_size; + result.modTime = statBuffer.st_mtime; return result; } + + // try @executable_path substitution + if ( (strncmp(path, "@executable_path/", 17) == 0) && (fExecutablePath != NULL) ) { + char newPath[strlen(fExecutablePath) + strlen(path)]; + strcpy(newPath, fExecutablePath); + char* addPoint = strrchr(newPath,'/'); + if ( addPoint != NULL ) + strcpy(&addPoint[1], &path[17]); + else + strcpy(newPath, &path[17]); + if ( stat(newPath, &statBuffer) == 0 ) { + result.path = strdup(newPath); + result.fileLen = statBuffer.st_size; + result.modTime = statBuffer.st_mtime; + return result; + } + } + // not found throwf("file not found: %s", path); } @@ -402,16 +513,16 @@ Options::FileInfo Options::findFile(const char* path) void Options::loadFileList(const char* fileOfPaths) { FILE* file = fopen(fileOfPaths, "r"); - if ( file == NULL ) + if ( file == NULL ) throwf("-filelist file not found: %s\n", fileOfPaths); - + char path[1024]; while ( fgets(path, 1024, file) != NULL ) { path[1023] = '\0'; char* eol = strchr(path, '\n'); if ( eol != NULL ) *eol = '\0'; - + fInputFiles.push_back(findFile(path)); } fclose(file); @@ -429,53 +540,68 @@ void Options::loadExportFile(const char* fileOfExports, const char* option, Name char* p = (char*)malloc(stat_buf.st_size); if ( p == NULL ) throwf("can't process %s file: %s", option, fileOfExports); - + if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) throwf("can't read %s file: %s", option, fileOfExports); - + ::close(fd); - + // parse into symbols and add to hash_set char * const end = &p[stat_buf.st_size]; enum { lineStart, inSymbol, inComment } state = lineStart; char* symbolStart = NULL; for (char* s = p; s < end; ++s ) { switch ( state ) { - case lineStart: - if ( *s =='#' ) { - state = inComment; - } - else if ( !isspace(*s) ) { - state = inSymbol; - symbolStart = s; - } - break; - case inSymbol: - if ( *s == '\n' ) { - *s = '\0'; - // removing any trailing spaces - char* last = s-1; - while ( isspace(*last) ) { - *last = '\0'; - --last; - } - set.insert(symbolStart); - symbolStart = NULL; - state = lineStart; + case lineStart: + if ( *s =='#' ) { + state = inComment; + } + else if ( !isspace(*s) ) { + state = inSymbol; + symbolStart = s; + } + break; + case inSymbol: + if ( *s == '\n' ) { + *s = '\0'; + // removing any trailing spaces + char* last = s-1; + while ( isspace(*last) ) { + *last = '\0'; + --last; } - break; - case inComment: - if ( *s == '\n' ) - state = lineStart; - break; + set.insert(symbolStart); + symbolStart = NULL; + state = lineStart; + } + break; + case inComment: + if ( *s == '\n' ) + state = lineStart; + break; + } + } + if ( state == inSymbol ) { + fprintf(stderr, "ld64 warning: missing line-end at end of file \"%s\"\n", fileOfExports); + int len = end-symbolStart+1; + char* temp = new char[len]; + strlcpy(temp, symbolStart, len); + + // remove any trailing spaces + char* last = &temp[len-2]; + while ( isspace(*last) ) { + *last = '\0'; + --last; } + set.insert(temp); } + // Note: we do not free() the malloc buffer, because the strings are used by the export-set hash table } void Options::setUndefinedTreatment(const char* treatment) { - if ( treatment == NULL ) + if ( treatment == NULL ) throw "-undefined missing [ warning | error | suppress | dynamic_lookup ]"; if ( strcmp(treatment, "warning") == 0 ) @@ -490,37 +616,43 @@ void Options::setUndefinedTreatment(const char* treatment) throw "invalid option to -undefined [ warning | error | suppress | dynamic_lookup ]"; } -void Options::setReadOnlyRelocTreatment(const char* treatment) +Options::Treatment Options::parseTreatment(const char* treatment) { - if ( treatment == NULL ) - throw "-read_only_relocs missing [ warning | error | suppress ]"; + if ( treatment == NULL ) + return kNULL; if ( strcmp(treatment, "warning") == 0 ) - throw "-read_only_relocs warning not supported"; + return kWarning; + else if ( strcmp(treatment, "error") == 0 ) + return kError; else if ( strcmp(treatment, "suppress") == 0 ) - throw "-read_only_relocs suppress not supported"; - else if ( strcmp(treatment, "error") != 0 ) - throw "invalid option to -read_only_relocs [ warning | error | suppress | dynamic_lookup ]"; + return kSuppress; + else + return kInvalid; } -void Options::setPICTreatment(const char* treatment) -{ - if ( treatment == NULL ) - throw "-sect_diff_relocs missing [ warning | error | suppress ]"; - - if ( strcmp(treatment, "warning") == 0 ) - fPICTreatment = kPICWarning; - else if ( strcmp(treatment, "error") == 0 ) - fPICTreatment = kPICError; - else if ( strcmp(treatment, "suppress") == 0 ) - fPICTreatment = kPICSuppress; +void Options::setVersionMin(const char* version) +{ + if ( version == NULL ) + throw "-macosx_version_min argument missing"; + + if ( strcmp(version, "10.1") == 0 ) + fVersionMin = k10_1; + else if ( strcmp(version, "10.2") == 0) + fVersionMin = k10_2; + else if ( strcmp(version, "10.3") == 0) + fVersionMin = k10_3; + else if ( strcmp(version, "10.4") == 0) + fVersionMin = k10_4; + else if ( strcmp(version, "10.5") == 0) + fVersionMin = k10_5; else - throw "invalid option to -sect_diff_relocs [ warning | error | suppress ]"; + fprintf(stderr, "ld64: unknown option to -macosx_version_min"); } void Options::setWeakReferenceMismatchTreatment(const char* treatment) { - if ( treatment == NULL ) + if ( treatment == NULL ) throw "-weak_reference_mismatches missing [ error | weak | non-weak ]"; if ( strcmp(treatment, "error") == 0 ) @@ -535,7 +667,7 @@ void Options::setWeakReferenceMismatchTreatment(const char* treatment) Options::CommonsMode Options::parseCommonsTreatment(const char* mode) { - if ( mode == NULL ) + if ( mode == NULL ) throw "-commons missing [ ignore_dylibs | use_dylibs | error ]"; if ( strcmp(mode, "ignore_dylibs") == 0 ) @@ -548,22 +680,12 @@ Options::CommonsMode Options::parseCommonsTreatment(const char* mode) throw "invalid option to -commons [ ignore_dylibs | use_dylibs | error ]"; } - void Options::setDylibInstallNameOverride(const char* paths) { } -void Options::setExecutablePath(const char* path) -{ - - -} - - - - uint64_t Options::parseAddress(const char* addr) { char* endptr; @@ -571,10 +693,8 @@ uint64_t Options::parseAddress(const char* addr) return result; } - - // -// Parses number of form X[.Y[.Z]] into a uint32_t where the nibbles are xxxx.yy.zz +// Parses number of form X[.Y[.Z]] into a uint32_t where the nibbles are xxxx.yy.zz // // uint32_t Options::parseVersionNumber(const char* versionString) @@ -605,8 +725,12 @@ void Options::addSection(const char* segment, const char* section, const char* p { if ( strlen(segment) > 16 ) throw "-seccreate segment name max 16 chars"; - if ( strlen(section) > 16 ) - throw "-seccreate section name max 16 chars"; + if ( strlen(section) > 16 ) { + char* tmp = strdup(section); + tmp[16] = '\0'; + fprintf(stderr, "ld64 warning: -seccreate section name (%s) truncated to 16 chars (%s)\n", section, tmp); + section = tmp; + } // read in whole file int fd = ::open(path, O_RDONLY, 0); @@ -620,7 +744,7 @@ void Options::addSection(const char* segment, const char* section, const char* p if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) throwf("can't read -sectcreate file: %s", path); ::close(fd); - + // record section to create ExtraSection info = { segment, section, path, (uint8_t*)p, stat_buf.st_size }; fExtraSections.push_back(info); @@ -633,33 +757,59 @@ void Options::addSectionAlignment(const char* segment, const char* section, cons if ( strlen(section) > 16 ) throw "-sectalign section name max 16 chars"; + // argument to -sectalign is a hexadecimal number char* endptr; unsigned long value = strtoul(alignmentStr, &endptr, 16); if ( *endptr != '\0') throw "argument for -sectalign is not a hexadecimal number"; if ( value > 0x8000 ) throw "argument for -sectalign must be less than or equal to 0x8000"; - uint8_t alignment = 0; - for(unsigned long x=value; x != 1; x >>= 1) - ++alignment; - if ( (unsigned long)(1 << alignment) != value ) - throw "argument for -sectalign is not a power of two"; + if ( value == 0 ) { + fprintf(stderr, "ld64 warning: zero is not a valid -sectalign\n"); + value = 1; + } + + // alignment is power of 2 (e.g. page alignment = 12) + uint8_t alignment = (uint8_t)log2(value); + + if ( (unsigned long)(1 << alignment) != value ) { + fprintf(stderr, "ld64 warning: alignment for -sectalign %s %s is not a power of two, using 0x%X\n", + segment, section, 1 << alignment); + } SectionAlignment info = { segment, section, alignment }; fSectionAlignments.push_back(info); } - +// +// Process all command line arguments. +// +// The only error checking done here is that each option is valid and if it has arguments +// that they too are valid. +// +// The general rule is "last option wins", i.e. if both -bundle and -dylib are specified, +// whichever was last on the command line is used. +// +// Error check for invalid combinations of options is done in checkIllegalOptionCombinations() +// void Options::parse(int argc, const char* argv[]) { // pass one builds search list from -L and -F options this->buildSearchPaths(argc, argv); - + + // reduce re-allocations + fInputFiles.reserve(32); + // pass two parse all other options for(int i=1; i < argc; ++i) { const char* arg = argv[i]; - + if ( arg[0] == '-' ) { + + // Since we don't care about the files passed, just the option names, we do this here. + if (fPrintOptions) + fprintf (stderr, "[Logging ld64 options]\t%s\n", arg); + if ( (arg[1] == 'L') || (arg[1] == 'F') ) { // previously handled by buildSearchPaths() } @@ -694,11 +844,15 @@ void Options::parse(int argc, const char* argv[]) else if ( arg[1] == 'l' ) { fInputFiles.push_back(findLibrary(&arg[2])); } - else if ( strcmp(arg, "-weak-l") == 0 ) { - FileInfo info = findLibrary(&arg[2]); + // This causes a dylib to be weakly bound at + // link time. This corresponds to weak_import. + else if ( strncmp(arg, "-weak-l", 7) == 0 ) { + FileInfo info = findLibrary(&arg[7]); info.options.fWeakImport = true; fInputFiles.push_back(info); } + // Avoid lazy binding. + // ??? Deprecate. else if ( strcmp(arg, "-bind_at_load") == 0 ) { fBindAtLoad = true; } @@ -708,15 +862,21 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-flat_namespace") == 0 ) { fNameSpace = kFlatNameSpace; } + // Also sets a bit to ensure dyld causes everything + // in the namespace to be flat. + // ??? Deprecate else if ( strcmp(arg, "-force_flat_namespace") == 0 ) { fNameSpace = kForceFlatNameSpace; } + // Similar to --whole-archive. else if ( strcmp(arg, "-all_load") == 0 ) { fReaderOptions.fFullyLoadArchives = true; } + // Similar to --whole-archive, but for all ObjC classes. else if ( strcmp(arg, "-ObjC") == 0 ) { fReaderOptions.fLoadObjcClassesInArchives = true; } + // Library versioning. else if ( strcmp(arg, "-dylib_compatibility_version") == 0 ) { fDylibCompatVersion = parseVersionNumber(argv[++i]); } @@ -727,32 +887,41 @@ void Options::parse(int argc, const char* argv[]) parseSectionOrderFile(argv[i+1], argv[i+2], argv[i+3]); i += 3; } + // ??? Deprecate segcreate. + // -sectcreate puts whole files into a section in the output. else if ( (strcmp(arg, "-sectcreate") == 0) || (strcmp(arg, "-segcreate") == 0) ) { addSection(argv[i+1], argv[i+2], argv[i+3]); i += 3; } + // Since we have a full path in binary/library names we need to be able to override it. else if ( (strcmp(arg, "-dylib_install_name") == 0) || (strcmp(arg, "-dylinker_install_name") == 0) ) { fDylibInstallName = argv[++i]; } + // Sets the base address of the output. else if ( strcmp(arg, "-seg1addr") == 0 ) { fBaseAddress = parseAddress(argv[++i]); } else if ( strcmp(arg, "-e") == 0 ) { fEntryName = argv[++i]; } + // Same as -@ from the FSF linker. else if ( strcmp(arg, "-filelist") == 0 ) { loadFileList(argv[++i]); } else if ( strcmp(arg, "-keep_private_externs") == 0 ) { fKeepPrivateExterns = true; } + // ??? Deprecate else if ( strcmp(arg, "-final_output") == 0 ) { ++i; // ignore for now } + // Ensure that all calls to exported symbols go through lazy pointers. Multi-module + // just ensures that this happens for cross object file boundaries. else if ( (strcmp(arg, "-interposable") == 0) || (strcmp(arg, "-multi_module") == 0)) { fInterposable = true; } + // Default for -interposable/-multi_module/-single_module. else if ( strcmp(arg, "-single_module") == 0 ) { fInterposable = false; } @@ -768,12 +937,14 @@ void Options::parse(int argc, const char* argv[]) fExportMode = kDontExportSome; loadExportFile(argv[++i], "-unexported_symbols_list", fDontExportSymbols); } + // ??? Deprecate else if ( strcmp(arg, "-no_arch_warnings") == 0 ) { fIgnoreOtherArchFiles = true; } else if ( strcmp(arg, "-force_cpusubtype_ALL") == 0 ) { fForceSubtypeAll = true; } + // Similar to -weak-l but uses the absolute path name to the library. else if ( strcmp(arg, "-weak_library") == 0 ) { FileInfo info = findFile(argv[++i]); info.options.fWeakImport = true; @@ -787,75 +958,119 @@ void Options::parse(int argc, const char* argv[]) info.options.fWeakImport = true; fInputFiles.push_back(info); } + // ??? Deprecate when we get -Bstatic/-Bdynamic. else if ( strcmp(arg, "-search_paths_first") == 0 ) { fLibrarySearchMode = kSearchDylibAndArchiveInEachDir; } else if ( strcmp(arg, "-undefined") == 0 ) { setUndefinedTreatment(argv[++i]); } + // Debugging output flag. else if ( strcmp(arg, "-arch_multiple") == 0 ) { fMessagesPrefixedWithArchitecture = true; } + // Specify what to do with relocations in read only + // sections like .text. Could be errors, warnings, + // or suppressed. Currently we do nothing with the + // flag. else if ( strcmp(arg, "-read_only_relocs") == 0 ) { - setReadOnlyRelocTreatment(argv[++i]); + Treatment temp = parseTreatment(argv[++i]); + + if ( temp == kNULL ) + throw "-read_only_relocs missing [ warning | error | suppress ]"; + else if ( temp == kInvalid ) + throw "invalid option to -read_only_relocs [ warning | error | suppress ]"; } + // Specifies whether or not there are intra section + // relocations and what to do when found. Could be + // errors, warnings, or suppressed. else if ( strcmp(arg, "-sect_diff_relocs") == 0 ) { - setPICTreatment(argv[++i]); + fPICTreatment = parseTreatment(argv[++i]); + + if ( fPICTreatment == kNULL ) + throw "-sect_diff_relocs missing [ warning | error | suppress ]"; + else if ( fPICTreatment == kInvalid ) + throw "invalid option to -sect_diff_relocs [ warning | error | suppress ]"; } + // Warn, error or make strong a mismatch between weak + // and non-weak references. else if ( strcmp(arg, "-weak_reference_mismatches") == 0 ) { setWeakReferenceMismatchTreatment(argv[++i]); } + // For a deployment target of 10.3 and earlier ld64 will + // prebind an executable with 0s in all addresses that + // are prebound. This can then be fixed up by update_prebinding + // later. Prebinding is less useful on 10.4 and greater. else if ( strcmp(arg, "-prebind") == 0 ) { - // FIX FIX + fPrebind = true; } else if ( strcmp(arg, "-noprebind") == 0 ) { - // FIX FIX + fPrebind = false; } + // ??? Deprecate else if ( strcmp(arg, "-prebind_allow_overlap") == 0 ) { - // FIX FIX + // Do not handle and suppress warnings always. } + // ??? Deprecate else if ( strcmp(arg, "-prebind_all_twolevel_modules") == 0 ) { - // FIX FIX + // Ignore. } + // ??? Deprecate else if ( strcmp(arg, "-noprebind_all_twolevel_modules") == 0 ) { - // FIX FIX + // Ignore. } + // Sets a bit in the main executable only that causes fix_prebinding + // not to run. This is always set. else if ( strcmp(arg, "-nofixprebinding") == 0 ) { - // FIX FIX + // Ignore. } + // This should probably be deprecated when we respect -L and -F + // when searching for libraries. else if ( strcmp(arg, "-dylib_file") == 0 ) { setDylibInstallNameOverride(argv[++i]); } + // Allows us to rewrite full paths to be relocatable based on + // the path name of the executable. else if ( strcmp(arg, "-executable_path") == 0 ) { - setExecutablePath(argv[++i]); + fExecutablePath = argv[++i]; + if ( (fExecutablePath == NULL) || (fExecutablePath[0] == '-') ) + throw "-executable_path missing "; } + // ??? Deprecate + // Aligns all segments to the power of 2 boundary specified. else if ( strcmp(arg, "-segalign") == 0 ) { - // FIX FIX - ++i; + // Ignore. + ++i; } + // Puts a specified segment at a particular address that must + // be a multiple of the segment alignment. else if ( strcmp(arg, "-segaddr") == 0 ) { // FIX FIX - i += 2; + i += 2; } + // ??? Deprecate when we deprecate split-seg. else if ( strcmp(arg, "-segs_read_only_addr") == 0 ) { - // FIX FIX - ++i; + // Ignore. + ++i; } + // ??? Deprecate when we deprecate split-seg. else if ( strcmp(arg, "-segs_read_write_addr") == 0 ) { - // FIX FIX - ++i; + // Ignore. + ++i; } + // ??? Deprecate when we get rid of basing at build time. else if ( strcmp(arg, "-seg_addr_table") == 0 ) { - // FIX FIX - ++i; + // Ignore. + ++i; } + // ??? Deprecate. else if ( strcmp(arg, "-seg_addr_table_filename") == 0 ) { - // FIX FIX - ++i; + // Ignore. + ++i; } else if ( strcmp(arg, "-segprot") == 0 ) { // FIX FIX - i += 3; + i += 3; } else if ( strcmp(arg, "-pagezero_size") == 0 ) { fZeroPageSize = parseAddress(argv[++i]); @@ -867,6 +1082,9 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-stack_size") == 0 ) { fStackSize = parseAddress(argv[++i]); } + else if ( strcmp(arg, "-allow_stack_execute") == 0 ) { + fExecutableStack = true; + } else if ( strcmp(arg, "-sectalign") == 0 ) { addSectionAlignment(argv[i+1], argv[i+2], argv[i+3]); i += 3; @@ -888,30 +1106,53 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-twolevel_namespace_hints") == 0 ) { // FIX FIX } + // Use this flag to set default behavior for deployement targets. else if ( strcmp(arg, "-macosx_version_min") == 0 ) { - // This flag isn't needed yet, so just ignore it. - ++i; + setVersionMin(argv[++i]); } + // This option (unlike -m below) only affects how we warn + // on multiple definitions inside dynamic libraries. else if ( strcmp(arg, "-multiply_defined") == 0 ) { - // FIX FIX - ++i; + fMultiplyDefinedDynamic = parseTreatment(argv[++i]); + + if ( fMultiplyDefinedDynamic == kNULL ) + throw "-multiply_defined missing [ warning | error | suppress ]"; + else if ( fMultiplyDefinedDynamic == kInvalid ) + throw "invalid option to -multiply_defined [ warning | error | suppress ]"; } else if ( strcmp(arg, "-multiply_defined_unused") == 0 ) { - // FIX FIX - ++i; + fMultiplyDefinedUnused = parseTreatment(argv[++i]); + + if ( fMultiplyDefinedUnused == kNULL ) + throw "-multiply_defined_unused missing [ warning | error | suppress ]"; + else if ( fMultiplyDefinedUnused == kInvalid ) + throw "invalid option to -multiply_defined_unused [ warning | error | suppress ]"; } else if ( strcmp(arg, "-nomultidefs") == 0 ) { // FIX FIX } - else if ( arg[1] == 'y' ) { - // FIX FIX + // Display each file in which the argument symbol appears and whether + // the file defines or references it. This option takes an argument + // as -y note that there is no space. + else if ( strncmp(arg, "-y", 2) == 0 ) { + const char* name = &arg[2]; + + if ( name == NULL ) + throw "-y missing argument"; + + fTraceSymbols.push_back(name); } + // Same output as -y, but output number of undefined symbols only. else if ( strcmp(arg, "-Y") == 0 ) { - ++i; - // FIX FIX + char* endptr; + fLimitUndefinedSymbols = strtoul (argv[++i], &endptr, 10); + + if(*endptr != '\0') + throw "invalid argument for -Y [decimal number]"; } + // This option affects all objects linked into the final result. else if ( strcmp(arg, "-m") == 0 ) { - // FIX FIX + fWarnOnMultiplyDefined = true; } else if ( strcmp(arg, "-whyload") == 0 ) { // FIX FIX @@ -922,37 +1163,42 @@ void Options::parse(int argc, const char* argv[]) throw "-u missing argument"; fInitialUndefines.push_back(name); } - else if ( strcmp(arg, "-i") == 0 ) { - // FIX FIX - } else if ( strcmp(arg, "-U") == 0 ) { // FIX FIX - ++i; + ++i; } else if ( strcmp(arg, "-s") == 0 ) { - // FIX FIX + fStripLocalSymbols = true; + fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoNone; } else if ( strcmp(arg, "-x") == 0 ) { - // FIX FIX + fStripLocalSymbols = true; } else if ( strcmp(arg, "-S") == 0 ) { - // FIX FIX + fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoNone; } else if ( strcmp(arg, "-X") == 0 ) { // FIX FIX } else if ( strcmp(arg, "-Si") == 0 ) { - // FIX FIX + fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoFull; } else if ( strcmp(arg, "-b") == 0 ) { // FIX FIX } else if ( strcmp(arg, "-Sn") == 0 ) { - // FIX FIX + fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoFull; + } + else if ( strcmp(arg, "-Sp") == 0 ) { + fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoMinimal; } else if ( strcmp(arg, "-dead_strip") == 0 ) { - // FIX FIX - fprintf(stderr, "ld64: warning -dead_strip not yet supported for 64-bit code\n"); + //fDeadStrip = kDeadStripOnPlusUnusedInits; + fprintf(stderr, "ld64: warning -dead_strip not yet supported in ld64\n"); + } + else if ( strcmp(arg, "-no_dead_strip_inits_and_terms") == 0 ) { + //fDeadStrip = kDeadStripOn; + fprintf(stderr, "ld64: warning -dead_strip not yet supported in ld64\n"); } else if ( strcmp(arg, "-w") == 0 ) { // FIX FIX @@ -980,7 +1226,7 @@ void Options::parse(int argc, const char* argv[]) } else if ( strcmp(arg, "-A") == 0 ) { // FIX FIX - ++i; + ++i; } else if ( strcmp(arg, "-umbrella") == 0 ) { const char* name = argv[++i]; @@ -989,12 +1235,20 @@ void Options::parse(int argc, const char* argv[]) fUmbrellaName = name; } else if ( strcmp(arg, "-allowable_client") == 0 ) { - // FIX FIX - ++i; + const char* name = argv[++i]; + + if ( name == NULL ) + throw "-allowable_client missing argument"; + + fAllowableClients.push_back(name); } else if ( strcmp(arg, "-client_name") == 0 ) { - // FIX FIX - ++i; + const char* name = argv[++i]; + + if ( name == NULL ) + throw "-client_name missing argument"; + + fClientName = name; } else if ( strcmp(arg, "-sub_umbrella") == 0 ) { const char* name = argv[++i]; @@ -1014,12 +1268,30 @@ void Options::parse(int argc, const char* argv[]) throw "-init missing argument"; fInitFunctionName = name; } + else if ( strcmp(arg, "-dot") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-dot missing argument"; + fDotOutputFile = name; + } else if ( strcmp(arg, "-warn_commons") == 0 ) { fWarnCommons = true; } else if ( strcmp(arg, "-commons") == 0 ) { fCommonsMode = parseCommonsTreatment(argv[++i]); } + else if ( strcmp(arg, "-keep_relocs") == 0 ) { + fKeepRelocations = true; + } + else if ( strcmp(arg, "-warn_stabs") == 0 ) { + fWarnStabs = true; + } + else if ( strcmp(arg, "-pause") == 0 ) { + fPause = true; + } + else if ( strcmp(arg, "-print_statistics") == 0 ) { + fStatistics = true; + } else if ( strcmp(arg, "-v") == 0 ) { // previously handled by buildSearchPaths() } @@ -1030,8 +1302,15 @@ void Options::parse(int argc, const char* argv[]) ++i; // previously handled by buildSearchPaths() } + else if ( strcmp(arg, "-no_uuid") == 0 ) { + fEmitUUID = false; + } + // put this last so that it does not interfer with other options starting with 'i' + else if ( strncmp(arg, "-i", 2) == 0 ) { + fprintf(stderr, "ld64: -i option (indirect symbols) not supported\n"); + } else { - fprintf(stderr, "unknown option: %s\n", arg); + throwf("unknown option: %s", arg); } } else { @@ -1040,22 +1319,22 @@ void Options::parse(int argc, const char* argv[]) } } - - -// -// -syslibroot is used for SDK support. +// +// -syslibroot is used for SDK support. // The rule is that all search paths (both explicit and default) are // checked to see if they exist in the SDK. If so, that path is // replaced with the sdk prefixed path. If not, that search path // is used as is. If multiple -syslibroot options are specified // their directory structures are logically overlayed and files // from sdks specified earlier on the command line used before later ones. -// + void Options::buildSearchPaths(int argc, const char* argv[]) { bool addStandardLibraryDirectories = true; std::vector libraryPaths; std::vector frameworkPaths; + libraryPaths.reserve(10); + frameworkPaths.reserve(10); // scan through argv looking for -L, -F, -Z, and -syslibroot options for(int i=0; i < argc; ++i) { if ( (argv[i][0] == '-') && (argv[i][1] == 'L') ) @@ -1082,13 +1361,14 @@ void Options::buildSearchPaths(int argc, const char* argv[]) if ( addStandardLibraryDirectories ) { libraryPaths.push_back("/usr/lib"); libraryPaths.push_back("/usr/local/lib"); - + frameworkPaths.push_back("/Library/Frameworks/"); frameworkPaths.push_back("/Network/Library/Frameworks/"); frameworkPaths.push_back("/System/Library/Frameworks/"); } - + // now merge sdk and library paths to make real search paths + fLibrarySearchPaths.reserve(libraryPaths.size()*(fSDKPaths.size()+1)); for (std::vector::iterator it = libraryPaths.begin(); it != libraryPaths.end(); it++) { const char* libDir = *it; bool sdkOverride = false; @@ -1096,10 +1376,11 @@ void Options::buildSearchPaths(int argc, const char* argv[]) char betterLibDir[PATH_MAX]; if ( strstr(libDir, "/..") != NULL ) { if ( realpath(libDir, betterLibDir) != NULL ) - libDir = betterLibDir; + libDir = strdup(betterLibDir); } const int libDirLen = strlen(libDir); for (std::vector::iterator sdkit = fSDKPaths.begin(); sdkit != fSDKPaths.end(); sdkit++) { + // ??? Should be using string here. const char* sdkDir = *sdkit; const int sdkDirLen = strlen(sdkDir); char newPath[libDirLen + sdkDirLen+4]; @@ -1114,11 +1395,12 @@ void Options::buildSearchPaths(int argc, const char* argv[]) } } } - if ( !sdkOverride ) + if ( !sdkOverride ) fLibrarySearchPaths.push_back(libDir); } - + // now merge sdk and framework paths to make real search paths + fFrameworkSearchPaths.reserve(frameworkPaths.size()*(fSDKPaths.size()+1)); for (std::vector::iterator it = frameworkPaths.begin(); it != frameworkPaths.end(); it++) { const char* frameworkDir = *it; bool sdkOverride = false; @@ -1126,10 +1408,11 @@ void Options::buildSearchPaths(int argc, const char* argv[]) char betterFrameworkDir[PATH_MAX]; if ( strstr(frameworkDir, "/..") != NULL ) { if ( realpath(frameworkDir, betterFrameworkDir) != NULL ) - frameworkDir = betterFrameworkDir; + frameworkDir = strdup(betterFrameworkDir); } const int frameworkDirLen = strlen(frameworkDir); for (std::vector::iterator sdkit = fSDKPaths.begin(); sdkit != fSDKPaths.end(); sdkit++) { + // ??? Should be using string here const char* sdkDir = *sdkit; const int sdkDirLen = strlen(sdkDir); char newPath[frameworkDirLen + sdkDirLen+4]; @@ -1144,16 +1427,20 @@ void Options::buildSearchPaths(int argc, const char* argv[]) } } } - if ( !sdkOverride ) + if ( !sdkOverride ) fFrameworkSearchPaths.push_back(frameworkDir); } - + if ( fVerbose ) { fprintf(stderr,"Library search paths:\n"); - for (std::vector::iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) + for (std::vector::iterator it = fLibrarySearchPaths.begin(); + it != fLibrarySearchPaths.end(); + it++) fprintf(stderr,"\t%s\n", *it); fprintf(stderr,"Framework search paths:\n"); - for (std::vector::iterator it = fFrameworkSearchPaths.begin(); it != fFrameworkSearchPaths.end(); it++) + for (std::vector::iterator it = fFrameworkSearchPaths.begin(); + it != fFrameworkSearchPaths.end(); + it++) fprintf(stderr,"\t%s\n", *it); } } @@ -1161,19 +1448,34 @@ void Options::buildSearchPaths(int argc, const char* argv[]) // this is run before the command line is parsed void Options::parsePreCommandLineEnvironmentSettings() { - if ( getenv("RC_TRACE_ARCHIVES") != NULL) + if ((getenv("LD_TRACE_ARCHIVES") != NULL) + || (getenv("RC_TRACE_ARCHIVES") != NULL)) fReaderOptions.fTraceArchives = true; - - if ( getenv("RC_TRACE_DYLIBS") != NULL) { + + if ((getenv("LD_TRACE_DYLIBS") != NULL) + || (getenv("RC_TRACE_DYLIBS") != NULL)) { fReaderOptions.fTraceDylibs = true; fReaderOptions.fTraceIndirectDylibs = true; } + + if (getenv("RC_TRACE_DYLIB_SEARCHING") != NULL) { + fTraceDylibSearching = true; + } + + if (getenv("LD_PRINT_OPTIONS") != NULL) + fPrintOptions = true; + + if (fReaderOptions.fTraceDylibs || fReaderOptions.fTraceArchives) + fReaderOptions.fTraceOutputFile = getenv("LD_TRACE_FILE"); } // this is run after the command line is parsed void Options::parsePostCommandLineEnvironmentSettings() { - + // when building a dynamic main executable, default any use of @executable_path to output path + if ( fExecutablePath == NULL && (fOutputKind == kDynamicExecutable) ) { + fExecutablePath = fOutputFile; + } } void Options::checkIllegalOptionCombinations() @@ -1191,7 +1493,7 @@ void Options::checkIllegalOptionCombinations() throw "can't use -undefined warning or suppress with -twolevel_namespace"; break; } - + // unify -sub_umbrella with dylibs for (std::vector::iterator it = fSubUmbellas.begin(); it != fSubUmbellas.end(); it++) { const char* subUmbrella = *it; @@ -1210,7 +1512,7 @@ void Options::checkIllegalOptionCombinations() if ( ! found ) fprintf(stderr, "ld64 warning: -sub_umbrella %s does not match a supplied dylib\n", subUmbrella); } - + // unify -sub_library with dylibs for (std::vector::iterator it = fSubLibraries.begin(); it != fSubLibraries.end(); it++) { const char* subLibrary = *it; @@ -1232,17 +1534,17 @@ void Options::checkIllegalOptionCombinations() if ( ! found ) fprintf(stderr, "ld64 warning: -sub_library %s does not match a supplied dylib\n", subLibrary); } - + // sync reader options if ( fNameSpace != kTwoLevelNameSpace ) fReaderOptions.fFlatNamespace = true; // check -stack_addr - if ( fStackAddr != 0 ) { + if ( fStackAddr != 0 ) { switch (fArchitecture) { case CPU_TYPE_I386: case CPU_TYPE_POWERPC: - if ( fStackAddr > 0xFFFFFFFF ) + if ( fStackAddr > 0xFFFFFFFF ) throw "-stack_addr must be < 4G for 32-bit processes"; break; case CPU_TYPE_POWERPC64: @@ -1253,13 +1555,13 @@ void Options::checkIllegalOptionCombinations() if ( fStackSize == 0 ) throw "-stack_addr must be used with -stack_size"; } - + // check -stack_size - if ( fStackSize != 0 ) { + if ( fStackSize != 0 ) { switch (fArchitecture) { case CPU_TYPE_I386: case CPU_TYPE_POWERPC: - if ( fStackSize > 0xFFFFFFFF ) + if ( fStackSize > 0xFFFFFFFF ) throw "-stack_size must be < 4G for 32-bit processes"; if ( fStackAddr == 0 ) { fprintf(stderr, "ld64 warning: -stack_addr not specified, using the default 0xC0000000\n"); @@ -1278,7 +1580,7 @@ void Options::checkIllegalOptionCombinations() switch ( fOutputKind ) { case Options::kDynamicExecutable: case Options::kStaticExecutable: - // custom stack size only legeal when building main executable + // custom stack size only legal when building main executable break; case Options::kDynamicLibrary: case Options::kDynamicBundle: @@ -1287,15 +1589,36 @@ void Options::checkIllegalOptionCombinations() throw "-stack_size option can only be used when linking a main executable"; } } - + + // check that -allow_stack_execute is only used with main executables + if ( fExecutableStack ) { + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + // -allow_stack_execute size only legal when building main executable + break; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kObjectFile: + case Options::kDyld: + throw "-allow_stack_execute option can only be used when linking a main executable"; + } + } + + // check -client_name is only used when -bundle is specified + if ( (fClientName != NULL) && (fOutputKind != Options::kDynamicBundle) ) + throw "-client_name can only be used with -bundle"; + // check -init is only used when building a dylib if ( (fInitFunctionName != NULL) && (fOutputKind != Options::kDynamicLibrary) ) throw "-init can only be used with -dynamiclib"; - - // make sure all required exported symbols exist - for (NameSet::iterator it=fExportSymbols.begin(); it != fExportSymbols.end(); it++) - fInitialUndefines.push_back(*it); - -} + // make sure all required exported symbols exist + for (NameSet::iterator it=fExportSymbols.begin(); it != fExportSymbols.end(); it++) { + const char* name = *it; + // never export .eh symbols + if ( strcmp(&name[strlen(name)-3], ".eh") != 0 ) + fInitialUndefines.push_back(name); + } +} diff --git a/src/Options.cpp.orig b/src/Options.cpp.orig deleted file mode 100644 index 6984e67..0000000 --- a/src/Options.cpp.orig +++ /dev/null @@ -1,1297 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * 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@ - */ - - -#include -#include -#include -#include - - -#include "Options.h" - - __attribute__((noreturn)) -void throwf(const char* format, ...) -{ - va_list list; - char* p; - va_start(list, format); - vasprintf(&p, format, list); - va_end(list); - - const char* t = p; - throw t; -} - - -Options::Options(int argc, const char* argv[]) - : fOutputFile("a.out"), fArchitecture(CPU_TYPE_POWERPC64), fOutputKind(kDynamicExecutable), fBindAtLoad(false), - fStripLocalSymbols(false), fKeepPrivateExterns(false), - fInterposable(false), fIgnoreOtherArchFiles(false), fForceSubtypeAll(false), fNameSpace(kTwoLevelNameSpace), - fDylibCompatVersion(0), fDylibCurrentVersion(0), fDylibInstallName(NULL), fEntryName("start"), fBaseAddress(0), - fExportMode(kExportDefault), fLibrarySearchMode(kSearchAllDirsForDylibsThenAllDirsForArchives), - fUndefinedTreatment(kUndefinedError), fMessagesPrefixedWithArchitecture(false), fPICTreatment(kPICError), - fWeakReferenceMismatchTreatment(kWeakReferenceMismatchError), - fUmbrellaName(NULL), fInitFunctionName(NULL), fZeroPageSize(0x1000), fStackSize(0), fStackAddr(0), fMinimumHeaderPad(0), - fCommonsMode(kCommonsIgnoreDylibs), fWarnCommons(false), fVerbose(false) -{ - this->parsePreCommandLineEnvironmentSettings(); - this->parse(argc, argv); - this->parsePostCommandLineEnvironmentSettings(); - this->checkIllegalOptionCombinations(); -} - -Options::~Options() -{ -} - - -ObjectFile::ReaderOptions& Options::readerOptions() -{ - return fReaderOptions; -} - -cpu_type_t Options::architecture() -{ - return fArchitecture; -} - - -const char* Options::getOutputFilePath() -{ - return fOutputFile; -} - - -std::vector& Options::getInputFiles() -{ - return fInputFiles; -} - -Options::OutputKind Options::outputKind() -{ - return fOutputKind; -} - -bool Options::stripLocalSymbols() -{ - return fStripLocalSymbols; -} - -bool Options::stripDebugInfo() -{ - return fReaderOptions.fStripDebugInfo; -} - -bool Options::bindAtLoad() -{ - return fBindAtLoad; -} - -bool Options::fullyLoadArchives() -{ - return fReaderOptions.fFullyLoadArchives; -} - -Options::NameSpace Options::nameSpace() -{ - return fNameSpace; -} - -const char* Options::installPath() -{ - if ( fDylibInstallName != NULL ) - return fDylibInstallName; - else - return fOutputFile; -} - -uint32_t Options::currentVersion() -{ - return fDylibCurrentVersion; -} - -uint32_t Options::compatibilityVersion() -{ - return fDylibCompatVersion; -} - -const char* Options::entryName() -{ - return fEntryName; -} - -uint64_t Options::baseAddress() -{ - return fBaseAddress; -} - -bool Options::keepPrivateExterns() -{ - return fKeepPrivateExterns; -} - -bool Options::interposable() -{ - return fInterposable; -} - -bool Options::ignoreOtherArchInputFiles() -{ - return fIgnoreOtherArchFiles; -} - -bool Options::forceCpuSubtypeAll() -{ - return fForceSubtypeAll; -} - -bool Options::traceDylibs() -{ - return fReaderOptions.fTraceDylibs; -} - -bool Options::traceArchives() -{ - return fReaderOptions.fTraceArchives; -} - -Options::UndefinedTreatment Options::undefinedTreatment() -{ - return fUndefinedTreatment; -} - -Options::WeakReferenceMismatchTreatment Options::weakReferenceMismatchTreatment() -{ - return fWeakReferenceMismatchTreatment; -} - -const char* Options::umbrellaName() -{ - return fUmbrellaName; -} - -uint64_t Options::zeroPageSize() -{ - return fZeroPageSize; -} - -bool Options::hasCustomStack() -{ - return (fStackSize != 0); -} - -uint64_t Options::customStackSize() -{ - return fStackSize; -} - -uint64_t Options::customStackAddr() -{ - return fStackAddr; -} - -std::vector& Options::initialUndefines() -{ - return fInitialUndefines; -} - -const char* Options::initFunctionName() -{ - return fInitFunctionName; -} - -bool Options::hasExportRestrictList() -{ - return (fExportMode != kExportDefault); -} - -uint32_t Options::minimumHeaderPad() -{ - return fMinimumHeaderPad; -} - -std::vector& Options::extraSections() -{ - return fExtraSections; -} - -std::vector& Options::sectionAlignments() -{ - return fSectionAlignments; -} - - -Options::CommonsMode Options::commonsMode() -{ - return fCommonsMode; -} - -bool Options::warnCommons() -{ - return fWarnCommons; -} - -bool Options::shouldExport(const char* symbolName) -{ - switch (fExportMode) { - case kExportSome: - return ( fExportSymbols.find(symbolName) != fExportSymbols.end() ); - case kDontExportSome: - return ( fDontExportSymbols.find(symbolName) == fDontExportSymbols.end() ); - case kExportDefault: - return true; - } - throw "internal error"; -} - - -void Options::parseArch(const char* architecture) -{ - if ( architecture == NULL ) - throw "-arch must be followed by an architecture string"; - if ( strcmp(architecture, "ppc") == 0 ) - fArchitecture = CPU_TYPE_POWERPC; - else if ( strcmp(architecture, "ppc64") == 0 ) - fArchitecture = CPU_TYPE_POWERPC64; - else if ( strcmp(architecture, "i386") == 0 ) - fArchitecture = CPU_TYPE_I386; - else - throw "-arch followed by unknown architecture name"; -} - -bool Options::checkForFile(const char* format, const char* dir, const char* rootName, FileInfo& result) -{ - struct stat statBuffer; - char possiblePath[strlen(dir)+strlen(rootName)+20]; - sprintf(possiblePath, format, dir, rootName); - if ( stat(possiblePath, &statBuffer) == 0 ) { - result.path = strdup(possiblePath); - result.fileLen = statBuffer.st_size; - return true; - } - return false; -} - - -Options::FileInfo Options::findLibrary(const char* rootName) -{ - FileInfo result; - const int rootNameLen = strlen(rootName); - // if rootName ends in .o there is no .a vs .dylib choice - if ( (rootNameLen > 3) && (strcmp(&rootName[rootNameLen-2], ".o") == 0) ) { - for (std::vector::iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { - const char* dir = *it; - if ( checkForFile("%s/%s", dir, rootName, result) ) - return result; - } - } - else { - bool lookForDylibs = ( fOutputKind != Options::kDyld); - switch ( fLibrarySearchMode ) { - case kSearchAllDirsForDylibsThenAllDirsForArchives: - // first look in all directories for just for dylibs - if ( lookForDylibs ) { - for (std::vector::iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { - const char* dir = *it; - if ( checkForFile("%s/lib%s.dylib", dir, rootName, result) ) - return result; - } - } - // next look in all directories for just for archives - for (std::vector::iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { - const char* dir = *it; - if ( checkForFile("%s/lib%s.a", dir, rootName, result) ) - return result; - } - break; - - case kSearchDylibAndArchiveInEachDir: - // look in each directory for just for a dylib then for an archive - for (std::vector::iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { - const char* dir = *it; - if ( lookForDylibs && checkForFile("%s/lib%s.dylib", dir, rootName, result) ) - return result; - if ( checkForFile("%s/lib%s.a", dir, rootName, result) ) - return result; - } - break; - } - } - throwf("library not found for -l%s", rootName); -} - - -Options::FileInfo Options::findFramework(const char* rootName) -{ - struct stat statBuffer; - const int rootNameLen = strlen(rootName); - for (std::vector::iterator it = fFrameworkSearchPaths.begin(); it != fFrameworkSearchPaths.end(); it++) { - const char* dir = *it; - char possiblePath[strlen(dir)+2*rootNameLen+20]; - strcpy(possiblePath, dir); - strcat(possiblePath, "/"); - strcat(possiblePath, rootName); - strcat(possiblePath, ".framework/"); - strcat(possiblePath, rootName); - if ( stat(possiblePath, &statBuffer) == 0 ) { - FileInfo result; - result.path = strdup(possiblePath); - result.fileLen = statBuffer.st_size; - return result; - } - } - throwf("framework not found %s", rootName); -} - -Options::FileInfo Options::findFile(const char* path) -{ - FileInfo result; - struct stat statBuffer; - - // if absolute path and not a .o file, the use SDK prefix - if ( (path[0] == '/') && (strcmp(&path[strlen(path)-2], ".o") != 0) ) { - const int pathLen = strlen(path); - for (std::vector::iterator it = fSDKPaths.begin(); it != fSDKPaths.end(); it++) { - const char* sdkPathDir = *it; - const int sdkPathDirLen = strlen(sdkPathDir); - char possiblePath[sdkPathDirLen+pathLen+4]; - strcpy(possiblePath, sdkPathDir); - if ( possiblePath[sdkPathDirLen-1] == '/' ) - possiblePath[sdkPathDirLen-1] = '\0'; - strcat(possiblePath, path); - if ( stat(possiblePath, &statBuffer) == 0 ) { - result.path = strdup(possiblePath); - result.fileLen = statBuffer.st_size; - return result; - } - } - } - // try raw path - if ( stat(path, &statBuffer) == 0 ) { - result.path = strdup(path); - result.fileLen = statBuffer.st_size; - return result; - } - // not found - throwf("file not found: %s", path); -} - - -void Options::loadFileList(const char* fileOfPaths) -{ - FILE* file = fopen(fileOfPaths, "r"); - if ( file == NULL ) - throwf("-filelist file not found: %s\n", fileOfPaths); - - char path[1024]; - while ( fgets(path, 1024, file) != NULL ) { - path[1023] = '\0'; - char* eol = strchr(path, '\n'); - if ( eol != NULL ) - *eol = '\0'; - - fInputFiles.push_back(findFile(path)); - } - fclose(file); -} - - -void Options::loadExportFile(const char* fileOfExports, const char* option, NameSet& set) -{ - // read in whole file - int fd = ::open(fileOfExports, O_RDONLY, 0); - if ( fd == -1 ) - throwf("can't open %s file: %s", option, fileOfExports); - struct stat stat_buf; - ::fstat(fd, &stat_buf); - char* p = (char*)malloc(stat_buf.st_size); - if ( p == NULL ) - throwf("can't process %s file: %s", option, fileOfExports); - - if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) - throwf("can't read %s file: %s", option, fileOfExports); - - ::close(fd); - - // parse into symbols and add to hash_set - char * const end = &p[stat_buf.st_size]; - enum { lineStart, inSymbol, inComment } state = lineStart; - char* symbolStart = NULL; - for (char* s = p; s < end; ++s ) { - switch ( state ) { - case lineStart: - if ( *s =='#' ) { - state = inComment; - } - else if ( !isspace(*s) ) { - state = inSymbol; - symbolStart = s; - } - break; - case inSymbol: - if ( *s == '\n' ) { - *s = '\0'; - // removing any trailing spaces - char* last = s-1; - while ( isspace(*last) ) { - *last = '\0'; - --last; - } - set.insert(symbolStart); - symbolStart = NULL; - state = lineStart; - } - break; - case inComment: - if ( *s == '\n' ) - state = lineStart; - break; - } - } - // Note: we do not free() the malloc buffer, because the strings are used by the export-set hash table -} - -void Options::setUndefinedTreatment(const char* treatment) -{ - if ( treatment == NULL ) - throw "-undefined missing [ warning | error | suppress | dynamic_lookup ]"; - - if ( strcmp(treatment, "warning") == 0 ) - fUndefinedTreatment = kUndefinedWarning; - else if ( strcmp(treatment, "error") == 0 ) - fUndefinedTreatment = kUndefinedError; - else if ( strcmp(treatment, "suppress") == 0 ) - fUndefinedTreatment = kUndefinedSuppress; - else if ( strcmp(treatment, "dynamic_lookup") == 0 ) - fUndefinedTreatment = kUndefinedDynamicLookup; - else - throw "invalid option to -undefined [ warning | error | suppress | dynamic_lookup ]"; -} - -void Options::setReadOnlyRelocTreatment(const char* treatment) -{ - if ( treatment == NULL ) - throw "-read_only_relocs missing [ warning | error | suppress ]"; - - if ( strcmp(treatment, "warning") == 0 ) - throw "-read_only_relocs warning not supported"; - else if ( strcmp(treatment, "suppress") == 0 ) - throw "-read_only_relocs suppress not supported"; - else if ( strcmp(treatment, "error") != 0 ) - throw "invalid option to -read_only_relocs [ warning | error | suppress | dynamic_lookup ]"; -} - -void Options::setPICTreatment(const char* treatment) -{ - if ( treatment == NULL ) - throw "-sect_diff_relocs missing [ warning | error | suppress ]"; - - if ( strcmp(treatment, "warning") == 0 ) - fPICTreatment = kPICWarning; - else if ( strcmp(treatment, "error") == 0 ) - fPICTreatment = kPICError; - else if ( strcmp(treatment, "suppress") == 0 ) - fPICTreatment = kPICSuppress; - else - throw "invalid option to -sect_diff_relocs [ warning | error | suppress ]"; -} - -void Options::setWeakReferenceMismatchTreatment(const char* treatment) -{ - if ( treatment == NULL ) - throw "-weak_reference_mismatches missing [ error | weak | non-weak ]"; - - if ( strcmp(treatment, "error") == 0 ) - fWeakReferenceMismatchTreatment = kWeakReferenceMismatchError; - else if ( strcmp(treatment, "weak") == 0 ) - fWeakReferenceMismatchTreatment = kWeakReferenceMismatchWeak; - else if ( strcmp(treatment, "non-weak") == 0 ) - fWeakReferenceMismatchTreatment = kWeakReferenceMismatchNonWeak; - else - throw "invalid option to -weak_reference_mismatches [ error | weak | non-weak ]"; -} - -Options::CommonsMode Options::parseCommonsTreatment(const char* mode) -{ - if ( mode == NULL ) - throw "-commons missing [ ignore_dylibs | use_dylibs | error ]"; - - if ( strcmp(mode, "ignore_dylibs") == 0 ) - return kCommonsIgnoreDylibs; - else if ( strcmp(mode, "use_dylibs") == 0 ) - return kCommonsOverriddenByDylibs; - else if ( strcmp(mode, "error") == 0 ) - return kCommonsConflictsDylibsError; - else - throw "invalid option to -commons [ ignore_dylibs | use_dylibs | error ]"; -} - - -void Options::setDylibInstallNameOverride(const char* paths) -{ - - -} - -void Options::setExecutablePath(const char* path) -{ - - -} - - - - -uint64_t Options::parseAddress(const char* addr) -{ - char* endptr; - uint64_t result = strtoull(addr, &endptr, 16); - return result; -} - - - -// -// Parses number of form X[.Y[.Z]] into a uint32_t where the nibbles are xxxx.yy.zz -// -// -uint32_t Options::parseVersionNumber(const char* versionString) -{ - unsigned long x = 0; - unsigned long y = 0; - unsigned long z = 0; - char* end; - x = strtoul(versionString, &end, 10); - if ( *end == '.' ) { - y = strtoul(&end[1], &end, 10); - if ( *end == '.' ) { - z = strtoul(&end[1], &end, 10); - } - } - if ( (*end != '\0') || (x > 0xffff) || (y > 0xff) || (z > 0xff) ) - throwf("malformed version number: %s", versionString); - - return (x << 16) | ( y << 8 ) | z; -} - -void Options::parseSectionOrderFile(const char* segment, const char* section, const char* path) -{ - fprintf(stderr, "ld64: warning -sectorder not yet supported for 64-bit code\n"); -} - -void Options::addSection(const char* segment, const char* section, const char* path) -{ - if ( strlen(segment) > 16 ) - throw "-seccreate segment name max 16 chars"; - if ( strlen(section) > 16 ) - throw "-seccreate section name max 16 chars"; - - // read in whole file - int fd = ::open(path, O_RDONLY, 0); - if ( fd == -1 ) - throwf("can't open -sectcreate file: %s", path); - struct stat stat_buf; - ::fstat(fd, &stat_buf); - char* p = (char*)malloc(stat_buf.st_size); - if ( p == NULL ) - throwf("can't process -sectcreate file: %s", path); - if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) - throwf("can't read -sectcreate file: %s", path); - ::close(fd); - - // record section to create - ExtraSection info = { segment, section, path, (uint8_t*)p, stat_buf.st_size }; - fExtraSections.push_back(info); -} - -void Options::addSectionAlignment(const char* segment, const char* section, const char* alignmentStr) -{ - if ( strlen(segment) > 16 ) - throw "-sectalign segment name max 16 chars"; - if ( strlen(section) > 16 ) - throw "-sectalign section name max 16 chars"; - - char* endptr; - unsigned long value = strtoul(alignmentStr, &endptr, 16); - if ( *endptr != '\0') - throw "argument for -sectalign is not a hexadecimal number"; - if ( value > 0x8000 ) - throw "argument for -sectalign must be less than or equal to 0x8000"; - uint8_t alignment = 0; - for(unsigned long x=value; x != 1; x >>= 1) - ++alignment; - if ( (unsigned long)(1 << alignment) != value ) - throw "argument for -sectalign is not a power of two"; - - SectionAlignment info = { segment, section, alignment }; - fSectionAlignments.push_back(info); -} - - -void Options::parse(int argc, const char* argv[]) -{ - // pass one builds search list from -L and -F options - this->buildSearchPaths(argc, argv); - - // pass two parse all other options - for(int i=1; i < argc; ++i) { - const char* arg = argv[i]; - - if ( arg[0] == '-' ) { - if ( (arg[1] == 'L') || (arg[1] == 'F') ) { - // previously handled by buildSearchPaths() - } - else if ( strcmp(arg, "-arch") == 0 ) { - parseArch(argv[++i]); - } - else if ( strcmp(arg, "-dynamic") == 0 ) { - // default - } - else if ( strcmp(arg, "-static") == 0 ) { - fOutputKind = kStaticExecutable; - } - else if ( strcmp(arg, "-dylib") == 0 ) { - fOutputKind = kDynamicLibrary; - } - else if ( strcmp(arg, "-bundle") == 0 ) { - fOutputKind = kDynamicBundle; - } - else if ( strcmp(arg, "-dylinker") == 0 ) { - fOutputKind = kDyld; - } - else if ( strcmp(arg, "-execute") == 0 ) { - if ( fOutputKind != kStaticExecutable ) - fOutputKind = kDynamicExecutable; - } - else if ( strcmp(arg, "-r") == 0 ) { - fOutputKind = kObjectFile; - } - else if ( strcmp(arg, "-o") == 0 ) { - fOutputFile = argv[++i]; - } - else if ( arg[1] == 'l' ) { - fInputFiles.push_back(findLibrary(&arg[2])); - } - else if ( strcmp(arg, "-weak-l") == 0 ) { - FileInfo info = findLibrary(&arg[2]); - info.options.fWeakImport = true; - fInputFiles.push_back(info); - } - else if ( strcmp(arg, "-bind_at_load") == 0 ) { - fBindAtLoad = true; - } - else if ( strcmp(arg, "-twolevel_namespace") == 0 ) { - fNameSpace = kTwoLevelNameSpace; - } - else if ( strcmp(arg, "-flat_namespace") == 0 ) { - fNameSpace = kFlatNameSpace; - } - else if ( strcmp(arg, "-force_flat_namespace") == 0 ) { - fNameSpace = kForceFlatNameSpace; - } - else if ( strcmp(arg, "-all_load") == 0 ) { - fReaderOptions.fFullyLoadArchives = true; - } - else if ( strcmp(arg, "-ObjC") == 0 ) { - fReaderOptions.fLoadObjcClassesInArchives = true; - } - else if ( strcmp(arg, "-dylib_compatibility_version") == 0 ) { - fDylibCompatVersion = parseVersionNumber(argv[++i]); - } - else if ( strcmp(arg, "-dylib_current_version") == 0 ) { - fDylibCurrentVersion = parseVersionNumber(argv[++i]); - } - else if ( strcmp(arg, "-sectorder") == 0 ) { - parseSectionOrderFile(argv[i+1], argv[i+2], argv[i+3]); - i += 3; - } - else if ( (strcmp(arg, "-sectcreate") == 0) || (strcmp(arg, "-segcreate") == 0) ) { - addSection(argv[i+1], argv[i+2], argv[i+3]); - i += 3; - } - else if ( (strcmp(arg, "-dylib_install_name") == 0) || (strcmp(arg, "-dylinker_install_name") == 0) ) { - fDylibInstallName = argv[++i]; - } - else if ( strcmp(arg, "-seg1addr") == 0 ) { - fBaseAddress = parseAddress(argv[++i]); - } - else if ( strcmp(arg, "-e") == 0 ) { - fEntryName = argv[++i]; - } - else if ( strcmp(arg, "-filelist") == 0 ) { - loadFileList(argv[++i]); - } - else if ( strcmp(arg, "-keep_private_externs") == 0 ) { - fKeepPrivateExterns = true; - } - else if ( strcmp(arg, "-final_output") == 0 ) { - ++i; - // ignore for now - } - else if ( (strcmp(arg, "-interposable") == 0) || (strcmp(arg, "-multi_module") == 0)) { - fInterposable = true; - } - else if ( strcmp(arg, "-single_module") == 0 ) { - fInterposable = false; - } - else if ( strcmp(arg, "-exported_symbols_list") == 0 ) { - if ( fExportMode == kDontExportSome ) - throw "can't use -exported_symbols_list and -unexported_symbols_list"; - fExportMode = kExportSome; - loadExportFile(argv[++i], "-exported_symbols_list", fExportSymbols); - } - else if ( strcmp(arg, "-unexported_symbols_list") == 0 ) { - if ( fExportMode == kExportSome ) - throw "can't use -exported_symbols_list and -unexported_symbols_list"; - fExportMode = kDontExportSome; - loadExportFile(argv[++i], "-unexported_symbols_list", fDontExportSymbols); - } - else if ( strcmp(arg, "-no_arch_warnings") == 0 ) { - fIgnoreOtherArchFiles = true; - } - else if ( strcmp(arg, "-force_cpusubtype_ALL") == 0 ) { - fForceSubtypeAll = true; - } - else if ( strcmp(arg, "-weak_library") == 0 ) { - FileInfo info = findFile(argv[++i]); - info.options.fWeakImport = true; - fInputFiles.push_back(info); - } - else if ( strcmp(arg, "-framework") == 0 ) { - fInputFiles.push_back(findFramework(argv[++i])); - } - else if ( strcmp(arg, "-weak_framework") == 0 ) { - FileInfo info = findFramework(argv[++i]); - info.options.fWeakImport = true; - fInputFiles.push_back(info); - } - else if ( strcmp(arg, "-search_paths_first") == 0 ) { - fLibrarySearchMode = kSearchDylibAndArchiveInEachDir; - } - else if ( strcmp(arg, "-undefined") == 0 ) { - setUndefinedTreatment(argv[++i]); - } - else if ( strcmp(arg, "-arch_multiple") == 0 ) { - fMessagesPrefixedWithArchitecture = true; - } - else if ( strcmp(arg, "-read_only_relocs") == 0 ) { - setReadOnlyRelocTreatment(argv[++i]); - } - else if ( strcmp(arg, "-sect_diff_relocs") == 0 ) { - setPICTreatment(argv[++i]); - } - else if ( strcmp(arg, "-weak_reference_mismatches") == 0 ) { - setWeakReferenceMismatchTreatment(argv[++i]); - } - else if ( strcmp(arg, "-prebind") == 0 ) { - // FIX FIX - } - else if ( strcmp(arg, "-noprebind") == 0 ) { - // FIX FIX - } - else if ( strcmp(arg, "-prebind_allow_overlap") == 0 ) { - // FIX FIX - } - else if ( strcmp(arg, "-prebind_all_twolevel_modules") == 0 ) { - // FIX FIX - } - else if ( strcmp(arg, "-noprebind_all_twolevel_modules") == 0 ) { - // FIX FIX - } - else if ( strcmp(arg, "-nofixprebinding") == 0 ) { - // FIX FIX - } - else if ( strcmp(arg, "-dylib_file") == 0 ) { - setDylibInstallNameOverride(argv[++i]); - } - else if ( strcmp(arg, "-executable_path") == 0 ) { - setExecutablePath(argv[++i]); - } - else if ( strcmp(arg, "-segalign") == 0 ) { - // FIX FIX - ++i; - } - else if ( strcmp(arg, "-segaddr") == 0 ) { - // FIX FIX - i += 2; - } - else if ( strcmp(arg, "-segs_read_only_addr") == 0 ) { - // FIX FIX - ++i; - } - else if ( strcmp(arg, "-segs_read_write_addr") == 0 ) { - // FIX FIX - ++i; - } - else if ( strcmp(arg, "-seg_addr_table") == 0 ) { - // FIX FIX - ++i; - } - else if ( strcmp(arg, "-seg_addr_table_filename") == 0 ) { - // FIX FIX - ++i; - } - else if ( strcmp(arg, "-segprot") == 0 ) { - // FIX FIX - i += 3; - } - else if ( strcmp(arg, "-pagezero_size") == 0 ) { - fZeroPageSize = parseAddress(argv[++i]); - fZeroPageSize &= (-4096); // page align - } - else if ( strcmp(arg, "-stack_addr") == 0 ) { - fStackAddr = parseAddress(argv[++i]); - } - else if ( strcmp(arg, "-stack_size") == 0 ) { - fStackSize = parseAddress(argv[++i]); - } - else if ( strcmp(arg, "-sectalign") == 0 ) { - addSectionAlignment(argv[i+1], argv[i+2], argv[i+3]); - i += 3; - } - else if ( strcmp(arg, "-sectorder_detail") == 0 ) { - // FIX FIX - } - else if ( strcmp(arg, "-sectobjectsymbols") == 0 ) { - // FIX FIX - i += 2; - } - else if ( strcmp(arg, "-bundle_loader") == 0 ) { - // FIX FIX - ++i; - } - else if ( strcmp(arg, "-private_bundle") == 0 ) { - // FIX FIX - } - else if ( strcmp(arg, "-twolevel_namespace_hints") == 0 ) { - // FIX FIX - } - else if ( strcmp(arg, "-multiply_defined") == 0 ) { - // FIX FIX - ++i; - } - else if ( strcmp(arg, "-multiply_defined_unused") == 0 ) { - // FIX FIX - ++i; - } - else if ( strcmp(arg, "-nomultidefs") == 0 ) { - // FIX FIX - } - else if ( arg[1] == 'y' ) { - // FIX FIX - } - else if ( strcmp(arg, "-Y") == 0 ) { - ++i; - // FIX FIX - } - else if ( strcmp(arg, "-m") == 0 ) { - // FIX FIX - } - else if ( strcmp(arg, "-whyload") == 0 ) { - // FIX FIX - } - else if ( strcmp(arg, "-u") == 0 ) { - const char* name = argv[++i]; - if ( name == NULL ) - throw "-u missing argument"; - fInitialUndefines.push_back(name); - } - else if ( strcmp(arg, "-i") == 0 ) { - // FIX FIX - } - else if ( strcmp(arg, "-U") == 0 ) { - // FIX FIX - ++i; - } - else if ( strcmp(arg, "-s") == 0 ) { - // FIX FIX - } - else if ( strcmp(arg, "-x") == 0 ) { - // FIX FIX - } - else if ( strcmp(arg, "-S") == 0 ) { - // FIX FIX - } - else if ( strcmp(arg, "-X") == 0 ) { - // FIX FIX - } - else if ( strcmp(arg, "-Si") == 0 ) { - // FIX FIX - } - else if ( strcmp(arg, "-b") == 0 ) { - // FIX FIX - } - else if ( strcmp(arg, "-Sn") == 0 ) { - // FIX FIX - } - else if ( strcmp(arg, "-dead_strip") == 0 ) { - // FIX FIX - fprintf(stderr, "ld64: warning -dead_strip not yet supported for 64-bit code\n"); - } - else if ( strcmp(arg, "-w") == 0 ) { - // FIX FIX - } - else if ( strcmp(arg, "-arch_errors_fatal") == 0 ) { - // FIX FIX - } - else if ( strcmp(arg, "-M") == 0 ) { - // FIX FIX - } - else if ( strcmp(arg, "-whatsloaded") == 0 ) { - // FIX FIX - } - else if ( strcmp(arg, "-headerpad") == 0 ) { - const char* size = argv[++i]; - if ( size == NULL ) - throw "-headerpad missing argument"; - fMinimumHeaderPad = parseAddress(size); - } - else if ( strcmp(arg, "-headerpad_max_install_names") == 0 ) { - // FIX FIX - } - else if ( strcmp(arg, "-t") == 0 ) { - // FIX FIX - } - else if ( strcmp(arg, "-A") == 0 ) { - // FIX FIX - ++i; - } - else if ( strcmp(arg, "-umbrella") == 0 ) { - const char* name = argv[++i]; - if ( name == NULL ) - throw "-umbrella missing argument"; - fUmbrellaName = name; - } - else if ( strcmp(arg, "-allowable_client") == 0 ) { - // FIX FIX - ++i; - } - else if ( strcmp(arg, "-client_name") == 0 ) { - // FIX FIX - ++i; - } - else if ( strcmp(arg, "-sub_umbrella") == 0 ) { - const char* name = argv[++i]; - if ( name == NULL ) - throw "-sub_umbrella missing argument"; - fSubUmbellas.push_back(name); - } - else if ( strcmp(arg, "-sub_library") == 0 ) { - const char* name = argv[++i]; - if ( name == NULL ) - throw "-sub_library missing argument"; - fSubLibraries.push_back(name); - } - else if ( strcmp(arg, "-init") == 0 ) { - const char* name = argv[++i]; - if ( name == NULL ) - throw "-init missing argument"; - fInitFunctionName = name; - } - else if ( strcmp(arg, "-warn_commons") == 0 ) { - fWarnCommons = true; - } - else if ( strcmp(arg, "-commons") == 0 ) { - fCommonsMode = parseCommonsTreatment(argv[++i]); - } - else if ( strcmp(arg, "-v") == 0 ) { - // previously handled by buildSearchPaths() - } - else if ( strcmp(arg, "-Z") == 0 ) { - // previously handled by buildSearchPaths() - } - else if ( strcmp(arg, "-syslibroot") == 0 ) { - ++i; - // previously handled by buildSearchPaths() - } - else { - fprintf(stderr, "unknown option: %s\n", arg); - } - } - else { - fInputFiles.push_back(findFile(arg)); - } - } -} - - - -// -// -syslibroot is used for SDK support. -// The rule is that all search paths (both explicit and default) are -// checked to see if they exist in the SDK. If so, that path is -// replaced with the sdk prefixed path. If not, that search path -// is used as is. If multiple -syslibroot options are specified -// their directory structures are logically overlayed and files -// from sdks specified earlier on the command line used before later ones. -// -void Options::buildSearchPaths(int argc, const char* argv[]) -{ - bool addStandardLibraryDirectories = true; - std::vector libraryPaths; - std::vector frameworkPaths; - // scan through argv looking for -L, -F, -Z, and -syslibroot options - for(int i=0; i < argc; ++i) { - if ( (argv[i][0] == '-') && (argv[i][1] == 'L') ) - libraryPaths.push_back(&argv[i][2]); - else if ( (argv[i][0] == '-') && (argv[i][1] == 'F') ) - frameworkPaths.push_back(&argv[i][2]); - else if ( strcmp(argv[i], "-Z") == 0 ) - addStandardLibraryDirectories = false; - else if ( strcmp(argv[i], "-v") == 0 ) { - fVerbose = true; - extern const char ld64VersionString[]; - fprintf(stderr, "%s", ld64VersionString); - // if only -v specified, exit cleanly - if ( argc == 2 ) - exit(0); - } - else if ( strcmp(argv[i], "-syslibroot") == 0 ) { - const char* path = argv[++i]; - if ( path == NULL ) - throw "-syslibroot missing argument"; - fSDKPaths.push_back(path); - } - } - if ( addStandardLibraryDirectories ) { - libraryPaths.push_back("/usr/lib"); - libraryPaths.push_back("/usr/local/lib"); - - frameworkPaths.push_back("/Library/Frameworks/"); - frameworkPaths.push_back("/Network/Library/Frameworks/"); - frameworkPaths.push_back("/System/Library/Frameworks/"); - } - - // now merge sdk and library paths to make real search paths - for (std::vector::iterator it = libraryPaths.begin(); it != libraryPaths.end(); it++) { - const char* libDir = *it; - bool sdkOverride = false; - if ( libDir[0] == '/' ) { - char betterLibDir[PATH_MAX]; - if ( strstr(libDir, "/..") != NULL ) { - if ( realpath(libDir, betterLibDir) != NULL ) - libDir = betterLibDir; - } - const int libDirLen = strlen(libDir); - for (std::vector::iterator sdkit = fSDKPaths.begin(); sdkit != fSDKPaths.end(); sdkit++) { - const char* sdkDir = *sdkit; - const int sdkDirLen = strlen(sdkDir); - char newPath[libDirLen + sdkDirLen+4]; - strcpy(newPath, sdkDir); - if ( newPath[sdkDirLen-1] == '/' ) - newPath[sdkDirLen-1] = '\0'; - strcat(newPath, libDir); - struct stat statBuffer; - if ( stat(newPath, &statBuffer) == 0 ) { - fLibrarySearchPaths.push_back(strdup(newPath)); - sdkOverride = true; - } - } - } - if ( !sdkOverride ) - fLibrarySearchPaths.push_back(libDir); - } - - // now merge sdk and framework paths to make real search paths - for (std::vector::iterator it = frameworkPaths.begin(); it != frameworkPaths.end(); it++) { - const char* frameworkDir = *it; - bool sdkOverride = false; - if ( frameworkDir[0] == '/' ) { - char betterFrameworkDir[PATH_MAX]; - if ( strstr(frameworkDir, "/..") != NULL ) { - if ( realpath(frameworkDir, betterFrameworkDir) != NULL ) - frameworkDir = betterFrameworkDir; - } - const int frameworkDirLen = strlen(frameworkDir); - for (std::vector::iterator sdkit = fSDKPaths.begin(); sdkit != fSDKPaths.end(); sdkit++) { - const char* sdkDir = *sdkit; - const int sdkDirLen = strlen(sdkDir); - char newPath[frameworkDirLen + sdkDirLen+4]; - strcpy(newPath, sdkDir); - if ( newPath[sdkDirLen-1] == '/' ) - newPath[sdkDirLen-1] = '\0'; - strcat(newPath, frameworkDir); - struct stat statBuffer; - if ( stat(newPath, &statBuffer) == 0 ) { - fFrameworkSearchPaths.push_back(strdup(newPath)); - sdkOverride = true; - } - } - } - if ( !sdkOverride ) - fFrameworkSearchPaths.push_back(frameworkDir); - } - - if ( fVerbose ) { - fprintf(stderr,"Library search paths:\n"); - for (std::vector::iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) - fprintf(stderr,"\t%s\n", *it); - fprintf(stderr,"Framework search paths:\n"); - for (std::vector::iterator it = fFrameworkSearchPaths.begin(); it != fFrameworkSearchPaths.end(); it++) - fprintf(stderr,"\t%s\n", *it); - } -} - -// this is run before the command line is parsed -void Options::parsePreCommandLineEnvironmentSettings() -{ - if ( getenv("RC_TRACE_ARCHIVES") != NULL) - fReaderOptions.fTraceArchives = true; - - if ( getenv("RC_TRACE_DYLIBS") != NULL) { - fReaderOptions.fTraceDylibs = true; - fReaderOptions.fTraceIndirectDylibs = true; - } -} - -// this is run after the command line is parsed -void Options::parsePostCommandLineEnvironmentSettings() -{ - -} - -void Options::checkIllegalOptionCombinations() -{ - // check -undefined setting - switch ( fUndefinedTreatment ) { - case kUndefinedError: - case kUndefinedDynamicLookup: - // always legal - break; - case kUndefinedWarning: - case kUndefinedSuppress: - // requires flat namespace - if ( fNameSpace == kTwoLevelNameSpace ) - throw "can't use -undefined warning or suppress with -twolevel_namespace"; - break; - } - - // unify -sub_umbrella with dylibs - for (std::vector::iterator it = fSubUmbellas.begin(); it != fSubUmbellas.end(); it++) { - const char* subUmbrella = *it; - bool found = false; - for (std::vector::iterator fit = fInputFiles.begin(); fit != fInputFiles.end(); fit++) { - Options::FileInfo& info = *fit; - const char* lastSlash = strrchr(info.path, '/'); - if ( lastSlash == NULL ) - lastSlash = info.path - 1; - if ( strcmp(&lastSlash[1], subUmbrella) == 0 ) { - info.options.fReExport = true; - found = true; - break; - } - } - if ( ! found ) - fprintf(stderr, "ld64 warning: -sub_umbrella %s does not match a supplied dylib\n", subUmbrella); - } - - // unify -sub_library with dylibs - for (std::vector::iterator it = fSubLibraries.begin(); it != fSubLibraries.end(); it++) { - const char* subLibrary = *it; - bool found = false; - for (std::vector::iterator fit = fInputFiles.begin(); fit != fInputFiles.end(); fit++) { - Options::FileInfo& info = *fit; - const char* lastSlash = strrchr(info.path, '/'); - if ( lastSlash == NULL ) - lastSlash = info.path - 1; - const char* dot = strchr(lastSlash, '.'); - if ( dot == NULL ) - dot = &lastSlash[strlen(lastSlash)]; - if ( strncmp(&lastSlash[1], subLibrary, dot-lastSlash-1) == 0 ) { - info.options.fReExport = true; - found = true; - break; - } - } - if ( ! found ) - fprintf(stderr, "ld64 warning: -sub_library %s does not match a supplied dylib\n", subLibrary); - } - - // sync reader options - if ( fNameSpace != kTwoLevelNameSpace ) - fReaderOptions.fFlatNamespace = true; - - // check -stack_addr - if ( fStackAddr != 0 ) { - switch (fArchitecture) { - case CPU_TYPE_I386: - case CPU_TYPE_POWERPC: - if ( fStackAddr > 0xFFFFFFFF ) - throw "-stack_addr must be < 4G for 32-bit processes"; - break; - case CPU_TYPE_POWERPC64: - break; - } - if ( (fStackAddr & -4096) != fStackAddr ) - throw "-stack_addr must be multiples of 4K"; - if ( fStackSize == 0 ) - throw "-stack_addr must be used with -stack_size"; - } - - // check -stack_size - if ( fStackSize != 0 ) { - switch (fArchitecture) { - case CPU_TYPE_I386: - case CPU_TYPE_POWERPC: - if ( fStackSize > 0xFFFFFFFF ) - throw "-stack_size must be < 4G for 32-bit processes"; - if ( fStackAddr == 0 ) { - fprintf(stderr, "ld64 warning: -stack_addr not specified, using the default 0xC0000000\n"); - fStackAddr = 0xC0000000; - } - break; - case CPU_TYPE_POWERPC64: - if ( fStackAddr == 0 ) { - fprintf(stderr, "ld64 warning: -stack_addr not specified, using the default 0x0008000000000000\n"); - fStackAddr = 0x0008000000000000LL; - } - break; - } - if ( (fStackSize & -4096) != fStackSize ) - throw "-stack_size must be multiples of 4K"; - switch ( fOutputKind ) { - case Options::kDynamicExecutable: - case Options::kStaticExecutable: - // custom stack size only legeal when building main executable - break; - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - case Options::kObjectFile: - case Options::kDyld: - throw "-stack_size option can only be used when linking a main executable"; - } - } - - // check -init is only used when building a dylib - if ( (fInitFunctionName != NULL) && (fOutputKind != Options::kDynamicLibrary) ) - throw "-init can only be used with -dynamiclib"; - - // make sure all required exported symbols exist - for (NameSet::iterator it=fExportSymbols.begin(); it != fExportSymbols.end(); it++) - fInitialUndefines.push_back(*it); - -} - - diff --git a/src/Options.h b/src/Options.h index 520b304..c8ef861 100644 --- a/src/Options.h +++ b/src/Options.h @@ -1,16 +1,16 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * * 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, @@ -18,7 +18,7 @@ * 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@ */ @@ -29,49 +29,56 @@ #include #include -#ifndef CPU_TYPE_POWERPC64 -#define CPU_TYPE_POWERPC64 0x1000012 -#endif - - #include #include #include "ObjectFile.h" -extern __attribute__((noreturn)) void throwf(const char* format, ...); - +void throwf (const char* format, ...) __attribute__ ((noreturn)); -class DynamicLibraryOptions +class DynamicLibraryOptions { public: - DynamicLibraryOptions() : fWeakImport(false), fReExport(false), fInstallPathOverride(NULL) {} - + DynamicLibraryOptions() : fWeakImport(false), fReExport(false), fInstallPathOverride(NULL) {} + bool fWeakImport; bool fReExport; const char* fInstallPathOverride; }; - +// +// The public interface to the Options class is the abstract representation of what work the linker +// should do. +// +// This abstraction layer will make it easier to support a future where the linker is a shared library +// invoked directly from Xcode. The target settings in Xcode would be used to directly construct an Options +// object (without building a command line which is then parsed). +// +// class Options { public: - Options(int argc, const char* argv[]); - ~Options(); + Options(int argc, const char* argv[]); + ~Options(); enum OutputKind { kDynamicExecutable, kStaticExecutable, kDynamicLibrary, kDynamicBundle, kObjectFile, kDyld }; enum NameSpace { kTwoLevelNameSpace, kFlatNameSpace, kForceFlatNameSpace }; + // Standard treatment for many options. + enum Treatment { kError, kWarning, kSuppress, kNULL, kInvalid }; enum UndefinedTreatment { kUndefinedError, kUndefinedWarning, kUndefinedSuppress, kUndefinedDynamicLookup }; - enum PICTreatment { kPICError, kPICWarning, kPICSuppress }; - enum WeakReferenceMismatchTreatment { kWeakReferenceMismatchError, kWeakReferenceMismatchWeak, kWeakReferenceMismatchNonWeak }; + enum WeakReferenceMismatchTreatment { kWeakReferenceMismatchError, kWeakReferenceMismatchWeak, + kWeakReferenceMismatchNonWeak }; enum CommonsMode { kCommonsIgnoreDylibs, kCommonsOverriddenByDylibs, kCommonsConflictsDylibsError }; + enum DeadStripMode { kDeadStripOff, kDeadStripOn, kDeadStripOnPlusUnusedInits }; + enum VersionMin { k10_1, k10_2, k10_3, k10_4, k10_5 }; struct FileInfo { const char* path; uint64_t fileLen; + time_t modTime; DynamicLibraryOptions options; }; - + struct ExtraSection { const char* segmentName; const char* sectionName; @@ -79,21 +86,21 @@ public: const uint8_t* data; uint64_t dataLen; }; - + struct SectionAlignment { const char* segmentName; const char* sectionName; uint8_t alignment; }; - ObjectFile::ReaderOptions& readerOptions(); - const char* getOutputFilePath(); - std::vector& getInputFiles(); - + const ObjectFile::ReaderOptions& readerOptions(); + const char* getOutputFilePath(); + std::vector& getInputFiles(); + cpu_type_t architecture(); OutputKind outputKind(); bool stripLocalSymbols(); - bool stripDebugInfo(); + bool prebind(); bool bindAtLoad(); bool fullyLoadArchives(); NameSpace nameSpace(); @@ -101,32 +108,48 @@ public: uint32_t currentVersion(); // only for kDynamicLibrary uint32_t compatibilityVersion(); // only for kDynamicLibrary const char* entryName(); // only for kDynamicExecutable or kStaticExecutable + const char* executablePath(); uint64_t baseAddress(); bool keepPrivateExterns(); // only for kObjectFile - bool interposable(); // only for kDynamicLibrary + bool interposable(); // only for kDynamicLibrary bool hasExportRestrictList(); bool shouldExport(const char*); bool ignoreOtherArchInputFiles(); bool forceCpuSubtypeAll(); bool traceDylibs(); bool traceArchives(); + DeadStripMode deadStrip(); UndefinedTreatment undefinedTreatment(); + VersionMin macosxVersionMin(); bool messagesPrefixedWithArchitecture(); - PICTreatment picTreatment(); + Treatment picTreatment(); WeakReferenceMismatchTreatment weakReferenceMismatchTreatment(); + Treatment multipleDefinitionsInDylibs(); + Treatment overridingDefinitionInDependentDylib(); + bool warnOnMultipleDefinitionsInObjectFiles(); const char* umbrellaName(); + std::vector& allowableClients(); + const char* clientName(); const char* initFunctionName(); // only for kDynamicLibrary + const char* dotOutputFile(); uint64_t zeroPageSize(); bool hasCustomStack(); uint64_t customStackSize(); uint64_t customStackAddr(); + bool hasExecutableStack(); std::vector& initialUndefines(); uint32_t minimumHeaderPad(); std::vector& extraSections(); std::vector& sectionAlignments(); CommonsMode commonsMode(); bool warnCommons(); + bool keepRelocations(); + std::vector& traceSymbols(); FileInfo findFile(const char* path); + bool emitUUID(); + bool warnStabs(); + bool pauseAtEnd() { return fPause; } + bool printStatistics() { return fStatistics; } private: class CStringEquals @@ -145,7 +168,8 @@ private: void parseArch(const char* architecture); FileInfo findLibrary(const char* rootName); FileInfo findFramework(const char* rootName); - bool checkForFile(const char* format, const char* dir, const char* rootName, FileInfo& result); + bool checkForFile(const char* format, const char* dir, const char* rootName, + FileInfo& result); uint32_t parseVersionNumber(const char*); void parseSectionOrderFile(const char* segment, const char* section, const char* path); void addSection(const char* segment, const char* section, const char* path); @@ -156,27 +180,29 @@ private: void parsePreCommandLineEnvironmentSettings(); void parsePostCommandLineEnvironmentSettings(); void setUndefinedTreatment(const char* treatment); - void setPICTreatment(const char* treatment); - void setReadOnlyRelocTreatment(const char* treatment); + void setVersionMin(const char* version); void setWeakReferenceMismatchTreatment(const char* treatment); void setDylibInstallNameOverride(const char* paths); - void setExecutablePath(const char* path); void addSectionAlignment(const char* segment, const char* section, const char* alignment); CommonsMode parseCommonsTreatment(const char* mode); - - + Treatment parseTreatment(const char* treatment); + + ObjectFile::ReaderOptions fReaderOptions; const char* fOutputFile; std::vector fInputFiles; cpu_type_t fArchitecture; OutputKind fOutputKind; + bool fPrebind; bool fBindAtLoad; bool fStripLocalSymbols; bool fKeepPrivateExterns; bool fInterposable; bool fIgnoreOtherArchFiles; bool fForceSubtypeAll; + DeadStripMode fDeadStrip; + VersionMin fVersionMin; NameSpace fNameSpace; uint32_t fDylibCompatVersion; uint32_t fDylibCurrentVersion; @@ -189,26 +215,44 @@ private: LibrarySearchMode fLibrarySearchMode; UndefinedTreatment fUndefinedTreatment; bool fMessagesPrefixedWithArchitecture; - PICTreatment fPICTreatment; + Treatment fPICTreatment; WeakReferenceMismatchTreatment fWeakReferenceMismatchTreatment; + Treatment fMultiplyDefinedDynamic; + Treatment fMultiplyDefinedUnused; + bool fWarnOnMultiplyDefined; std::vector fSubUmbellas; std::vector fSubLibraries; + std::vector fAllowableClients; + const char* fClientName; const char* fUmbrellaName; const char* fInitFunctionName; + const char* fDotOutputFile; + const char* fExecutablePath; uint64_t fZeroPageSize; uint64_t fStackSize; uint64_t fStackAddr; + bool fExecutableStack; uint32_t fMinimumHeaderPad; CommonsMode fCommonsMode; bool fWarnCommons; bool fVerbose; + bool fKeepRelocations; + bool fEmitUUID; + bool fWarnStabs; + bool fTraceDylibSearching; + bool fPause; + bool fStatistics; + bool fPrintOptions; std::vector fInitialUndefines; + std::vector fTraceSymbols; + unsigned long fLimitUndefinedSymbols; std::vector fExtraSections; std::vector fSectionAlignments; - + std::vector fLibrarySearchPaths; std::vector fFrameworkSearchPaths; std::vector fSDKPaths; + bool fAllowStackExecute; }; @@ -216,14 +260,3 @@ private: #endif // __OPTIONS__ - - - - - - - - - - - diff --git a/src/Readers/ObjectFileArchiveMachO.cpp b/src/Readers/ObjectFileArchiveMachO.cpp deleted file mode 100644 index b01d8a0..0000000 --- a/src/Readers/ObjectFileArchiveMachO.cpp +++ /dev/null @@ -1,324 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * 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 ObjectFileArchiveMachO { - -class Reader : public ObjectFile::Reader -{ -public: - Reader(const uint8_t fileContent[], uint64_t fileLength, const char* path, const ObjectFile::ReaderOptions& options); - virtual ~Reader(); - - virtual const char* getPath(); - virtual std::vector& getAtoms(); - virtual std::vector* getJustInTimeAtomsFor(const char* name); - virtual std::vector* getStabsDebugInfo(); - -private: - class Entry : ar_hdr - { - public: - const char* getName() const; - const uint8_t* getContent() const; - uint32_t getContentSize() const; - const Entry* getNext() const; - private: - bool hasLongName() const; - unsigned int getLongNameSpace() const; - - }; - - const struct ranlib* ranlibBinarySearch(const char* name); - const struct ranlib* ranlibLinearSearch(const char* name); - ObjectFile::Reader* makeObjectReaderForMember(const Entry* member); - void dumpTableOfContents(); - - const char* fPath; - const ObjectFile::ReaderOptions& fOptions; - const uint8_t* fFileContent; - uint64_t fFileLength; - const struct ranlib* fTableOfContents; - uint32_t fTableOfContentCount; - bool fSorted; - const char* fStringPool; - std::vector fAllAtoms; - std::set fInstantiatedEntries; - - static std::vector fgEmptyList; -}; - -std::vector Reader::fgEmptyList; - -#undef SwapArchToHostInt32 -#if defined(ARCH_PPC) || defined(ARCH_PPC64) - #define SwapArchToHostInt32(value) OSSwapBigToHostInt32(value) -#elif defined(ARCH_I386) - #define SwapArchToHostInt32(value) OSSwapLittleToHostInt32(value) -#endif - - - -bool Reader::Entry::hasLongName() const -{ - return ( strncmp(this->ar_name, AR_EFMT1, strlen(AR_EFMT1)) == 0 ); -} - -unsigned int Reader::Entry::getLongNameSpace() const -{ - char* endptr; - long result = strtol(&this->ar_name[strlen(AR_EFMT1)], &endptr, 10); - return result; -} - -const char* Reader::Entry::getName() const -{ - if ( this->hasLongName() ) { - int len = this->getLongNameSpace(); - static char longName[256]; - strncpy(longName, ((char*)this)+sizeof(ar_hdr), len); - longName[len] = '\0'; - return longName; - } - else { - static char shortName[20]; - strncpy(shortName, this->ar_name, 16); - shortName[16] = '\0'; - char* space = strchr(shortName, ' '); - if ( space != NULL ) - *space = '\0'; - return shortName; - } -} - - -const uint8_t* Reader::Entry::getContent() const -{ - if ( this->hasLongName() ) - return ((uint8_t*)this) + sizeof(ar_hdr) + this->getLongNameSpace(); - else - return ((uint8_t*)this) + sizeof(ar_hdr); -} - - -uint32_t Reader::Entry::getContentSize() const -{ - char temp[12]; - strncpy(temp, this->ar_size, 10); - temp[10] = '\0'; - char* endptr; - long size = strtol(temp, &endptr, 10); - // long name is included in ar_size - if ( this->hasLongName() ) - size -= this->getLongNameSpace(); - return size; -} - -const Reader::Entry* Reader::Entry::getNext() const -{ - const uint8_t* p = this->getContent() + getContentSize(); - p = (const uint8_t*)(((uintptr_t)p+3) & (-4)); // 4-byte align - return (Reader::Entry*)p; -} - - - -Reader::Reader(const uint8_t fileContent[], uint64_t fileLength, const char* path, const ObjectFile::ReaderOptions& options) - : fPath(NULL), fOptions(options), fFileContent(NULL), fTableOfContents(NULL), fTableOfContentCount(0), - fSorted(false), fStringPool(NULL) -{ - fPath = strdup(path); - fFileContent = fileContent; - fFileLength = fileLength; - - if ( strncmp((const char*)fileContent, "!\n", 8) != 0 ) - throw "not an archive"; - - if ( !options.fFullyLoadArchives ) { - const Entry* const firstMember = (Entry*)&fFileContent[8]; - if ( strcmp(firstMember->getName(), SYMDEF_SORTED) == 0 ) - fSorted = true; - else if ( strcmp(firstMember->getName(), SYMDEF) == 0 ) - fSorted = false; - else - throw "archive has no table of contents"; - const uint8_t* contents = firstMember->getContent(); - uint32_t ranlibArrayLen = SwapArchToHostInt32(*((uint32_t*)contents)); - fTableOfContents = (const struct ranlib*)&contents[4]; - fTableOfContentCount = ranlibArrayLen / sizeof(struct ranlib); - fStringPool = (const char*)&contents[ranlibArrayLen+8]; - } - - if ( options.fTraceArchives ) - printf("[Logging for Build & Integration] Used static archive: %s\n", fPath); -} - -Reader::~Reader() -{ -} - - - -ObjectFile::Reader* Reader::makeObjectReaderForMember(const Entry* member) -{ - const char* memberName = member->getName(); - char memberPath[strlen(fPath) + strlen(memberName)+4]; - strcpy(memberPath, fPath); - strcat(memberPath, "("); - strcat(memberPath, memberName); - strcat(memberPath, ")"); - //fprintf(stderr, "using %s from %s\n", memberName, fPath); - try { - return ObjectFileMachO::MakeReader((class macho_header*)member->getContent(), memberPath, fOptions); - } - catch (const char* msg) { - throwf("in %s, %s", memberPath, msg); - } -} - -const char* Reader::getPath() -{ - return fPath; -} - -std::vector& Reader::getAtoms() -{ - if ( fOptions.fFullyLoadArchives ) { - // build vector of all atoms from all .o files in this archive - const Entry* const start = (Entry*)&fFileContent[8]; - const Entry* const end = (Entry*)&fFileContent[fFileLength]; - for (const Entry* p=start; p < end; p = p->getNext()) { - const char* memberName = p->getName(); - if ( (p==start) && (strcmp(memberName, SYMDEF_SORTED) == 0) ) - continue; - ObjectFile::Reader* r = this->makeObjectReaderForMember(p); - std::vector& atoms = r->getAtoms(); - fAllAtoms.insert(fAllAtoms.end(), atoms.begin(), atoms.end()); - } - return fAllAtoms; - } - else { - // return nonthing for now, getJustInTimeAtomsFor() will return atoms as needed - return fgEmptyList; - } -} - - -const struct ranlib* Reader::ranlibBinarySearch(const char* key) -{ - const struct ranlib* base = fTableOfContents; - for (uint32_t n = fTableOfContentCount; n > 0; n /= 2) { - const struct ranlib* pivot = &base[n/2]; - const char* pivotStr = &fStringPool[SwapArchToHostInt32(pivot->ran_un.ran_strx)]; - int cmp = strcmp(key, pivotStr); - if ( cmp == 0 ) - return pivot; - if ( cmp > 0 ) { - // key > pivot - // move base to symbol after pivot - base = &pivot[1]; - --n; - } - else { - // key < pivot - // keep same base - } - } - return NULL; -} - -const struct ranlib* Reader::ranlibLinearSearch(const char* key) -{ - for (uint32_t i = 0; i < fTableOfContentCount; ++i) { - const struct ranlib* entry = &fTableOfContents[i]; - const char* entryName = &fStringPool[SwapArchToHostInt32(entry->ran_un.ran_strx)]; - if ( strcmp(key, entryName) == 0 ) - return entry; - } - return NULL; -} - - -void Reader::dumpTableOfContents() -{ - for (unsigned int i=0; i < fTableOfContentCount; ++i) { - const struct ranlib* e = &fTableOfContents[i]; - printf("%s in %s\n", &fStringPool[SwapArchToHostInt32(e->ran_un.ran_strx)], ((Entry*)&fFileContent[SwapArchToHostInt32(e->ran_off)])->getName()); - } -} - -std::vector* Reader::getJustInTimeAtomsFor(const char* name) -{ - if ( fOptions.fFullyLoadArchives ) { - return NULL; - } - else { - const struct ranlib* result = NULL; - if ( fSorted ) { - // do a binary search of table of contents looking for requested symbol - result = ranlibBinarySearch(name); - } - else { - // do a linear search of table of contents looking for requested symbol - result = ranlibLinearSearch(name); - } - if ( result != NULL ) { - const Entry* member = (Entry*)&fFileContent[SwapArchToHostInt32(result->ran_off)]; - if ( fInstantiatedEntries.count(member) == 0 ) { - // only return these atoms once - fInstantiatedEntries.insert(member); - ObjectFile::Reader* r = makeObjectReaderForMember(member); - //fprintf(stderr, "%s found in %s\n", name, member->getName()); - return new std::vector(r->getAtoms()); - } - } - //fprintf(stderr, "%s NOT found in archive %s\n", name, fPath); - return NULL; - } -} - - -std::vector* Reader::getStabsDebugInfo() -{ - return NULL; -} - - - -Reader* MakeReader(const uint8_t fileContent[], uint64_t fileLength, const char* path, const ObjectFile::ReaderOptions& options) -{ - return new Reader(fileContent, fileLength, path, options); -} - - - -}; - - - - - - - diff --git a/src/Readers/ObjectFileDylibMachO.cpp b/src/Readers/ObjectFileDylibMachO.cpp deleted file mode 100644 index fad4dcb..0000000 --- a/src/Readers/ObjectFileDylibMachO.cpp +++ /dev/null @@ -1,485 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * 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 ObjectFileDylibMachO { - -class Reader : public ObjectFile::Reader -{ -public: - Reader(const macho_header* header, const char* path, const ObjectFile::ReaderOptions& options); - virtual ~Reader(); - - virtual const char* getPath(); - virtual std::vector& getAtoms(); - virtual std::vector* getJustInTimeAtomsFor(const char* name); - virtual std::vector* getStabsDebugInfo(); - virtual const char* getInstallPath(); - virtual uint32_t getTimestamp(); - virtual uint32_t getCurrentVersion(); - virtual uint32_t getCompatibilityVersion(); - virtual std::vector* getDependentLibraryPaths(); - virtual bool reExports(ObjectFile::Reader*); - virtual bool isDefinitionWeak(const ObjectFile::Atom&); - -private: - struct CStringComparor - { - bool operator()(const char* left, const char* right) { return (strcmp(left, right) > 0); } - }; - typedef std::map Mapper; - - - void init(const macho_header* header,const char* path); - const macho_nlist* binarySearchWithToc(const char* key, const char stringPool[], const macho_nlist symbols[], const struct dylib_table_of_contents toc[], uint32_t symbolCount); - const macho_nlist* binarySearch(const char* key, const char stringPool[], const macho_nlist symbols[], uint32_t symbolCount); - bool hasExport(const char* name); - const macho_nlist* findExport(const char* name); - - const char* fPath; - const macho_header* fHeader; - const char* fStrings; - const macho_dysymtab_command* fDynamicInfo; - const macho_dylib_command* fDylibID; - const macho_nlist* fSymbols; - uint32_t fSymbolCount; - Mapper fAtoms; - std::vector fReExportedDylibs; - - static std::vector fEmptyAtomList; -}; - -std::vector Reader::fEmptyAtomList; - - -class Segment : public ObjectFile::Segment -{ -public: - Segment(const char* name) { fName = name; } - virtual const char* getName() const { return fName; } - virtual bool isContentReadable() const { return true; } - virtual bool isContentWritable() const { return false; } - virtual bool isContentExecutable() const { return false; } -private: - const char* fName; -}; - - -class ExportAtom : public ObjectFile::Atom -{ -public: - virtual ObjectFile::Reader* getFile() const { return &fOwner; } - virtual const char* getName() const { return fName; } - virtual const char* getDisplayName() const; - virtual Scope getScope() const { return ObjectFile::Atom::scopeGlobal; } - 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 false; } - virtual bool dontStripName() const { return false; } - virtual bool isImportProxy() const { return true; } - virtual uint64_t getSize() const { return 0; } - virtual std::vector& getReferences() const { return fgEmptyReferenceList; } - virtual bool mustRemainInSection() const { return false; } - virtual const char* getSectionName() const { return "._imports"; } - virtual Segment& getSegment() const { return fgImportSegment; } - 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 0; } - virtual WeakImportSetting getImportWeakness() const { return fWeakImportSetting; } - virtual void copyRawContent(uint8_t buffer[]) const {} - virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const {} - - virtual void setScope(Scope) { } - virtual void setImportWeakness(bool weakImport) { fWeakImportSetting = weakImport ? kWeakImport : kNonWeakImport; } - -protected: - friend class Reader; - - ExportAtom(Reader& owner, const char* name) : fOwner(owner), fName(name), fWeakImportSetting(kWeakUnset) {} - virtual ~ExportAtom() {} - - Reader& fOwner; - const char* fName; - WeakImportSetting fWeakImportSetting; - - static std::vector fgEmptyReferenceList; - static Segment fgImportSegment; -}; - -Segment ExportAtom::fgImportSegment("__LINKEDIT"); -std::vector ExportAtom::fgEmptyReferenceList; - -const char* ExportAtom::getDisplayName() const -{ - static char temp[300]; - strcpy(temp, fName); - strcat(temp, "$import"); - return temp; -} - - - -Reader::Reader(const macho_header* header, const char* path, const ObjectFile::ReaderOptions& options) - : fHeader(header), fStrings(NULL), fDylibID(NULL), fSymbols(NULL), fSymbolCount(0) -{ - typedef std::pair DylibAndReExportFlag; - std::vector dependentDylibs; - - fPath = strdup(path); - const uint32_t cmd_count = header->ncmds(); - const macho_load_command* const cmds = (macho_load_command*)((char*)header + macho_header::size); - // get all dylib load commands - const macho_load_command* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd()) { - case LC_LOAD_DYLIB: - case LC_LOAD_WEAK_DYLIB: - { - DylibAndReExportFlag info; - info.first = (struct macho_dylib_command*)cmd; - info.second = options.fFlatNamespace; - dependentDylibs.push_back(info); - } - break; - } - cmd = (const macho_load_command*)(((char*)cmd)+cmd->cmdsize()); - } - - // cache interesting pointers - cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd()) { - case LC_SYMTAB: - { - const macho_symtab_command* symtab = (macho_symtab_command*)cmd; - fSymbolCount = symtab->nsyms(); - fSymbols = (const macho_nlist*)((char*)header + symtab->symoff()); - fStrings = (char*)header + symtab->stroff(); - } - break; - case LC_DYSYMTAB: - fDynamicInfo = (macho_dysymtab_command*)cmd; - break; - case LC_ID_DYLIB: - fDylibID = (macho_dylib_command*)cmd; - break; - case LC_SUB_UMBRELLA: - if ( !options.fFlatNamespace ) { - const char* frameworkLeafName = ((macho_sub_umbrella_command*)cmd)->name(); - for (std::vector::iterator it = dependentDylibs.begin(); it != dependentDylibs.end(); it++) { - const char* dylibName = it->first->name(); - const char* lastSlash = strrchr(dylibName, '/'); - if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], frameworkLeafName) == 0) ) - it->second = true; - } - } - break; - case LC_SUB_LIBRARY: - if ( !options.fFlatNamespace ) { - const char* dylibBaseName = ((macho_sub_library_command*)cmd)->name(); - for (std::vector::iterator it = dependentDylibs.begin(); it != dependentDylibs.end(); it++) { - const char* dylibName = it->first->name(); - const char* lastSlash = strrchr(dylibName, '/'); - const char* leafStart = &lastSlash[1]; - if ( lastSlash == NULL ) - leafStart = dylibName; - const char* firstDot = strchr(leafStart, '.'); - int len = strlen(leafStart); - if ( firstDot != NULL ) - len = firstDot - leafStart; - if ( strncmp(leafStart, dylibBaseName, len) == 0 ) - it->second = true; - } - } - break; - } - cmd = (const macho_load_command*)(((char*)cmd)+cmd->cmdsize()); - } - - // load dylibs we need to re-export - for (std::vector::iterator it = dependentDylibs.begin(); it != dependentDylibs.end(); it++) { - if ( it->second ) { - // printf("%s need to re-export %s\n", path, it->first->name()); - //fReExportedDylibs.push_back( - } - } -} - - -Reader::~Reader() -{ -} - -const char* Reader::getPath() -{ - return fPath; -} - - -std::vector& Reader::getAtoms() -{ - return fEmptyAtomList; -} - - - -const macho_nlist* Reader::binarySearchWithToc(const char* key, const char stringPool[], const macho_nlist symbols[], - const struct dylib_table_of_contents toc[], uint32_t symbolCount) -{ - int32_t high = symbolCount-1; - int32_t mid = symbolCount/2; - - for (int32_t low = 0; low <= high; mid = (low+high)/2) { - const uint32_t index = ENDIAN_READ32(toc[mid].symbol_index); - const macho_nlist* pivot = &symbols[index]; - const char* pivotStr = &stringPool[pivot->n_strx()]; - int cmp = strcmp(key, pivotStr); - if ( cmp == 0 ) - return pivot; - if ( cmp > 0 ) { - // key > pivot - low = mid + 1; - } - else { - // key < pivot - high = mid - 1; - } - } - return NULL; -} - - -const macho_nlist* Reader::binarySearch(const char* key, const char stringPool[], const macho_nlist symbols[], uint32_t symbolCount) -{ - const macho_nlist* base = symbols; - for (uint32_t n = symbolCount; n > 0; n /= 2) { - const macho_nlist* pivot = &base[n/2]; - const char* pivotStr = &stringPool[pivot->n_strx()]; - int cmp = strcmp(key, pivotStr); - if ( cmp == 0 ) - return pivot; - if ( cmp > 0 ) { - // key > pivot - // move base to symbol after pivot - base = &pivot[1]; - --n; - } - else { - // key < pivot - // keep same base - } - } - return NULL; -} - -const macho_nlist* Reader::findExport(const char* name) -{ - if ( fDynamicInfo->tocoff() == 0 ) - return binarySearch(name, fStrings, &fSymbols[fDynamicInfo->iextdefsym()], fDynamicInfo->nextdefsym()); - else { - return binarySearchWithToc(name, fStrings, fSymbols, (dylib_table_of_contents*)((char*)fHeader + fDynamicInfo->tocoff()), - fDynamicInfo->nextdefsym()); - } -} - -bool Reader::hasExport(const char* name) -{ - return ( findExport(name) != NULL ); -} - -std::vector* Reader::getJustInTimeAtomsFor(const char* name) -{ - std::vector* atoms = NULL; - // search exports - if ( this->hasExport(name) ) { - // see if this atom already synthesized - ObjectFile::Atom* atom = NULL; - Mapper::iterator pos = fAtoms.find(name); - if ( pos != fAtoms.end() ) { - atom = pos->second; - } - else { - atom = new ExportAtom(*this, name); - fAtoms[name] = atom; - } - // return a vector of one atom - atoms = new std::vector; - atoms->push_back(atom); - return atoms; - } - - // check re-exports - for (std::vector::iterator it = fReExportedDylibs.begin(); it != fReExportedDylibs.end(); it++) { - Reader* reExportedReader = *it; - atoms = reExportedReader->getJustInTimeAtomsFor(name); - if ( atoms != NULL ) - return atoms; - } - - return NULL; -} - - -std::vector* Reader::getStabsDebugInfo() -{ - return NULL; -} - -const char* Reader::getInstallPath() -{ - return fDylibID->name(); -} - -uint32_t Reader::getTimestamp() -{ - return fDylibID->timestamp(); -} - -uint32_t Reader::getCurrentVersion() -{ - return fDylibID->current_version(); -} - -uint32_t Reader::getCompatibilityVersion() -{ - return fDylibID->compatibility_version(); -} - -std::vector* Reader::getDependentLibraryPaths() -{ - std::vector* result = new std::vector; - const uint32_t cmd_count = fHeader->ncmds(); - const macho_load_command* const cmds = (macho_load_command*)((char*)fHeader + macho_header::size); - const macho_load_command* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd()) { - case LC_LOAD_DYLIB: - case LC_LOAD_WEAK_DYLIB: - { - result->push_back(((struct macho_dylib_command*)cmd)->name()); - } - break; - } - cmd = (const macho_load_command*)(((char*)cmd)+cmd->cmdsize()); - } - return result; -} - -bool Reader::reExports(ObjectFile::Reader* child) -{ - // A dependent dylib is re-exported under two conditions: - // 1) parent contains LC_SUB_UMBRELLA or LC_SUB_LIBRARY with child name - { - const uint32_t cmd_count = fHeader->ncmds(); - const macho_load_command* const cmds = (macho_load_command*)((char*)fHeader + macho_header::size); - const macho_load_command* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd()) { - case LC_SUB_UMBRELLA: - { - const char* frameworkLeafName = ((macho_sub_umbrella_command*)cmd)->name(); - const char* dylibName = child->getPath(); - const char* lastSlash = strrchr(dylibName, '/'); - if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], frameworkLeafName) == 0) ) - return true; - } - break; - case LC_SUB_LIBRARY: - { - const char* dylibBaseName = ((macho_sub_library_command*)cmd)->name(); - const char* dylibName = child->getPath(); - const char* lastSlash = strrchr(dylibName, '/'); - const char* leafStart = &lastSlash[1]; - if ( lastSlash == NULL ) - leafStart = dylibName; - const char* firstDot = strchr(leafStart, '.'); - int len = strlen(leafStart); - if ( firstDot != NULL ) - len = firstDot - leafStart; - if ( strncmp(leafStart, dylibBaseName, len) == 0 ) - return true; - } - break; - } - cmd = (const macho_load_command*)(((char*)cmd)+cmd->cmdsize()); - } - } - - // 2) child contains LC_SUB_FRAMEWORK with parent name - { - const uint32_t cmd_count = ((Reader*)child)->fHeader->ncmds(); - const macho_load_command* const cmds = (macho_load_command*)((char*)(((Reader*)child)->fHeader) + macho_header::size); - const macho_load_command* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd()) { - case LC_SUB_FRAMEWORK: - { - const char* frameworkLeafName = ((macho_sub_framework_command*)cmd)->name(); - const char* parentName = this->getPath(); - const char* lastSlash = strrchr(parentName, '/'); - if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], frameworkLeafName) == 0) ) - return true; - } - break; - } - cmd = (const macho_load_command*)(((char*)cmd)+cmd->cmdsize()); - } - } - - - return false; -} - -bool Reader::isDefinitionWeak(const ObjectFile::Atom& atom) -{ - const macho_nlist* sym = findExport(atom.getName()); - if ( sym != NULL ) { - if ( (sym->n_desc() & N_WEAK_DEF) != 0 ) - return true; - } - return false; -} - - - -Reader* MakeReader(const macho_header* mh, const char* path, const ObjectFile::ReaderOptions& options) -{ - return new Reader(mh, path, options); -} - - - -}; - - - - - - - diff --git a/src/Readers/ObjectFileMachO-all.cpp b/src/Readers/ObjectFileMachO-all.cpp deleted file mode 100644 index 736b2a6..0000000 --- a/src/Readers/ObjectFileMachO-all.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* - * 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@ - */ - - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "ObjectFile.h" -#include "Options.h" - - - - -// buiild Writer for -arch ppc64 -#undef MACHO_32_SAME_ENDIAN -#undef MACHO_32_OPPOSITE_ENDIAN -#undef MACHO_64_SAME_ENDIAN -#undef MACHO_64_OPPOSITE_ENDIAN -#if __ppc__ || __ppc64__ - #define MACHO_64_SAME_ENDIAN -#elif __i386__ - #define MACHO_64_OPPOSITE_ENDIAN -#else - #error unknown architecture -#endif -namespace ppc64 { - #undef ARCH_PPC - #define ARCH_PPC64 - #undef ARCH_I386 - #include "MachOAbstraction.h" - #include "ObjectFileMachO.cpp" - #include "ObjectFileDylibMachO.cpp" - #include "ObjectFileArchiveMachO.cpp" -}; - -// buiild Writer for -arch ppc -#undef MACHO_32_SAME_ENDIAN -#undef MACHO_32_OPPOSITE_ENDIAN -#undef MACHO_64_SAME_ENDIAN -#undef MACHO_64_OPPOSITE_ENDIAN -#if __ppc__ || __ppc64__ - #define MACHO_32_SAME_ENDIAN -#elif __i386__ - #define MACHO_32_OPPOSITE_ENDIAN -#else - #error unknown architecture -#endif -namespace ppc { - #define ARCH_PPC - #undef ARCH_PPC64 - #undef ARCH_I386 - #include "MachOAbstraction.h" - #include "ObjectFileMachO.cpp" - #include "ObjectFileDylibMachO.cpp" - #include "ObjectFileArchiveMachO.cpp" -}; - - -// buiild Writer for -arch i386 -#undef MACHO_32_SAME_ENDIAN -#undef MACHO_32_OPPOSITE_ENDIAN -#undef MACHO_64_SAME_ENDIAN -#undef MACHO_64_OPPOSITE_ENDIAN -#if __ppc__ || __ppc64__ - #define MACHO_32_OPPOSITE_ENDIAN -#elif __i386__ - #define MACHO_32_SAME_ENDIAN -#else - #error unknown architecture -#endif -#undef i386 // compiler sometimes #defines this -namespace i386 { - #undef ARCH_PPC - #undef ARCH_PPC64 - #define ARCH_I386 - #include "MachOAbstraction.h" - #include "ObjectFileMachO.cpp" - #include "ObjectFileDylibMachO.cpp" - #include "ObjectFileArchiveMachO.cpp" -}; - - diff --git a/src/Readers/ObjectFileMachO-all.h b/src/Readers/ObjectFileMachO-all.h deleted file mode 100644 index 0f27244..0000000 --- a/src/Readers/ObjectFileMachO-all.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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@ - */ - - -#ifndef __OBJECTFILEMACHO__ -#define __OBJECTFILEMACHO__ - -class Options; - -namespace ppc { - class macho_header; - namespace ObjectFileMachO { - extern class ObjectFile::Reader* MakeReader(const macho_header*, const char* path, const ObjectFile::ReaderOptions& options); - }; - namespace ObjectFileDylibMachO { - extern class ObjectFile::Reader* MakeReader(const macho_header*, const char* path, const ObjectFile::ReaderOptions& options); - }; - namespace ObjectFileArchiveMachO { - extern class ObjectFile::Reader* MakeReader(const uint8_t fileContent[], uint64_t fileLength, const char* path, const ObjectFile::ReaderOptions& options); - }; -}; - -namespace ppc64 { - class macho_header; - namespace ObjectFileMachO { - extern class ObjectFile::Reader* MakeReader(const macho_header*, const char* path, const ObjectFile::ReaderOptions& options); - }; - namespace ObjectFileDylibMachO { - extern class ObjectFile::Reader* MakeReader(const macho_header*, const char* path, const ObjectFile::ReaderOptions& options); - }; - namespace ObjectFileArchiveMachO { - extern class ObjectFile::Reader* MakeReader(const uint8_t fileContent[], uint64_t fileLength, const char* path, const ObjectFile::ReaderOptions& options); - }; -}; - -#undef i386 // compiler sometimes #defines this -namespace i386 { - class macho_header; - namespace ObjectFileMachO { - extern class ObjectFile::Reader* MakeReader(const macho_header*, const char* path, const ObjectFile::ReaderOptions& options); - }; - namespace ObjectFileDylibMachO { - extern class ObjectFile::Reader* MakeReader(const macho_header*, const char* path, const ObjectFile::ReaderOptions& options); - }; - namespace ObjectFileArchiveMachO { - extern class ObjectFile::Reader* MakeReader(const uint8_t fileContent[], uint64_t fileLength, const char* path, const ObjectFile::ReaderOptions& options); - }; -}; - - - -#endif // __OBJECTFILEMACHO__ - - - diff --git a/src/Readers/ObjectFileMachO.cpp b/src/Readers/ObjectFileMachO.cpp deleted file mode 100644 index 154ff8b..0000000 --- a/src/Readers/ObjectFileMachO.cpp +++ /dev/null @@ -1,2376 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * 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 ObjectFileMachO { - -class Reference : public ObjectFile::Reference -{ -public: - Reference(macho_uintptr_t fixUpOffset, Kind kind, const char* targetName, uint64_t offsetInTarget, uint64_t offsetInFromTarget); - Reference(macho_uintptr_t fixUpOffset, Kind kind, class Atom& target, uint64_t offsetInTarget, uint64_t offsetInFromTarget); - Reference(macho_uintptr_t fixUpOffset, Kind kind, class Atom& target, uint64_t offsetInTarget, class Atom& fromTarget, uint64_t offsetInFromTarget); - virtual ~Reference(); - - - virtual bool isTargetUnbound() const; - virtual bool isFromTargetUnbound() const; - virtual bool isWeakReference() const; - virtual bool requiresRuntimeFixUp(bool slideable) const; - virtual bool isLazyReference() const; - virtual Kind getKind() const; - virtual uint64_t getFixUpOffset() const; - virtual const char* getTargetName() const; - virtual ObjectFile::Atom& getTarget() const; - virtual uint64_t getTargetOffset() const; - virtual bool hasFromTarget() const; - virtual ObjectFile::Atom& getFromTarget() const; - virtual const char* getFromTargetName() const; - virtual void setTarget(ObjectFile::Atom&, uint64_t offset); - virtual void setFromTarget(ObjectFile::Atom&); - virtual void setFromTargetName(const char*); - virtual void setFromTargetOffset(uint64_t); - virtual const char* getDescription() const; - virtual uint64_t getFromTargetOffset() const; - - void setLazy(bool); - void setWeak(bool); -private: - ObjectFile::Atom* fTarget; - ObjectFile::Atom* fFromTarget; - const char* fTargetName; - const char* fFromTargetName; - macho_uintptr_t fTargetOffset; - macho_uintptr_t fFromTargetOffset; - macho_uintptr_t fFixUpOffsetInSrc; - Kind fKind; - bool fLazy; - bool fWeak; -}; - - - -class Reader : public ObjectFile::Reader -{ -public: - Reader(const char* path); - Reader(const macho_header* header, const char* path, const ObjectFile::ReaderOptions& options); - virtual ~Reader(); - - virtual const char* getPath(); - virtual std::vector& getAtoms(); - virtual std::vector* getJustInTimeAtomsFor(const char* name); - virtual std::vector* getStabsDebugInfo(); // stabs info not associated with an atom - - -private: - friend class Atom; - void init(const macho_header* header, const char* path); - void buildOffsetsSet(const macho_relocation_info* reloc, const macho_section* sect, std::set& offsets, std::set& dontUse); - void addRelocReference(const macho_section* sect, const macho_relocation_info* reloc); - Atom* findAtomCoveringOffset(uint32_t offset); - uint32_t findAtomIndex(const Atom& atom); - void addFixUp(uint32_t srcAddr, uint32_t dstAddr, Reference::Kind kind, uint32_t picBaseAddr); - class Segment* makeSegmentFromSection(const macho_section*); - macho_uintptr_t commonsOffset(); - void insertOffsetIfText(std::set& offsets, uint32_t value); - void insertOffsetIfNotText(std::set& offsets, uint32_t value); - const macho_section* findSectionCoveringOffset(uint32_t offset); - void addCallSiteReference(Atom& src, uint32_t offsetInSrcAtom, Reference::Kind kind, Atom& target, uint32_t picBaseOffset, uint32_t offsetInTargetAtom); - void deadStub(Atom& target); - - const char* fPath; - const ObjectFile::ReaderOptions& fOptions; - const macho_header* fHeader; - const char* fStrings; - const macho_nlist* fSymbols; - uint32_t fSymbolCount; - const macho_segment_command* fSegment; - const uint32_t* fIndirectTable; - std::vector fAtoms; - std::vector fSegments; - std::set fDeadAtoms; - uint32_t fNonAtomStabsStartIndex; - uint32_t fNonAtomStabsCount; - std::vector fNonAtomExtras; -}; - -class Segment : public ObjectFile::Segment -{ -public: - virtual const char* getName() const; - virtual bool isContentReadable() const; - virtual bool isContentWritable() const; - virtual bool isContentExecutable() const; -protected: - Segment(const macho_section*); - friend class Reader; -private: - const macho_section* fSection; -}; - -class Atom : public ObjectFile::Atom -{ -public: - virtual ObjectFile::Reader* getFile() const; - virtual const char* getName() const; - virtual const char* getDisplayName() const; - virtual Scope getScope() const; - virtual bool isTentativeDefinition() const; - virtual bool isWeakDefinition() const; - virtual bool isCoalesableByName() const; - virtual bool isCoalesableByValue() const; - virtual bool isZeroFill() const; - virtual bool dontDeadStrip() const; - virtual bool dontStripName() const; // referenced dynamically - virtual bool isImportProxy() const; - virtual uint64_t getSize() const; - virtual std::vector& getReferences() const; - virtual bool mustRemainInSection() const; - virtual const char* getSectionName() const; - virtual Segment& getSegment() const; - virtual bool requiresFollowOnAtom() const; - virtual ObjectFile::Atom& getFollowOnAtom() const; - virtual std::vector* getStabsDebugInfo() const; - virtual uint8_t getAlignment() const; - virtual WeakImportSetting getImportWeakness() const { return ObjectFile::Atom::kWeakUnset; } - virtual void copyRawContent(uint8_t buffer[]) const; - virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const; - virtual void setScope(Scope); - virtual void setImportWeakness(bool weakImport) { } - - bool isLazyStub(); - -protected: - friend class Reader; - Atom(Reader&, const macho_nlist*); - Atom(Reader&, uint32_t offset); - virtual ~Atom(); - - const macho_section* findSectionFromOffset(uint32_t offset); - const macho_section* getCommonsSection(); - void setSize(macho_uintptr_t); - void setFollowOnAtom(Atom&); - static bool atomCompare(const Atom* lhs, const Atom* rhs); - Reference* addDirectReference(macho_uintptr_t offsetInSrcAtom, Reference::Kind kind, Atom& target, uint64_t offsetInTarget, uint64_t offsetInFromTarget); - Reference* addByNameReference(macho_uintptr_t offsetInSrcAtom, Reference::Kind kind, const char* targetName, uint64_t offsetInTarget, uint64_t offsetInFromTarget); - Reference* addDifferenceReference(macho_uintptr_t offsetInSrcAtom, Reference::Kind kind, Atom& target, uint64_t offsetInTarget, Atom& fromTarget, uint64_t offsetInFromTarget); - Reference* addReference(macho_uintptr_t offsetInSrcAtom, Reference::Kind kind, Atom& target, uint64_t offsetInTarget, uint64_t offsetInFromTarget); - - Reader& fOwner; - const macho_nlist* fSymbol; - macho_uintptr_t fOffset; - macho_uintptr_t fSize; - const macho_section* fSection; - Segment* fSegment; - const char* fSynthesizedName; - std::vector fReferences; - ObjectFile::Atom::Scope fScope; - uint32_t fStabsStartIndex; - uint32_t fStabsCount; - - static macho_section fgCommonsSection; // for us by tentative definitions -}; - - -Reader* MakeReader(const macho_header* mh, const char* path, const ObjectFile::ReaderOptions& options) -{ - return new Reader(mh, path, options); -} - - -Reader::Reader(const macho_header* header, const char* path, const ObjectFile::ReaderOptions& options) - : fPath(NULL), fOptions(options), fHeader(NULL), fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fSegment(NULL) -{ - init(header, path); -} - -Reader::Reader(const char* path) - : fPath(NULL), fOptions(*(new ObjectFile::ReaderOptions())), fHeader(NULL), fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fSegment(NULL), - fIndirectTable(NULL), fNonAtomStabsStartIndex(0), fNonAtomStabsCount(0) -{ - struct stat stat_buf; - - int fd = ::open(path, O_RDONLY, 0); - ::fstat(fd, &stat_buf); - void* p = ::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE, fd, 0); - ::close(fd); - if ( ((macho_header*)p)->magic() == MH_MAGIC ) { - init((macho_header*)p, path); - return; - } - throw "add fat handling"; -} - - -Reader::~Reader() -{ -} - - -bool Atom::atomCompare(const Atom* lhs, const Atom* rhs) -{ - return lhs->fOffset < rhs->fOffset; -} - - - -void Reader::init(const macho_header* header, const char* path) -{ - // sanity check -#if defined(ARCH_PPC) - if ( (header->magic() != MH_MAGIC) || (header->cputype() != CPU_TYPE_POWERPC) ) - throw "not a valid ppc mach-o file"; -#elif defined(ARCH_I386) - if ( (header->magic() != MH_MAGIC) || (header->cputype() != CPU_TYPE_I386) ) - throw "not a valid i386 mach-o file"; -#elif defined(ARCH_PPC64) - if ( (header->magic() != MH_MAGIC_64) || (header->cputype() != CPU_TYPE_POWERPC64) ) - throw "not a valid ppc64 mach-o file"; -#endif - - // cache intersting pointers - fPath = strdup(path); - fHeader = header; - const uint32_t cmd_count = header->ncmds(); - const macho_load_command* const cmds = (macho_load_command*)((char*)header + macho_header::size); - const macho_load_command* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd()) { - case LC_SYMTAB: - { - const macho_symtab_command* symtab = (macho_symtab_command*)cmd; - fSymbolCount = symtab->nsyms(); - fSymbols = (const macho_nlist*)((char*)header + symtab->symoff()); - fStrings = (char*)header + symtab->stroff(); - } - break; - case LC_DYSYMTAB: - { - const macho_dysymtab_command* dsymtab = (struct macho_dysymtab_command*)cmd; - fIndirectTable = (uint32_t*)((char*)fHeader + dsymtab->indirectsymoff()); - } - break; - default: - if ( cmd->cmd() == macho_segment_command::command ) { - fSegment= (macho_segment_command*)cmd; - } - break; - } - cmd = (const macho_load_command*)(((char*)cmd)+cmd->cmdsize()); - } - - // add all atoms that have entries in symbol table - std::set symbolAtomOffsets; - for (uint32_t i=0; i < fSymbolCount; ++i) { - const macho_nlist& sym = fSymbols[i]; - if ( (sym.n_type() & N_STAB) == 0 ) { - uint8_t type = (sym.n_type() & N_TYPE); - if ( (type == N_SECT) || ((type == N_UNDF) && (sym.n_value() != 0)) ) { - // real definition or "tentative definition" - Atom* newAtom = new Atom(*this, &sym); - fAtoms.push_back(newAtom); - symbolAtomOffsets.insert(newAtom->fOffset); - } - } - } - - // add all points referenced in relocations - const macho_section* const sectionsStart = (macho_section*)((char*)fSegment + sizeof(macho_segment_command)); - const macho_section* const sectionsEnd = §ionsStart[fSegment->nsects()]; - std::set cleavePoints; - std::set dontCleavePoints; - for (const macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { - const macho_relocation_info* relocs = (macho_relocation_info*)((char*)(fHeader) + sect->reloff()); - const uint32_t relocCount = sect->nreloc(); - for (uint32_t r = 0; r < relocCount; ++r) { - buildOffsetsSet(&relocs[r], sect, cleavePoints, dontCleavePoints); - } - } - // add all stub functions and (non)lazy pointers - std::set deadStubOffsets; - for (const macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { - uint8_t type (sect->flags() & SECTION_TYPE); - switch ( type ) { - case S_SYMBOL_STUBS: - { - const uint32_t stubSize = sect->reserved2(); - // TVector glue sections are marked as S_SYMBOL_STUBS but are only 8 bytes - if ( stubSize > 8 ) { - for(uint32_t sectOffset=0; sectOffset < sect->size(); sectOffset += stubSize) { - uint32_t stubAddr = sect->addr() + sectOffset; - if ( cleavePoints.count(stubAddr) == 0 ) { - cleavePoints.insert(stubAddr); - deadStubOffsets.insert(stubAddr); - } - } - } - } - break; - case S_NON_LAZY_SYMBOL_POINTERS: - case S_LAZY_SYMBOL_POINTERS: - for(uint32_t sectOffset=0; sectOffset < sect->size(); sectOffset += sizeof(macho_uintptr_t)) { - uint32_t pointerAddr = sect->addr() + sectOffset; - cleavePoints.insert(pointerAddr); - } - break; - } - // also make sure each section break is a cleave point - if ( sect->size() != 0 ) - cleavePoints.insert(sect->addr()); - } - - for (std::set::iterator it=cleavePoints.begin(); it != cleavePoints.end(); it++) { - uint32_t cleavePoint = *it; - //printf("cleave offset 0x%08X, don't cleave=%d, isSymbol=%d\n", cleavePoint, dontCleavePoints.count(cleavePoint), symbolAtomOffsets.count(cleavePoint)); - // only create an atom if it is not a don't-cleave point and there is not already an atom at this offset - if ( (dontCleavePoints.count(cleavePoint) == 0) && (symbolAtomOffsets.count(cleavePoint) == 0) ) - fAtoms.push_back(new Atom(*this, cleavePoint)); - } - - const uint32_t atomCount = fAtoms.size(); - if ( atomCount > 0 ) { - // sort the atoms so the occur in source file order - std::sort(fAtoms.begin(), fAtoms.end(), Atom::atomCompare); - - // tell each atom its size and follow on - const bool dontDeadStrip = ((fHeader->flags() & MH_SUBSECTIONS_VIA_SYMBOLS) == 0); - Atom* lastAtom = fAtoms[0]; - for (uint32_t i=1; i < atomCount; ++i) { - Atom* thisAtom = fAtoms[i]; - if ( lastAtom->getSize() == 0 ) { - if ( lastAtom->fSection == thisAtom->fSection ) - lastAtom->setSize(thisAtom->fOffset - lastAtom->fOffset); - else - lastAtom->setSize(lastAtom->fSection->addr() + lastAtom->fSection->size() - lastAtom->fOffset); - } - if ( dontDeadStrip ) - lastAtom->setFollowOnAtom(*thisAtom); - lastAtom = thisAtom; - } - lastAtom = fAtoms[atomCount-1]; - if ( lastAtom->getSize() == 0 ) - lastAtom->setSize(lastAtom->fSection->addr() + lastAtom->fSection->size() - lastAtom->fOffset); - - // add relocation based references - for (const macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { - const macho_relocation_info* relocs = (macho_relocation_info*)((char*)(fHeader) + sect->reloff()); - const uint32_t relocCount = sect->nreloc(); - for (uint32_t r = 0; r < relocCount; ++r) { - addRelocReference(sect, &relocs[r]); - } - } - - // add dead stubs to list to delete - for (std::set::iterator it=deadStubOffsets.begin(); it != deadStubOffsets.end(); it++) { - Atom* deadStub = findAtomCoveringOffset(*it); - this->deadStub(*deadStub); - } - - // remove dead stubs and lazy pointers - for (std::set::iterator deadIt=fDeadAtoms.begin(); deadIt != fDeadAtoms.end(); deadIt++) { - for (std::vector::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) { - if ( *deadIt == *it ) { - fAtoms.erase(it); - break; - } - } - } - - } - - // process stabs debugging info - if ( ! fOptions.fStripDebugInfo ) { - // scan symbol table for stabs entries - fNonAtomStabsStartIndex = 0xFFFFFFFF; - fNonAtomStabsCount = 0; - uint32_t possibleStart = 0; - Atom* atom = NULL; - const uint32_t atomCount = fAtoms.size(); - enum { start, inBeginEnd, foundFirst, inFunction } state = start; - for (uint32_t symbolIndex = 0; symbolIndex < fSymbolCount; ++symbolIndex ) { - const macho_nlist* sym = &fSymbols[symbolIndex]; - uint8_t type = sym->n_type(); - if ( (type & N_STAB) != 0 ) { - if ( fNonAtomStabsStartIndex == 0xFFFFFFFF ) - fNonAtomStabsStartIndex = symbolIndex; - switch (state) { - case start: - if ( (type == N_SLINE) || (type == N_SOL) ) { - possibleStart = symbolIndex; - state = foundFirst; - } - else if ( type == N_BNSYM ) { - macho_uintptr_t targetAddr = sym->n_value(); - atom = this->findAtomCoveringOffset(targetAddr); - if ( (atom != NULL) || (atom->fOffset == targetAddr) ) { - atom->fStabsStartIndex = symbolIndex; - if ( fNonAtomStabsCount == 0 ) - fNonAtomStabsCount = symbolIndex - fNonAtomStabsStartIndex; - } - else { - fprintf(stderr, "can't find atom for stabs 0x%02X at %08X in %s\n", type, targetAddr, path); - atom = NULL; - } - state = inBeginEnd; - } - else if ( (type == N_STSYM) || (type == N_LCSYM) ) { - macho_uintptr_t targetAddr = sym->n_value(); - atom = this->findAtomCoveringOffset(targetAddr); - if ( (atom != NULL) || (atom->fOffset == targetAddr) ) { - atom->fStabsStartIndex = symbolIndex; - atom->fStabsCount = 1; - if ( fNonAtomStabsCount == 0 ) - fNonAtomStabsCount = symbolIndex - fNonAtomStabsStartIndex; - } - else { - fprintf(stderr, "can't find atom for stabs 0x%02X at %08X in %s\n", type, targetAddr, path); - atom = NULL; - } - } - else if ( type == N_GSYM ) { - // n_value field is NOT atom address ;-( - // need to find atom by name match - const char* symString = &fStrings[sym->n_strx()]; - const char* colon = strchr(symString, ':'); - bool found = false; - if ( colon != NULL ) { - int nameLen = colon - symString; - for (uint32_t searchIndex = 0; searchIndex < atomCount; ++searchIndex) { - const char* atomName = fAtoms[searchIndex]->getName(); - if ( (atomName != NULL) && (strncmp(&atomName[1], symString, nameLen) == 0) ) { - atom = fAtoms[searchIndex]; - atom->fStabsStartIndex = symbolIndex; - atom->fStabsCount = 1; - if ( fNonAtomStabsCount == 0 ) - fNonAtomStabsCount = symbolIndex - fNonAtomStabsStartIndex; - found = true; - break; - } - } - } - if ( !found ) { - fprintf(stderr, "can't find atom for N_GSYM stabs %s in %s\n", symString, path); - atom = NULL; - } - } - else if ( type == N_LSYM ) { - if ( fNonAtomStabsCount != 0 ) { - // built with -gfull and some type definition not at start of source - fNonAtomExtras.push_back(symbolIndex); - } - } - break; - case inBeginEnd: - if ( type == N_ENSYM ) { - state = start; - if ( atom != NULL ) - atom->fStabsCount = symbolIndex - atom->fStabsStartIndex + 1; - } - break; - case foundFirst: - if ( (type == N_FUN) && (sym->n_sect() != 0) ) { - state = inFunction; - macho_uintptr_t targetAddr = sym->n_value(); - atom = this->findAtomCoveringOffset(targetAddr); - if ( (atom == NULL) || (atom->fOffset != targetAddr) ) { - fprintf(stderr, "can't find atom for stabs FUN index: %d at 0x%08llX in %s\n", symbolIndex, (uint64_t)targetAddr, path); - atom = NULL; - } - else { - atom->fStabsStartIndex = possibleStart; - if ( fNonAtomStabsCount == 0 ) - fNonAtomStabsCount = possibleStart - fNonAtomStabsStartIndex; - } - } - else if ( (type == N_FUN) && (sym->n_sect() == 0) ) { - fprintf(stderr, "end stab FUN found without start FUN, index=%d in %s\n", symbolIndex, path); - state = start; - } - break; - case inFunction: - if ( (type == N_FUN) && (sym->n_sect() == 0) ) { - state = start; - if ( atom != NULL ) - atom->fStabsCount = symbolIndex - atom->fStabsStartIndex + 1; - } - break; - } - } - } - - } -} - - -void Reader::addRelocReference(const macho_section* sect, const macho_relocation_info* reloc) -{ - uint32_t srcAddr; - uint32_t dstAddr; - Atom* src; - Atom* dst; -#if defined(ARCH_PPC) || defined(ARCH_PPC64) - uint32_t instruction; -#endif - uint32_t* fixUpPtr; - if ( (reloc->r_address() & R_SCATTERED) == 0 ) { - fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address()); -#if defined(ARCH_PPC) || defined(ARCH_PPC64) - const macho_relocation_info* nextReloc = &reloc[1]; -#endif - switch ( reloc->r_type() ) { -#if defined(ARCH_PPC) || defined(ARCH_PPC64) - case PPC_RELOC_BR24: - { - if ( reloc->r_extern() ) { - instruction = OSSwapBigToHostInt32(*fixUpPtr); - int32_t displacement = (instruction & 0x03FFFFFC); - if ( (displacement & 0x02000000) != 0 ) - displacement |= 0xFC000000; - uint32_t offsetInTarget = sect->addr() + reloc->r_address() + displacement; - srcAddr = sect->addr() + reloc->r_address(); - src = findAtomCoveringOffset(srcAddr); - const macho_nlist* targetSymbol = &fSymbols[reloc->r_symbolnum()]; - const char* targetName = &fStrings[targetSymbol->n_strx()]; - src->addByNameReference(srcAddr - src->fOffset, Reference::ppcFixupBranch24, targetName, offsetInTarget, 0); - } - else { - instruction = OSSwapBigToHostInt32(*fixUpPtr); - if ( (instruction & 0x4C000000) == 0x48000000 ) { - int32_t displacement = (instruction & 0x03FFFFFC); - if ( (displacement & 0x02000000) != 0 ) - displacement |= 0xFC000000; - srcAddr = sect->addr() + reloc->r_address(); - dstAddr = srcAddr + displacement; - src = findAtomCoveringOffset(srcAddr); - dst = findAtomCoveringOffset(dstAddr); - this->addCallSiteReference(*src, srcAddr - src->fOffset, Reference::ppcFixupBranch24, *dst, 0, dstAddr - dst->fOffset); - } - } - } - break; - case PPC_RELOC_BR14: - { - if ( reloc->r_extern() ) { - srcAddr = sect->addr() + reloc->r_address(); - src = findAtomCoveringOffset(srcAddr); - const macho_nlist* targetSymbol = &fSymbols[reloc->r_symbolnum()]; - const char* targetName = &fStrings[targetSymbol->n_strx()]; - src->addByNameReference(srcAddr - src->fOffset, Reference::ppcFixupBranch14, targetName, 0, 0); - } - else { - instruction = OSSwapBigToHostInt32(*fixUpPtr); - int32_t displacement = (instruction & 0x0000FFFC); - if ( (displacement & 0x00008000) != 0 ) - displacement |= 0xFFFF0000; - srcAddr = sect->addr() + reloc->r_address(); - dstAddr = srcAddr + displacement; - src = findAtomCoveringOffset(srcAddr); - dst = findAtomCoveringOffset(dstAddr); - this->addCallSiteReference(*src, srcAddr - src->fOffset, Reference::ppcFixupBranch14, *dst, 0, dstAddr - dst->fOffset); - } - } - break; - case PPC_RELOC_PAIR: - // skip, processed by a previous look ahead - break; - case PPC_RELOC_LO16: - { - if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { - printf("PPC_RELOC_LO16 missing following pair\n"); - break; - } - srcAddr = sect->addr() + reloc->r_address(); - if ( reloc->r_extern() ) { - const macho_nlist* targetSymbol = &fSymbols[reloc->r_symbolnum()]; - const char* targetName = &fStrings[targetSymbol->n_strx()]; - src = findAtomCoveringOffset(srcAddr); - instruction = OSSwapBigToHostInt32(*fixUpPtr); - dstAddr = (nextReloc->r_address() << 16) | (instruction & 0x0000FFFF); - src->addByNameReference(srcAddr - src->fOffset, Reference::ppcFixupAbsLow16, targetName, dstAddr, 0); - } - else { - instruction = OSSwapBigToHostInt32(*fixUpPtr); - int16_t lowBits = (instruction & 0xFFFF); - dstAddr = (nextReloc->r_address() << 16) + (int32_t)lowBits; - if ( (lowBits & 0x8000) != 0 ) - dstAddr += 0x10000; - addFixUp(srcAddr, dstAddr, Reference::ppcFixupAbsLow16, 0); - } - } - break; - case PPC_RELOC_LO14: - { - if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { - printf("PPC_RELOC_LO14 missing following pair\n"); - break; - } - srcAddr = sect->addr() + reloc->r_address(); - if ( reloc->r_extern() ) { - const macho_nlist* targetSymbol = &fSymbols[reloc->r_symbolnum()]; - const char* targetName = &fStrings[targetSymbol->n_strx()]; - src = findAtomCoveringOffset(srcAddr); - instruction = OSSwapBigToHostInt32(*fixUpPtr); - dstAddr = (nextReloc->r_address() << 16) | (instruction & 0x0000FFFC); - src->addByNameReference(srcAddr - src->fOffset, Reference::ppcFixupAbsLow14, targetName, dstAddr, 0); - } - else { - instruction = OSSwapBigToHostInt32(*fixUpPtr); - dstAddr = (nextReloc->r_address() << 16) | (instruction & 0x0000FFFC); - addFixUp(srcAddr, dstAddr, Reference::ppcFixupAbsLow14, 0); - } - } - break; - case PPC_RELOC_HI16: - { - if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { - printf("PPC_RELOC_HI16 missing following pair\n"); - break; - } - srcAddr = sect->addr() + reloc->r_address(); - if ( reloc->r_extern() ) { - const macho_nlist* targetSymbol = &fSymbols[reloc->r_symbolnum()]; - const char* targetName = &fStrings[targetSymbol->n_strx()]; - src = findAtomCoveringOffset(srcAddr); - instruction = OSSwapBigToHostInt32(*fixUpPtr); - dstAddr = ((instruction & 0x0000FFFF) << 16) | (nextReloc->r_address() & 0x0000FFFF); - src->addByNameReference(srcAddr - src->fOffset, Reference::ppcFixupAbsHigh16, targetName, dstAddr, 0); - } - else { - instruction = OSSwapBigToHostInt32(*fixUpPtr); - dstAddr = ((instruction & 0x0000FFFF) << 16) | (nextReloc->r_address() & 0x0000FFFF); - addFixUp(srcAddr, dstAddr, Reference::ppcFixupAbsHigh16, 0); - } - } - break; - case PPC_RELOC_HA16: - { - if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { - printf("PPC_RELOC_HA16 missing following pair\n"); - break; - } - srcAddr = sect->addr() + reloc->r_address(); - if ( reloc->r_extern() ) { - const macho_nlist* targetSymbol = &fSymbols[reloc->r_symbolnum()]; - const char* targetName = &fStrings[targetSymbol->n_strx()]; - instruction = OSSwapBigToHostInt32(*fixUpPtr); - int16_t lowBits = (nextReloc->r_address() & 0x0000FFFF); - dstAddr = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits; - src = findAtomCoveringOffset(srcAddr); - src->addByNameReference(srcAddr - src->fOffset, Reference::ppcFixupAbsHigh16AddLow, targetName, dstAddr, 0); - } - else { - instruction = OSSwapBigToHostInt32(*fixUpPtr); - int16_t lowBits = (nextReloc->r_address() & 0x0000FFFF); - dstAddr = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits; - addFixUp(srcAddr, dstAddr, Reference::ppcFixupAbsHigh16AddLow, 0); - } - } - break; - case GENERIC_RELOC_VANILLA: - { - srcAddr = sect->addr() + reloc->r_address(); - Atom* srcAtom = findAtomCoveringOffset(srcAddr); - uint32_t offsetInSrcAtom = srcAddr - srcAtom->fOffset; - macho_uintptr_t pointerValue = ENDIAN_SWAP_POINTER(*((macho_uintptr_t*)fixUpPtr)); - if ( reloc->r_extern() ) { - const macho_nlist* targetSymbol = &fSymbols[reloc->r_symbolnum()]; - uint8_t type = targetSymbol->n_type() & N_TYPE; - if ( type == N_UNDF ) { - const char* targetName = &fStrings[targetSymbol->n_strx()]; - macho_uintptr_t addend = pointerValue; - // ppc lazy pointers have initial reference to dyld_stub_binding_helper - if ( (srcAtom->fSection->flags() & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ) { - std::vector& refs = srcAtom->getReferences(); - if ( refs.size() > 0 ) { - Reference* ref = (Reference*)refs[0]; - #if defined(ARCH_PPC64) - // hack to work around bad crt1.o in Mac OS X 10.4 - targetName = "dyld_stub_binding_helper"; - #endif - ref->setFromTargetName(targetName); - } - else { - fprintf(stderr, "lazy pointer (%s) should only have one reference - has %ld references\n", srcAtom->getDisplayName(), refs.size()); - } - } - #if defined(ARCH_PPC64) - // hack to work around bad crt1.o in Mac OS X 10.4 - else if ( (srcAtom->fSection->flags() & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS ) { - // ignore extra relocation - } - #endif - else { - srcAtom->addByNameReference(offsetInSrcAtom, Reference::pointer, targetName, addend, 0); - } - } - else { - dstAddr = targetSymbol->n_value(); - Atom* dstAtom = findAtomCoveringOffset(dstAddr); - macho_uintptr_t addend = pointerValue; - // ppc lazy pointers have initial reference to dyld_stub_binding_helper - if ( (srcAtom->fSection->flags() & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ) { - std::vector& refs = srcAtom->getReferences(); - if ( refs.size() > 0 ) { - Reference* ref = (Reference*)refs[0]; - ref->setFromTarget(*dstAtom); - ref->setFromTargetOffset(dstAddr - dstAtom->fOffset); - } - else { - fprintf(stderr, "lazy pointer (%s) should only have one reference - has %ld references\n", srcAtom->getDisplayName(), refs.size()); - } - } - else { - srcAtom->addReference(offsetInSrcAtom, Reference::pointer, *dstAtom, addend, 0); - } - } - } - else { - Atom* dstAtom = findAtomCoveringOffset(pointerValue); - // lazy pointers have references to dyld_stub_binding_helper which need to be ignored - if ( (srcAtom->fSection->flags() & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ) { - std::vector& refs = srcAtom->getReferences(); - if ( refs.size() > 0 ) { - Reference* ref = (Reference*)refs[0]; - ref->setFromTarget(*dstAtom); - ref->setFromTargetOffset(pointerValue - dstAtom->fOffset); - } - else { - fprintf(stderr, "lazy pointer (%s) should only have one reference - has %ld references\n", srcAtom->getDisplayName(), refs.size()); - } - } - else { - srcAtom->addReference(offsetInSrcAtom, Reference::pointer, *dstAtom, pointerValue-dstAtom->fOffset, 0); - } - } - } - break; - case PPC_RELOC_JBSR: - // ignore for now - break; -#endif -#if defined(ARCH_I386) - case GENERIC_RELOC_VANILLA: - { - srcAddr = sect->addr() + reloc->r_address(); - src = findAtomCoveringOffset(srcAddr); - if ( reloc->r_length() != 2 ) - throw "bad vanilla relocation length"; - Reference::Kind kind; - macho_uintptr_t pointerValue = ENDIAN_SWAP_POINTER(*((macho_uintptr_t*)fixUpPtr)); - if ( reloc->r_pcrel() ) { - kind = Reference::x86FixupBranch32; - pointerValue += srcAddr + sizeof(macho_uintptr_t); - } - else { - kind = Reference::pointer; - } - uint32_t offsetInSrcAtom = srcAddr - src->fOffset; - if ( reloc->r_extern() ) { - const macho_nlist* targetSymbol = &fSymbols[reloc->r_symbolnum()]; - uint8_t type = targetSymbol->n_type() & N_TYPE; - if ( type == N_UNDF ) { - const char* targetName = &fStrings[targetSymbol->n_strx()]; - macho_uintptr_t addend = pointerValue; - src->addByNameReference(offsetInSrcAtom, kind, targetName, addend, 0); - } - else { - dstAddr = targetSymbol->n_value(); - dst = findAtomCoveringOffset(dstAddr); - macho_uintptr_t addend = pointerValue - dstAddr; - src->addReference(offsetInSrcAtom, kind, *dst, addend, 0); - } - } - else { - dst = findAtomCoveringOffset(pointerValue); - // lazy pointers have references to dyld_stub_binding_helper which need to be ignored - if ( (src->fSection->flags() & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ) { - std::vector& refs = src->getReferences(); - if ( refs.size() == 1 ) { - Reference* ref = (Reference*)refs[0]; - ref->setFromTarget(*dst); - ref->setFromTargetOffset(pointerValue - dst->fOffset); - } - else { - fprintf(stderr, "lazy pointer (%s) should only have one reference - has %ld references\n", src->getDisplayName(), refs.size()); - } - } - else if ( ((uint8_t*)fixUpPtr)[-1] == 0xE8 ) // special case call instruction - this->addCallSiteReference(*src, offsetInSrcAtom, kind, *dst, 0, pointerValue - dst->fOffset); - else - src->addReference(offsetInSrcAtom, kind, *dst, 0, 0); - } - } - break; -#endif - - default: - printf("unknown relocation type %d\n", reloc->r_type()); - } - } - else { - const macho_scattered_relocation_info* sreloc = (macho_scattered_relocation_info*)reloc; - srcAddr = sect->addr() + sreloc->r_address(); - dstAddr = sreloc->r_value(); - fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + sreloc->r_address()); - const macho_scattered_relocation_info* nextSReloc = &sreloc[1]; - const macho_relocation_info* nextReloc = &reloc[1]; - // file format allows pair to be scattered or not - bool nextRelocIsPair = false; - uint32_t nextRelocAddress = 0; - uint32_t nextRelocValue = 0; - if ( (nextReloc->r_address() & R_SCATTERED) == 0 ) { - if ( nextReloc->r_type() == PPC_RELOC_PAIR ) { - nextRelocIsPair = true; - nextRelocAddress = nextReloc->r_address(); - } - } - else { - if ( nextSReloc->r_type() == PPC_RELOC_PAIR ) { - nextRelocIsPair = true; - nextRelocAddress = nextSReloc->r_address(); - nextRelocValue = nextSReloc->r_value(); - } - } - switch (sreloc->r_type()) { - case GENERIC_RELOC_VANILLA: - { - macho_uintptr_t betterDstAddr = ENDIAN_SWAP_POINTER(*((macho_uintptr_t*)fixUpPtr)); - //fprintf(stderr, "pointer reloc: srcAddr=0x%08X, dstAddr=0x%08X, pointer=0x%08lX\n", srcAddr, dstAddr, betterDstAddr); - // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr) - Atom* src = findAtomCoveringOffset(srcAddr); - Atom* dst = findAtomCoveringOffset(dstAddr); - src->addReference(srcAddr - src->fOffset, Reference::pointer, *dst, betterDstAddr - dst->fOffset, 0); - } - break; -#if defined(ARCH_PPC) || defined(ARCH_PPC64) - case PPC_RELOC_BR24: - { - instruction = OSSwapBigToHostInt32(*fixUpPtr); - if ( (instruction & 0x4C000000) == 0x48000000 ) { - int32_t displacement = (instruction & 0x03FFFFFC); - if ( (displacement & 0x02000000) != 0 ) - displacement |= 0xFC000000; - srcAddr = sect->addr() + sreloc->r_address(); - dstAddr = sreloc->r_value(); - src = findAtomCoveringOffset(srcAddr); - dst = findAtomCoveringOffset(dstAddr); - this->addCallSiteReference(*src, srcAddr - src->fOffset, Reference::ppcFixupBranch24, *dst, 0, srcAddr + displacement - sreloc->r_value()); - } - } - break; - case PPC_RELOC_LO16_SECTDIFF: - { - if ( ! nextRelocIsPair) { - printf("PPC_RELOC_LO16_SECTDIFF missing following PAIR\n"); - break; - } - src = findAtomCoveringOffset(srcAddr); - dst = findAtomCoveringOffset(dstAddr); - instruction = OSSwapBigToHostInt32(*fixUpPtr); - int16_t lowBits = (instruction & 0xFFFF); - int32_t displacement = (nextRelocAddress << 16) + (int32_t)lowBits; - if ( (lowBits & 0x8000) != 0 ) - displacement += 0x10000; - uint32_t picBaseOffset = nextRelocValue - src->fOffset; - int64_t dstOffset = src->fOffset + picBaseOffset + displacement - dst->fOffset; - src->addReference(srcAddr - src->fOffset, Reference::ppcFixupPicBaseLow16, *dst, dstOffset, picBaseOffset); - } - break; - case PPC_RELOC_LO14_SECTDIFF: - { - if ( ! nextRelocIsPair) { - printf("PPC_RELOC_LO14_SECTDIFF missing following PAIR\n"); - break; - } - src = findAtomCoveringOffset(srcAddr); - dst = findAtomCoveringOffset(dstAddr); - instruction = OSSwapBigToHostInt32(*fixUpPtr); - int16_t lowBits = (instruction & 0xFFFC); - int32_t displacement = (nextRelocAddress << 16) + (int32_t)lowBits; - if ( (lowBits & 0x8000) != 0 ) - displacement += 0x10000; - uint32_t picBaseOffset = nextRelocValue - src->fOffset; - int64_t dstOffset = src->fOffset + picBaseOffset + displacement - dst->fOffset; - src->addReference(srcAddr - src->fOffset, Reference::ppcFixupPicBaseLow14, *dst, dstOffset, picBaseOffset); - } - break; - case PPC_RELOC_HA16_SECTDIFF: - { - if ( ! nextRelocIsPair) { - printf("PPC_RELOC_LO14_SECTDIFF missing following PAIR\n"); - break; - } - src = findAtomCoveringOffset(srcAddr); - dst = findAtomCoveringOffset(dstAddr); - instruction = OSSwapBigToHostInt32(*fixUpPtr); - int16_t lowBits = (nextRelocAddress & 0x0000FFFF); - int32_t displacement = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits; - uint32_t picBaseOffset = nextRelocValue - src->fOffset; - int64_t dstOffset = src->fOffset + picBaseOffset + displacement - dst->fOffset; - src->addReference(srcAddr - src->fOffset, Reference::ppcFixupPicBaseHigh16, *dst, dstOffset, picBaseOffset); - } - break; - case PPC_RELOC_LO14: - { - if ( ! nextRelocIsPair) { - printf("PPC_RELOC_LO14 missing following PAIR\n"); - break; - } - src = findAtomCoveringOffset(srcAddr); - dst = findAtomCoveringOffset(dstAddr); - instruction = OSSwapBigToHostInt32(*fixUpPtr); - int16_t lowBits = (instruction & 0xFFFC); - uint32_t betterDstAddr = (nextRelocAddress << 16) + (int32_t)lowBits; - if ( (lowBits & 0x8000) != 0 ) - betterDstAddr += 0x10000; - src->addReference(srcAddr - src->fOffset, Reference::ppcFixupAbsLow14, *dst, betterDstAddr - dst->fOffset, 0); - } - break; - case PPC_RELOC_LO16: - { - if ( ! nextRelocIsPair) { - printf("PPC_RELOC_LO16 missing following PAIR\n"); - break; - } - src = findAtomCoveringOffset(srcAddr); - dst = findAtomCoveringOffset(dstAddr); - instruction = OSSwapBigToHostInt32(*fixUpPtr); - int16_t lowBits = (instruction & 0xFFFF); - uint32_t betterDstAddr = (nextRelocAddress << 16) + (int32_t)lowBits; - if ( (lowBits & 0x8000) != 0 ) - betterDstAddr += 0x10000; - src->addReference(srcAddr - src->fOffset, Reference::ppcFixupAbsLow16, *dst, betterDstAddr - dst->fOffset, 0); - } - break; - case PPC_RELOC_HA16: - { - if ( ! nextRelocIsPair) { - printf("PPC_RELOC_HA16 missing following PAIR\n"); - break; - } - src = findAtomCoveringOffset(srcAddr); - dst = findAtomCoveringOffset(dstAddr); - instruction = OSSwapBigToHostInt32(*fixUpPtr); - int16_t lowBits = (nextRelocAddress & 0xFFFF); - uint32_t betterDstAddr = ((instruction & 0xFFFF) << 16) + (int32_t)lowBits; - src->addReference(srcAddr - src->fOffset, Reference::ppcFixupAbsHigh16AddLow, *dst, betterDstAddr - dst->fOffset, 0); - } - break; - case PPC_RELOC_SECTDIFF: - case PPC_RELOC_LOCAL_SECTDIFF: - { - const macho_scattered_relocation_info* nextReloc = &sreloc[1]; - if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { - printf("PPC_RELOC_SECTDIFF missing following pair\n"); - break; - } - srcAddr = sect->addr() + sreloc->r_address(); - uint32_t toAddr = sreloc->r_value(); - uint32_t fromAddr = nextReloc->r_value(); - src = findAtomCoveringOffset(srcAddr); - Atom* to = findAtomCoveringOffset(toAddr); - Atom* from = findAtomCoveringOffset(fromAddr); - //macho_intptr_t pointerValue = *(macho_intptr_t*)fixUpPtr; - //uint64_t toOffset = to->fOffset; - //uint64_t fromOffset = from->fOffset; - //int64_t pointerValue64 = pointerValue; - //uint64_t addend = pointerValue64 - (toOffset - fromOffset); - Reference::Kind kind = Reference::pointer32Difference; - if ( sreloc->r_length() == 3 ) - kind = Reference::pointer64Difference; - src->addDifferenceReference(srcAddr - src->fOffset, kind, *to, toAddr - to->fOffset, *from, fromAddr - from->fOffset); - } - break; - case PPC_RELOC_PAIR: - break; - case PPC_RELOC_HI16_SECTDIFF: - printf("unexpected scattered relocation type PPC_RELOC_HI16_SECTDIFF\n"); - break; -#endif -#if defined(ARCH_I386) - case GENERIC_RELOC_SECTDIFF: - case GENERIC_RELOC_LOCAL_SECTDIFF: - { - if ( nextSReloc->r_type() != GENERIC_RELOC_PAIR ) { - printf("GENERIC_RELOC_SECTDIFF missing following pair\n"); - break; - } - srcAddr = sect->addr() + sreloc->r_address(); - uint32_t toAddr = sreloc->r_value(); - uint32_t fromAddr = nextSReloc->r_value(); - src = findAtomCoveringOffset(srcAddr); - Atom* to = findAtomCoveringOffset(toAddr); - Atom* from = findAtomCoveringOffset(fromAddr); - Reference::Kind kind = Reference::pointer32Difference; - if ( sreloc->r_length() != 2 ) - throw "bad length for GENERIC_RELOC_SECTDIFF"; - src->addDifferenceReference(srcAddr - src->fOffset, kind, *to, toAddr - to->fOffset, *from, fromAddr - from->fOffset); - } - break; - case GENERIC_RELOC_PAIR: - // do nothing, already used via a look ahead - break; -#endif - default: - printf("unknown scattered relocation type %d\n", sreloc->r_type()); - } - - } -} - - -void Reader::addFixUp(uint32_t srcAddr, uint32_t dstAddr, Reference::Kind kind, uint32_t picBaseAddr) -{ - Atom* src = findAtomCoveringOffset(srcAddr); - Atom* dst = findAtomCoveringOffset(dstAddr); - src->addReference(srcAddr - src->fOffset, kind, *dst, dstAddr - dst->fOffset, picBaseAddr - src->fOffset); -} - -Atom* Reader::findAtomCoveringOffset(uint32_t offset) -{ -#if 1 - // binary search of sorted atoms - Atom** base = &fAtoms[0]; - for (uint32_t n = fAtoms.size(); n > 0; n /= 2) { - Atom** pivot = &base[n/2]; - Atom* pivotAtom = *pivot; - if ( pivotAtom->fOffset <= offset ) { - if ( offset < (pivotAtom->fOffset + pivotAtom->fSize) ) - return pivotAtom; - // key > pivot - // move base to symbol after pivot - base = &pivot[1]; - --n; - } - else { - // key < pivot - // keep same base - } - } - // possible that last atom is zero length - Atom* lastAtom = fAtoms.back(); - if ( (lastAtom->fOffset == offset) && (lastAtom->fSize == 0) ) - return lastAtom; -#else - const uint32_t atomCount = fAtoms.size(); - for (uint32_t i=0; i < atomCount; ++i) { - Atom* atom = fAtoms[i]; - if ( (atom->fOffset <= offset) && (offset < (atom->fOffset + atom->fSize)) ) - return atom; - } -#endif - throwf("address 0x%08X is not in any atom", offset); -} - -uint32_t Reader::findAtomIndex(const Atom& atom) -{ - const Atom* target = &atom; - const uint32_t atomCount = fAtoms.size(); - for (uint32_t i=0; i < atomCount; ++i) { - Atom* anAtom = fAtoms[i]; - if ( anAtom == target ) - return i; - } - return 0xffffffff; -} - -static void insertOffset(std::set& offsets, uint32_t value) -{ - //fprintf(stderr, "cleave point at 0x%08X\n", value); - offsets.insert(value); -} - -void Reader::insertOffsetIfNotText(std::set& offsets, uint32_t value) -{ - const macho_section* sect = findSectionCoveringOffset(value); - if ( (sect == NULL) || (strcmp(sect->segname(),"__TEXT") != 0) || (strncmp(sect->sectname(),"__text", 6) != 0) ) { - offsets.insert(value); - } -} - -void Reader::insertOffsetIfText(std::set& offsets, uint32_t value) -{ - const macho_section* sect = findSectionCoveringOffset(value); - if ( (sect != NULL) && (strcmp(sect->segname(),"__TEXT") == 0) && (strncmp(sect->sectname(),"__text", 6) == 0) ) { - //fprintf(stderr, "don't cleave point at 0x%08X\n", value); - offsets.insert(value); - } -} - -const macho_section* Reader::findSectionCoveringOffset(uint32_t offset) -{ - const macho_section* const sectionsStart = (macho_section*)((char*)fSegment + sizeof(macho_segment_command)); - const macho_section* const sectionsEnd = §ionsStart[fSegment->nsects()]; - for (const macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { - if ( (sect->addr() <= offset) && (offset < (sect->addr() + sect->size())) ) - return sect; - } - return NULL; -} - - -void Reader::buildOffsetsSet(const macho_relocation_info* reloc, const macho_section* sect, std::set& cleavePoints, std::set& dontCleavePoints) -{ -#if defined(ARCH_PPC) || defined(ARCH_PPC64) - uint32_t targetAddr; -#endif - if ( (reloc->r_address() & R_SCATTERED) == 0 ) { - uint32_t* fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address()); -#if defined(ARCH_PPC) || defined(ARCH_PPC64) - uint32_t instruction; -#endif - switch ( reloc->r_type() ) { -#if defined(ARCH_PPC) || defined(ARCH_PPC64) - case PPC_RELOC_BR14: - // do nothing. local branch should not cleave - break; - case PPC_RELOC_BR24: - { - if ( ! reloc->r_extern() ) { - instruction = OSSwapBigToHostInt32(*fixUpPtr); - if ( (instruction & 0x4C000000) == 0x48000000 ) { - int32_t displacement = (instruction & 0x03FFFFFC); - if ( (displacement & 0x02000000) != 0 ) - displacement |= 0xFC000000; - //cleavePoints.insert(reloc->r_address() + displacement); - insertOffset(cleavePoints, sect->addr() + reloc->r_address() + displacement); - } - } - } - break; - case PPC_RELOC_PAIR: - // skip, processed by a look ahead - break; - case PPC_RELOC_LO16: - { - const macho_relocation_info* nextReloc = &reloc[1]; - if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { - printf("PPC_RELOC_LO16 missing following pair\n"); - break; - } - if ( ! reloc->r_extern() ) { - instruction = OSSwapBigToHostInt32(*fixUpPtr); - targetAddr = (nextReloc->r_address() << 16) | (instruction & 0x0000FFFF); - //cleavePoints.insert(targetAddr); - insertOffset(cleavePoints, (targetAddr)); - } - } - break; - case PPC_RELOC_LO14: - { - const macho_relocation_info* nextReloc = &reloc[1]; - if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { - printf("PPC_RELOC_LO14 missing following pair\n"); - break; - } - if ( ! reloc->r_extern() ) { - instruction = OSSwapBigToHostInt32(*fixUpPtr); - targetAddr = (nextReloc->r_address() << 16) | (instruction & 0x0000FFFC); - //cleavePoints.insert(targetAddr); - insertOffset(cleavePoints, (targetAddr)); - } - } - break; - case PPC_RELOC_HI16: - { - const macho_relocation_info* nextReloc = &reloc[1]; - if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { - printf("PPC_RELOC_HI16 missing following pair\n"); - break; - } - if ( ! reloc->r_extern() ) { - instruction = OSSwapBigToHostInt32(*fixUpPtr); - targetAddr = ((instruction & 0x0000FFFF) << 16) | (nextReloc->r_address() & 0x0000FFFF); - //cleavePoints.insert(targetAddr); - insertOffset(cleavePoints, targetAddr); - } - } - break; - case PPC_RELOC_HA16: - { - const macho_relocation_info* nextReloc = &reloc[1]; - if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { - printf("PPC_RELOC_HA16 missing following pair\n"); - break; - } - if ( ! reloc->r_extern() ) { - instruction = OSSwapBigToHostInt32(*fixUpPtr); - int16_t lowBits = (nextReloc->r_address() & 0x0000FFFF); - targetAddr = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits; - //cleavePoints.insert(targetAddr); - insertOffset(cleavePoints, targetAddr); - } - } - break; - case PPC_RELOC_JBSR: - // ignore for now - break; -#endif - case GENERIC_RELOC_VANILLA: - { -#if defined(ARCH_PPC64) - if ( reloc->r_length() != 3 ) - throw "vanilla pointer relocation found that is not 8-bytes"; -#elif defined(ARCH_PPC) || defined(ARCH_I386) - if ( reloc->r_length() != 2 ) - throw "vanilla pointer relocation found that is not 4-bytes"; -#endif - //fprintf(stderr, "addr=0x%08X, pcrel=%d, len=%d, extern=%d, type=%d\n", reloc->r_address(), reloc->r_pcrel(), reloc->r_length(), reloc->r_extern(), reloc->r_type()); - if ( !reloc->r_extern() ) { - macho_uintptr_t pointerValue = ENDIAN_SWAP_POINTER(*((macho_uintptr_t*)fixUpPtr)); -#if defined(ARCH_I386) - // i386 stubs have internal relocs that should not cause a cleave - if ( (sect->flags() & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ) - break; -#endif - if ( reloc->r_pcrel() ) - pointerValue += reloc->r_address() + sect->addr() + sizeof(macho_uintptr_t); - // a pointer into code does not cleave the code (gcc always pointers to labels) - insertOffsetIfNotText(cleavePoints, pointerValue); - } - } - break; - default: - printf("unknown relocation type %d\n", reloc->r_type()); - } - } - else { - const macho_scattered_relocation_info* sreloc = (macho_scattered_relocation_info*)reloc; - switch (sreloc->r_type()) { - case GENERIC_RELOC_VANILLA: - insertOffset(cleavePoints, sreloc->r_value()); - break; -#if defined(ARCH_PPC) || defined(ARCH_PPC64) - case PPC_RELOC_BR24: - insertOffset(cleavePoints, sreloc->r_value()); - break; - case PPC_RELOC_HA16: - case PPC_RELOC_HI16: - case PPC_RELOC_LO16: - case PPC_RELOC_LO14: - case PPC_RELOC_LO16_SECTDIFF: - case PPC_RELOC_LO14_SECTDIFF: - case PPC_RELOC_HA16_SECTDIFF: - case PPC_RELOC_HI16_SECTDIFF: - //cleavePoints.insert(sreloc->r_value()); - insertOffset(cleavePoints, sreloc->r_value()); - insertOffsetIfText(dontCleavePoints, sreloc->r_value()); - break; - case PPC_RELOC_SECTDIFF: - case PPC_RELOC_LOCAL_SECTDIFF: - // these do not cleave up a .o file - // a SECTDIFF in __TEXT probably means a jump table, and should prevent a cleave - { - const macho_scattered_relocation_info* nextReloc = &sreloc[1]; - if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { - printf("PPC_RELOC_SECTDIFF missing following pair\n"); - break; - } - insertOffsetIfText(dontCleavePoints, sreloc->r_value()); - insertOffsetIfText(dontCleavePoints, nextReloc->r_value()); - } - break; - case PPC_RELOC_PAIR: - // do nothing, already used via a look ahead - break; -#endif -#if defined(ARCH_I386) - case GENERIC_RELOC_SECTDIFF: - case GENERIC_RELOC_LOCAL_SECTDIFF: - // these do not cleave up a .o file - // a SECTDIFF in __TEXT probably means a jump table, and should prevent a cleave - { - const macho_scattered_relocation_info* nextReloc = &sreloc[1]; - if ( nextReloc->r_type() != GENERIC_RELOC_PAIR ) { - printf("GENERIC_RELOC_SECTDIFF missing following pair\n"); - break; - } - insertOffsetIfText(dontCleavePoints, sreloc->r_value()); - insertOffsetIfText(dontCleavePoints, nextReloc->r_value()); - } - break; - case GENERIC_RELOC_PAIR: - // do nothing, already used via a look ahead - break; -#endif - default: - printf("unknown relocation type %d\n", sreloc->r_type()); - } - - } -} - - -Segment* Reader::makeSegmentFromSection(const macho_section* sect) -{ - // make segment object if one does not already exist - const uint32_t segmentCount = fSegments.size(); - for (uint32_t i=0; i < segmentCount; ++i) { - Segment* seg = fSegments[i]; - if ( strcmp(sect->segname(), seg->getName()) == 0 ) - return seg; - } - Segment* seg = new Segment(sect); - fSegments.push_back(seg); - return seg; -} - -macho_uintptr_t Reader::commonsOffset() -{ - return fSegment->vmsize(); -} - -const char* Reader::getPath() -{ - return fPath; -} - -std::vector& Reader::getAtoms() -{ - return (std::vector&)(fAtoms); -} - -std::vector* Reader::getJustInTimeAtomsFor(const char* name) -{ - // object files have no just-in-time atoms - return NULL; -} - - -std::vector* Reader::getStabsDebugInfo() -{ - if ( fNonAtomStabsCount == 0 ) - return NULL; - - std::vector* stabs = new std::vector(); - stabs->reserve(fNonAtomStabsCount); - - for (uint32_t i=0; i < fNonAtomStabsCount; ++i) { - const macho_nlist* sym = &fSymbols[fNonAtomStabsStartIndex+i]; - if ( (sym->n_type() & N_STAB) != 0 ) { - ObjectFile::StabsInfo stab; - stab.atomOffset = sym->n_value(); - stab.string = &fStrings[sym->n_strx()]; - stab.type = sym->n_type(); - stab.other = sym->n_sect(); - stab.desc = sym->n_desc(); - // for N_SO n_value is offset of first atom, but our gdb ignores this, so we omit that calculation - if ( stab.type == N_SO ) - stab.atomOffset = 0; - stabs->push_back(stab); - } - } - - // add any extra N_LSYM's not at start of symbol table - for (std::vector::iterator it=fNonAtomExtras.begin(); it != fNonAtomExtras.end(); ++it) { - const macho_nlist* sym = &fSymbols[*it]; - ObjectFile::StabsInfo stab; - stab.atomOffset = sym->n_value(); - stab.string = &fStrings[sym->n_strx()]; - stab.type = sym->n_type(); - stab.other = sym->n_sect(); - stab.desc = sym->n_desc(); - stabs->push_back(stab); - } - - return stabs; -} - -void Reader::deadStub(Atom& target) -{ - // remove stub - fDeadAtoms.insert(&target); - - // remove lazy pointer - const int stubNameLen = strlen(target.fSynthesizedName); - char lazyName[stubNameLen+8]; - strcpy(lazyName, target.fSynthesizedName); - strcpy(&lazyName[stubNameLen-5], "$lazy_ptr"); - const uint32_t atomCount = fAtoms.size(); - for (uint32_t i=1; i < atomCount; ++i) { - Atom* atom = fAtoms[i]; - if ( (atom->fSynthesizedName != NULL) && (strcmp(atom->fSynthesizedName, lazyName) == 0) ) { - fDeadAtoms.insert(atom); - break; - } - } -} - - -void Reader::addCallSiteReference(Atom& src, uint32_t offsetInSrcAtom, Reference::Kind kind, Atom& target, uint32_t picBaseOffset, uint32_t offsetInTargetAtom) -{ - // the compiler some times produces stub to static functions and then calls the stubs - // we need to skip the stub if a static function exists with the same name and remove the stub - if ( target.isLazyStub() ) { - const macho_section* section = target.fSection; - uint32_t index = (target.fOffset - section->addr()) / section->reserved2(); - uint32_t indirectTableIndex = section->reserved1() + index; - uint32_t symbolIndex = ENDIAN_READ32(fIndirectTable[indirectTableIndex]); - if ( (symbolIndex & INDIRECT_SYMBOL_LOCAL) == 0 ) { - const macho_nlist* sym = &fSymbols[symbolIndex]; - if ( (sym->n_value() != 0) && ((sym->n_type() & N_EXT) == 0) ) { - Atom* betterTarget = this->findAtomCoveringOffset(sym->n_value()); - if ( (betterTarget != NULL) && (betterTarget->getScope() == ObjectFile::Atom::scopeTranslationUnit) ) { - // use direct reference to static function - src.addDirectReference(offsetInSrcAtom, kind, *betterTarget, offsetInTargetAtom, picBaseOffset); - - // remove stub and lazy pointer - this->deadStub(target); - return; - } - } - } - } - - // fall through to general case - src.addReference(offsetInSrcAtom, kind, target, offsetInTargetAtom, picBaseOffset); -} - - -Atom::Atom(Reader& owner, const macho_nlist* symbol) - : fOwner(owner), fSymbol(symbol), fOffset(0), fSize(0), fSection(NULL), fSegment(NULL), fSynthesizedName(NULL), - fStabsStartIndex(0), fStabsCount(0) -{ - uint8_t type = symbol->n_type(); - if ( (type & N_EXT) == 0 ) - fScope = ObjectFile::Atom::scopeTranslationUnit; - else if ( (type & N_PEXT) != 0 ) - fScope = ObjectFile::Atom::scopeLinkageUnit; - else - fScope = ObjectFile::Atom::scopeGlobal; - if ( (type & N_TYPE) == N_SECT ) { - // real definition - const macho_section* sections = (macho_section*)((char*)fOwner.fSegment + sizeof(macho_segment_command)); - fSection = §ions[fSymbol->n_sect()-1]; - fSegment = owner.makeSegmentFromSection(fSection); - fOffset = fSymbol->n_value(); - uint8_t type = fSection->flags() & SECTION_TYPE; - switch ( type ) { - case S_LAZY_SYMBOL_POINTERS: - case S_NON_LAZY_SYMBOL_POINTERS: - { - // get target name out of indirect symbol table - uint32_t index = (fOffset - fSection->addr()) / sizeof(macho_uintptr_t); - index += fSection->reserved1(); - uint32_t symbolIndex = ENDIAN_READ32(fOwner.fIndirectTable[index]); - uint32_t strOffset = fOwner.fSymbols[symbolIndex].n_strx(); - const char* name = &fOwner.fStrings[strOffset]; - Reference* ref = this->addByNameReference(0, Reference::pointer, name, 0, 0); - if ( type == S_LAZY_SYMBOL_POINTERS ) { - ref->setLazy(true); - } - } - break; - } - } - else if ( ((type & N_TYPE) == N_UNDF) && (symbol->n_value() != 0) ) { - // tentative definition - fSize = symbol->n_value(); - fSection = getCommonsSection(); - fSegment = owner.makeSegmentFromSection(fSection); - fOffset = owner.commonsOffset(); - } - else { - printf("unknown symbol type: %d\n", type); - } -} - -Atom::Atom(Reader& owner, uint32_t offset) - : fOwner(owner), fSymbol(NULL), fOffset(offset), fSize(0), fSection(NULL), fSegment(NULL), fSynthesizedName(NULL), - fStabsStartIndex(0), fStabsCount(0) -{ - fSection = findSectionFromOffset(offset); - fScope = ObjectFile::Atom::scopeLinkageUnit; - fSegment = owner.makeSegmentFromSection(fSection); - uint8_t type = fSection->flags() & SECTION_TYPE; - switch ( type ) { - case S_SYMBOL_STUBS: - { - uint32_t index = (offset - fSection->addr()) / fSection->reserved2(); - index += fSection->reserved1(); - uint32_t symbolIndex = ENDIAN_READ32(fOwner.fIndirectTable[index]); - uint32_t strOffset = fOwner.fSymbols[symbolIndex].n_strx(); - const char* name = &fOwner.fStrings[strOffset]; - char* str = new char[strlen(name)+8]; - strcpy(str, name); - strcat(str, "$stub"); - fSynthesizedName = str; - } - break; - case S_LAZY_SYMBOL_POINTERS: - case S_NON_LAZY_SYMBOL_POINTERS: - { - uint32_t index = (offset - fSection->addr()) / sizeof(macho_uintptr_t); - index += fSection->reserved1(); - uint32_t symbolIndex = ENDIAN_READ32(fOwner.fIndirectTable[index]); - uint32_t strOffset = fOwner.fSymbols[symbolIndex].n_strx(); - const char* name = &fOwner.fStrings[strOffset]; - char* str = new char[strlen(name)+16]; - strcpy(str, name); - if ( type == S_LAZY_SYMBOL_POINTERS ) - strcat(str, "$lazy_ptr"); - else - strcat(str, "$non_lazy_ptr"); - fSynthesizedName = str; - Reference* ref = this->addByNameReference(0, Reference::pointer, name, 0, 0); - if ( type == S_LAZY_SYMBOL_POINTERS ) { - ref->setLazy(true); - } - const macho_nlist* sym = &fOwner.fSymbols[symbolIndex]; - if ( (sym->n_type() & N_TYPE) == N_UNDF ) { - if ( (sym->n_desc() & N_WEAK_REF) != 0 ) - ref->setWeak(true); - } - } - break; - } -} - - -Atom::~Atom() -{ -} - -macho_section Atom::fgCommonsSection; - - -bool Atom::isLazyStub() -{ - return ( (fSection->flags() & SECTION_TYPE) == S_SYMBOL_STUBS); -} - -const macho_section* Atom::getCommonsSection() { - if ( strcmp(fgCommonsSection.sectname(), "__common") != 0 ) { - fgCommonsSection.set_sectname("__common"); - fgCommonsSection.set_segname("__DATA"); - fgCommonsSection.set_flags(S_ZEROFILL); - } - return &fgCommonsSection; -} - -ObjectFile::Reader* Atom::getFile() const -{ - return &fOwner; -} - - -const char* Atom::getName() const -{ - if ( fSymbol != NULL ) - return &fOwner.fStrings[fSymbol->n_strx()]; - else - return fSynthesizedName; -} - -const char* Atom::getDisplayName() const -{ - if ( fSymbol != NULL ) - return &fOwner.fStrings[fSymbol->n_strx()]; - - if ( fSynthesizedName != NULL ) - return fSynthesizedName; - - static char temp[32]; - sprintf(temp, "atom #%u", fOwner.findAtomIndex(*this)); - return temp; -} - -ObjectFile::Atom::Scope Atom::getScope() const -{ - return fScope; -} - -void Atom::setScope(ObjectFile::Atom::Scope newScope) -{ - fScope = newScope; -} - - -bool Atom::isWeakDefinition() const -{ - if ( isTentativeDefinition() ) - return true; - if ( fSymbol != NULL ) - return ( (fSymbol->n_desc() & N_WEAK_DEF) != 0 ); - uint8_t type = fSection->flags() & SECTION_TYPE; - switch ( type ) { - case S_SYMBOL_STUBS: - case S_LAZY_SYMBOL_POINTERS: - case S_NON_LAZY_SYMBOL_POINTERS: - return true; - } - return false; -} - -bool Atom::isTentativeDefinition() const -{ - return (fSection == &fgCommonsSection); -} - -bool Atom::isCoalesableByName() const -{ - uint8_t type = fSection->flags() & SECTION_TYPE; - switch ( type ) { - case S_SYMBOL_STUBS: - case S_COALESCED: - return true; - }; - if ( isTentativeDefinition() ) - return true; - return false; -} - -bool Atom::isCoalesableByValue() const -{ - uint8_t type = fSection->flags() & SECTION_TYPE; - switch ( type ) { - case S_CSTRING_LITERALS: - case S_4BYTE_LITERALS: - case S_8BYTE_LITERALS: - return true; - }; - return false; -} - -bool Atom::isZeroFill() const -{ - return ((fSection->flags() & SECTION_TYPE) == S_ZEROFILL); -} - -bool Atom::dontDeadStrip() const -{ - if ( fSymbol != NULL ) - return ( (fSymbol->n_desc() & N_NO_DEAD_STRIP) != 0 ); - return false; -} - - -bool Atom::dontStripName() const -{ - if ( fSymbol != NULL ) - return ( (fSymbol->n_desc() & REFERENCED_DYNAMICALLY) != 0 ); - return false; -} - -bool Atom::isImportProxy() const -{ - return false; -} - - -uint64_t Atom::getSize() const -{ - //return fOffset; - return fSize; -} - - -std::vector& Atom::getReferences() const -{ - return (std::vector&)(fReferences); -} - -bool Atom::mustRemainInSection() const -{ - return true; -} - -const char* Atom::getSectionName() const -{ - if ( strlen(fSection->sectname()) > 15 ) { - static char temp[18]; - strncpy(temp, fSection->sectname(), 16); - temp[17] = '\0'; - return temp; - } - return fSection->sectname(); -} - -Segment& Atom::getSegment() const -{ - return *fSegment; -} - -bool Atom::requiresFollowOnAtom() const -{ - // requires follow-on if built with old compiler and not the last atom - if ( (fOwner.fHeader->flags() & MH_SUBSECTIONS_VIA_SYMBOLS) == 0) { - if ( fOwner.findAtomIndex(*this) < (fOwner.fAtoms.size()-1) ) - return true; - } - return false; -} - -ObjectFile::Atom& Atom::getFollowOnAtom() const -{ - uint32_t myIndex = fOwner.findAtomIndex(*this); - return *fOwner.fAtoms[myIndex+1]; -} - -std::vector* Atom::getStabsDebugInfo() const -{ - if ( fStabsCount == 0 ) - return NULL; - - std::vector* stabs = new std::vector(); - stabs->reserve(fStabsCount); - - for (uint32_t i=0; i < fStabsCount; ++i) { - const macho_nlist* sym = &fOwner.fSymbols[fStabsStartIndex+i]; - if ( (sym->n_type() & N_STAB) != 0 ) { - ObjectFile::StabsInfo stab; - stab.atomOffset = sym->n_value(); - stab.string = &fOwner.fStrings[sym->n_strx()]; - stab.type = sym->n_type(); - stab.other = sym->n_sect(); - stab.desc = sym->n_desc(); - 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 absolute address to the atom offset - stab.atomOffset -= fOffset; - break; - } - stabs->push_back(stab); - } - } - - return stabs; -} - -uint8_t Atom::getAlignment() const -{ - // mach-o file format has no alignment information for atoms - just whole sections - if ( fSection != NULL ) { - if ( isTentativeDefinition() ) { - // common symbols align to their size - // that is, a 4-byte common aligns to 4-bytes - // to be safe, odd size commons align to the next power-of-2 size - uint8_t alignment = (uint8_t)ceil(log2(this->getSize())); - // limit alignment of extremely large commons to 2^15 bytes (8-page) - if ( alignment < 15 ) - return alignment; - else - return 15; - } - else { - // so we assume every atom requires the same alignment as the whole section - return fSection->align(); - } - } - else { - return 2; - } -} - -void Atom::copyRawContent(uint8_t buffer[]) const -{ - // copy base bytes - if ( isZeroFill() ) - bzero(buffer, fSize); - else { - uint32_t fileOffset = fSection->offset() - fSection->addr() + fOffset; - memcpy(buffer, (char*)(fOwner.fHeader)+fileOffset, fSize); - } -} - -void Atom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const -{ - const uint32_t referencesCount = fReferences.size(); - - // skip copy if no fix-ups - if ( referencesCount == 0 ) { - uint32_t fileOffset = fSection->offset() - fSection->addr() + fOffset; - writer.write(0, (char*)(fOwner.fHeader)+fileOffset, fSize); - return; - } - - // copy base bytes - uint8_t buffer[this->getSize()]; - this->copyRawContent(buffer); - - // apply any fix-ups - for (uint32_t i=0; i < referencesCount; ++i) { - Reference* ref = fReferences[i]; - uint32_t offset = ref->getFixUpOffset(); - uint32_t* instructionPtr = (uint32_t*)&buffer[offset]; - ObjectFile::Atom& target = ref->getTarget(); - if ( &target == NULL ) { - if ( finalLinkedImage ) - throw "target not found"; - else - continue; - } - uint32_t instruction; - uint32_t newInstruction; - switch ( ref->getKind() ) { - case Reference::noFixUp: - break; - case Reference::pointer: - { - //fprintf(stderr, "writeContent: %s reference to %s\n", this->getDisplayName(), target.getDisplayName()); - if ( ref->isLazyReference() && finalLinkedImage ) { - // lazy-symbol ==> pointer contains address of dyld_stub_binding_helper (stored in "from" target) - *((macho_uintptr_t*)instructionPtr) = ENDIAN_SWAP_POINTER(ref->getFromTarget().getAddress()); - } - else if ( target.isImportProxy() ) { - // external realocation ==> pointer contains addend - *((macho_uintptr_t*)instructionPtr) = ENDIAN_SWAP_POINTER(ref->getTargetOffset()); - } - else { - // internal relocation - if ( finalLinkedImage || (strcmp(target.getSectionName(), "__common") != 0) ) { - // pointer contains target address - //printf("Atom::writeContent() target.name=%s, target.address=0x%08llX\n", target.getDisplayName(), target.getAddress()); - *((macho_uintptr_t*)instructionPtr) = ENDIAN_SWAP_POINTER(target.getAddress() + ref->getTargetOffset()); - } - else { - // pointer contains addend - *((macho_uintptr_t*)instructionPtr) = ENDIAN_SWAP_POINTER(ref->getTargetOffset()); - } - } - } - break; - case Reference::ppcFixupBranch24: - { - //fprintf(stderr, "bl fixup to %s at 0x%08llX, ", target.getDisplayName(), target.getAddress()); - int64_t displacement = (target.getAddress() + ref->getTargetOffset() ) - (this->getAddress() + offset); - if ( !finalLinkedImage && target.isImportProxy() ) { - // doing "ld -r" to an external symbol - // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target - displacement -= target.getAddress(); - } - else { - const int64_t bl_eightMegLimit = 0x00FFFFFF; - if ( (displacement > bl_eightMegLimit) || (displacement < (-bl_eightMegLimit)) ) { - //fprintf(stderr, "bl out of range (%lld max is +/-16M) from %s in %s to %s in %s\n", displacement, this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); - throwf("bl out of range (%lld max is +/-16M) from %s in %s to %s in %s", displacement, this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); - } - } - instruction = OSReadBigInt32(instructionPtr, 0); - newInstruction = (instruction & 0xFC000003) | ((uint32_t)displacement & 0x03FFFFFC); - //fprintf(stderr, "bl fixup: 0x%08X -> 0x%08X\n", instruction, newInstruction); - OSWriteBigInt32(instructionPtr, 0, newInstruction); - } - break; - case Reference::ppcFixupBranch14: - break; - case Reference::ppcFixupPicBaseLow16: - { - uint64_t targetAddr = target.getAddress() + ref->getTargetOffset(); - uint64_t picBaseAddr = this->getAddress() + ref->getFromTargetOffset(); - int64_t displacement = targetAddr - picBaseAddr; - const int64_t picbase_twoGigLimit = 0x80000000; - if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) ) - throw "32-bit pic-base out of range"; - uint16_t instructionLowHalf = (displacement & 0xFFFF); - instruction = OSReadBigInt32(instructionPtr, 0); - newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; - OSWriteBigInt32(instructionPtr, 0, newInstruction); - } - break; - case Reference::ppcFixupPicBaseLow14: - { - uint64_t targetAddr = target.getAddress() + ref->getTargetOffset(); - uint64_t picBaseAddr = this->getAddress() + ref->getFromTargetOffset(); - int64_t displacement = targetAddr - picBaseAddr; - const int64_t picbase_twoGigLimit = 0x80000000; - if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) ) - throw "32-bit pic-base out of range"; - uint16_t instructionLowHalf = (displacement & 0xFFFF); - if ( (instructionLowHalf & 0x3) != 0 ) - throw "bad address for lo14 instruction fix-up"; - instruction = OSReadBigInt32(instructionPtr, 0); - newInstruction = (instruction & 0xFFFF0003) | instructionLowHalf; - OSWriteBigInt32(instructionPtr, 0, newInstruction); - } - break; - case Reference::ppcFixupPicBaseHigh16: - { - uint64_t targetAddr = target.getAddress() + ref->getTargetOffset(); - uint64_t picBaseAddr = this->getAddress() + ref->getFromTargetOffset(); - int64_t displacement = targetAddr - picBaseAddr; - const int64_t picbase_twoGigLimit = 0x80000000; - if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) ) - throw "32-bit pic-base out of range"; - uint16_t instructionLowHalf = displacement >> 16; - if ( (displacement & 0x00008000) != 0 ) - ++instructionLowHalf; - instruction = OSReadBigInt32(instructionPtr, 0); - newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; - OSWriteBigInt32(instructionPtr, 0, newInstruction); - } - break; - case Reference::ppcFixupAbsLow16: - { - int64_t addr = target.getAddress() + ref->getTargetOffset(); - if ( !finalLinkedImage && target.isImportProxy() ) - addr -= target.getAddress() ; - uint16_t instructionLowHalf = (addr & 0xFFFF); - instruction = OSReadBigInt32(instructionPtr, 0); - newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; - OSWriteBigInt32(instructionPtr, 0, newInstruction); - } - break; - case Reference::ppcFixupAbsLow14: - { - int64_t addr = target.getAddress() + ref->getTargetOffset(); - if ( !finalLinkedImage && target.isImportProxy() ) - addr -= target.getAddress() ; - uint16_t instructionLowHalf = (addr & 0xFFFF); - if ( (instructionLowHalf & 0x3) != 0 ) - throw "bad address for lo14 instruction fix-up"; - instruction = OSReadBigInt32(instructionPtr, 0); - newInstruction = (instruction & 0xFFFF0003) | instructionLowHalf; - OSWriteBigInt32(instructionPtr, 0, newInstruction); - } - break; - case Reference::ppcFixupAbsHigh16: - { - int64_t addr = target.getAddress() + ref->getTargetOffset(); - if ( !finalLinkedImage && target.isImportProxy() ) - addr -= target.getAddress() ; - uint16_t hi16 = (addr >> 16); - instruction = OSReadBigInt32(instructionPtr, 0); - newInstruction = (instruction & 0xFFFF0000) | hi16; - OSWriteBigInt32(instructionPtr, 0, newInstruction); - } - break; - case Reference::ppcFixupAbsHigh16AddLow: - { - int64_t addr = target.getAddress() + ref->getTargetOffset(); - if ( !finalLinkedImage && target.isImportProxy() ) - addr -= target.getAddress() ; - if ( addr & 0x00008000 ) - addr += 0x00010000; - instruction = OSReadBigInt32(instructionPtr, 0); - newInstruction = (instruction & 0xFFFF0000) | (addr >> 16); - OSWriteBigInt32(instructionPtr, 0, newInstruction); - } - break; - case Reference::pointer32Difference: - ENDIAN_WRITE32(*instructionPtr, target.getAddress() + ref->getTargetOffset() - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset())); - break; - case Reference::pointer64Difference: - *((uint64_t*)instructionPtr) = ENDIAN_SWAP64(target.getAddress() + ref->getTargetOffset() - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset())); - break; - case Reference::x86FixupBranch32: - { - int64_t displacement = target.getAddress() - (this->getAddress() + offset); - if ( target.isImportProxy() ) { - displacement = 0; - } - else { - const int64_t bl_twoGigLimit = 0x7FFFFFFF; - if ( (displacement > bl_twoGigLimit) || (displacement < (-bl_twoGigLimit)) ) { - //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); - throw "call out of range"; - } - } - OSWriteLittleInt32(instructionPtr, 0, (int32_t)displacement); - } - break; - } - } - - // write out - writer.write(0, buffer, getSize()); -} - - - -const macho_section* Atom::findSectionFromOffset(uint32_t offset) -{ - const macho_section* const sectionsStart = (const macho_section*)( (char*)fOwner.fSegment + sizeof(macho_segment_command) ); - const macho_section* const sectionsEnd = §ionsStart[fOwner.fSegment->nsects()]; - for (const macho_section* s = sectionsStart; s < sectionsEnd; ++s) { - if ( (s->addr() <= offset) && (offset < (s->addr()+s->size())) ) - return s; - } - throwf("address 0x%08X is not in any section", offset); -} - -void Atom::setSize(macho_uintptr_t size) -{ - fSize = size; -} - - -void Atom::setFollowOnAtom(Atom&) -{ - -} - -Reference* Atom::addReference(macho_uintptr_t offsetInSrcAtom, Reference::Kind kind, Atom& target, uint64_t offsetInTarget, uint64_t offsetInFromTarget) -{ - if ( (target.getScope() != ObjectFile::Atom::scopeTranslationUnit) && ((target.fSymbol != NULL) || (target.fSynthesizedName != NULL)) ) - return this->addByNameReference(offsetInSrcAtom, kind, target.getName(), offsetInTarget, offsetInFromTarget); - else - return this->addDirectReference(offsetInSrcAtom, kind, target, offsetInTarget, offsetInFromTarget); -} - - -Reference* Atom::addDirectReference(macho_uintptr_t offsetInSrcAtom, Reference::Kind kind, Atom& target, uint64_t offsetInTarget, uint64_t offsetInFromTarget) -{ - Reference* ref = new Reference(offsetInSrcAtom, kind, target, offsetInTarget, offsetInFromTarget); - // in rare cases, there may already be a by-name reference to the same atom. If so, replace with this direct reference - for (std::vector::iterator it=fReferences.begin(); it != fReferences.end(); it++) { - ObjectFile::Reference* aRef = *it; - if ( (aRef->getFixUpOffset() == offsetInSrcAtom) && (aRef->getKind() == kind) ) { - *it = ref; - return ref; - } - } - - // note: adding to start of list because mach-o relocs are in reverse offset order in the .o file - fReferences.insert(fReferences.begin(), ref); - return ref; -} - -Reference* Atom::addByNameReference(macho_uintptr_t offsetInSrcAtom, Reference::Kind kind, const char* targetName, uint64_t offsetInTarget, uint64_t offsetInFromTarget) -{ - Reference* ref = new Reference(offsetInSrcAtom, kind, targetName, offsetInTarget, offsetInFromTarget); - // note: adding to start of list because mach-o relocs are in reverse offset order in the .o file - fReferences.insert(fReferences.begin(), ref); - return ref; -} - -Reference* Atom::addDifferenceReference(macho_uintptr_t offsetInSrcAtom, Reference::Kind kind, Atom& target, uint64_t offsetInTarget, Atom& fromTarget, uint64_t offsetInFromTarget) -{ - Reference* ref = new Reference(offsetInSrcAtom, kind, target, offsetInTarget, fromTarget, offsetInFromTarget); - // note: adding to start of list because mach-o relocs are in reverse offset order in the .o file - fReferences.insert(fReferences.begin(), ref); - return ref; -} - - -Segment::Segment(const macho_section* sect) - : fSection(sect) -{ -} - -const char* Segment::getName() const -{ - return fSection->segname(); -} - -bool Segment::isContentReadable() const -{ - return true; -} - -bool Segment::isContentWritable() const -{ - if ( strcmp(fSection->segname(), "__DATA") == 0 ) - return true; - if ( strcmp(fSection->segname(), "__OBJC") == 0 ) - return true; - return false; -} - -bool Segment::isContentExecutable() const -{ - return ( strcmp(fSection->segname(), "__TEXT") == 0 ); -} - - -Reference::Reference(macho_uintptr_t fixUpOffset, Kind kind, const char* targetName, uint64_t offsetInTarget, uint64_t offsetInFromTarget) - : fTarget(NULL), fFromTarget(NULL), fTargetName(targetName), fFromTargetName(NULL), fTargetOffset(offsetInTarget), fFromTargetOffset(offsetInFromTarget), - fFixUpOffsetInSrc(fixUpOffset), fKind(kind), fLazy(false), fWeak(false) -{ -} - -Reference::Reference(macho_uintptr_t fixUpOffset, Kind kind, class Atom& target, uint64_t offsetInTarget, uint64_t offsetInFromTarget) - : fTarget(&target), fFromTarget(NULL), fTargetName(NULL), fFromTargetName(NULL), fTargetOffset(offsetInTarget), fFromTargetOffset(offsetInFromTarget), - fFixUpOffsetInSrc(fixUpOffset), fKind(kind), fLazy(false), fWeak(false) -{ -} - -Reference::Reference(macho_uintptr_t fixUpOffset, Kind kind, class Atom& target, uint64_t offsetInTarget, class Atom& fromTarget, uint64_t offsetInFromTarget) - : fTarget(&target), fFromTarget(&fromTarget), fTargetName(NULL), fFromTargetName(NULL), fTargetOffset(offsetInTarget), fFromTargetOffset(offsetInFromTarget), - fFixUpOffsetInSrc(fixUpOffset), fKind(kind), fLazy(false), fWeak(false) -{ - // assure no direct references to something that might be coalesced - if ( (target.isWeakDefinition() || target.isCoalesableByName()) && (target.getScope() != ObjectFile::Atom::scopeTranslationUnit) && (target.getName() != NULL) ) { - //fprintf(stderr, "change TO direct reference to by-name: from %s to %s in %p\n", fromTarget.getDisplayName(), target.getName(), this); - fTargetName = target.getName(); - fTarget = NULL; - } -// Note: We should also allow by-name from references, but many other chunks of code assume from targets are always direct// -// if ( (fromTarget.isWeakDefinition() || fromTarget.isCoalesableByName()) && (fromTarget.getScope() != ObjectFile::Atom::scopeTranslationUnit) && (fromTarget.getName() != NULL)) { -// fprintf(stderr, "change FROM direct reference to by-name: from %s to %s in %p\n", fromTarget.getDisplayName(), target.getName(), this); -// fFromTargetName = fromTarget.getName(); -// fFromTarget = NULL; -// } -} - - - -Reference::~Reference() -{ -} - -bool Reference::isTargetUnbound() const -{ - return ( fTarget == NULL ); -} - -bool Reference::isFromTargetUnbound() const -{ - return ( fFromTarget == NULL ); -} - -bool Reference::isWeakReference() const -{ - return fWeak; -} - -bool Reference::requiresRuntimeFixUp(bool slideable) const -{ - // This static linker only supports pure code (no code fixups are runtime) -#if defined(ARCH_PPC) || defined(ARCH_PPC64) - // Only data can be fixed up, and the codegen assures only "pointers" need runtime fixups - return ( (fKind == Reference::pointer) && (fTarget->isImportProxy() || fTarget->isWeakDefinition() || slideable) ); -#elif defined(ARCH_I386) - // For i386, Reference::pointer is used for both data pointers and instructions with 32-bit absolute operands - if ( fKind == Reference::pointer ) { - if ( fTarget->isImportProxy() ) - return true; - else - return slideable; - } - return false; -#else - #error -#endif -} - -bool Reference::isLazyReference() const -{ - return fLazy; -} - -ObjectFile::Reference::Kind Reference::getKind() const -{ - return fKind; -} - -uint64_t Reference::getFixUpOffset() const -{ - return fFixUpOffsetInSrc; -} - -const char* Reference::getTargetName() const -{ - if ( fTargetName != NULL ) - return fTargetName; - return fTarget->getName(); -} - -ObjectFile::Atom& Reference::getTarget() const -{ - return *fTarget; -} - -void Reference::setTarget(ObjectFile::Atom& target, uint64_t offset) -{ - fTarget = ⌖ - fTargetOffset = offset; -} - - -ObjectFile::Atom& Reference::getFromTarget() const -{ - return *fFromTarget; -} - -bool Reference::hasFromTarget() const -{ - return ( (fFromTarget != NULL) || (fFromTargetName != NULL) ); -} - -const char* Reference::getFromTargetName() const -{ - if ( fFromTargetName != NULL ) - return fFromTargetName; - return fFromTarget->getName(); -} - -void Reference::setFromTarget(ObjectFile::Atom& target) -{ - fFromTarget = ⌖ -} - -void Reference::setFromTargetName(const char* name) -{ - fFromTargetName = name; -} - -void Reference::setFromTargetOffset(uint64_t offset) -{ - fFromTargetOffset = offset; -} - -uint64_t Reference::getTargetOffset() const -{ - return fTargetOffset; -} - - -uint64_t Reference::getFromTargetOffset() const -{ - return fFromTargetOffset; -} - -void Reference::setLazy(bool lazy) -{ - fLazy = lazy; -} - -void Reference::setWeak(bool weak) -{ - fWeak = weak; -} - -const char* Reference::getDescription() const -{ - static char temp[256]; - if ( fKind == pointer32Difference ) { - // by-name references have quoted names - bool targetByName = ( &(this->getTarget()) == NULL ); - bool fromByName = ( &(this->getFromTarget()) == NULL ); - const char* targetQuotes = targetByName ? "\"" : ""; - const char* fromQuotes = fromByName ? "\"" : ""; - sprintf(temp, "offset 0x%04llX, 32-bit pointer difference: (&%s%s%s + %lld) - (&%s%s%s + %lld)", - this->getFixUpOffset(), targetQuotes, this->getTargetName(), targetQuotes, this->getTargetOffset(), - fromQuotes, this->getFromTargetName(), fromQuotes, this->getFromTargetOffset() ); - } - else if ( fKind == pointer64Difference ) { - // by-name references have quoted names - bool targetByName = ( &(this->getTarget()) == NULL ); - bool fromByName = ( &(this->getFromTarget()) == NULL ); - const char* targetQuotes = targetByName ? "\"" : ""; - const char* fromQuotes = fromByName ? "\"" : ""; - sprintf(temp, "offset 0x%04llX, 64-bit pointer difference: (&%s%s%s + %lld) - (&%s%s%s + %lld)", - this->getFixUpOffset(), targetQuotes, this->getTargetName(), targetQuotes, this->getTargetOffset(), - fromQuotes, this->getFromTargetName(), fromQuotes, this->getFromTargetOffset() ); - } - else { - switch( fKind ) { - case noFixUp: - sprintf(temp, "reference to "); - break; - case pointer: - { - const char* weak = ""; - if ( fWeak ) - weak = "weak "; - const char* lazy = ""; - if ( fLazy ) - lazy = "lazy "; - sprintf(temp, "offset 0x%04llX, %s%spointer to ", this->getFixUpOffset(), weak, lazy); - } - break; - case ppcFixupBranch14: - case ppcFixupBranch24: - sprintf(temp, "offset 0x%04llX, bl pc-rel fixup to ", this->getFixUpOffset()); - break; - case ppcFixupPicBaseLow16: - sprintf(temp, "offset 0x%04llX, low 16 fixup from pic-base offset 0x%04llX to ", this->getFixUpOffset(), this->getFromTargetOffset()); - break; - case ppcFixupPicBaseLow14: - sprintf(temp, "offset 0x%04llX, low 14 fixup from pic-base offset 0x%04llX to ", this->getFixUpOffset(), this->getFromTargetOffset()); - break; - case ppcFixupPicBaseHigh16: - sprintf(temp, "offset 0x%04llX, high 16 fixup from pic-base offset 0x%04llX to ", this->getFixUpOffset(), this->getFromTargetOffset()); - break; - case ppcFixupAbsLow16: - sprintf(temp, "offset 0x%04llX, low 16 fixup to absolute address of ", this->getFixUpOffset()); - break; - case ppcFixupAbsLow14: - sprintf(temp, "offset 0x%04llX, low 14 fixup to absolute address of ", this->getFixUpOffset()); - break; - case ppcFixupAbsHigh16: - sprintf(temp, "offset 0x%04llX, high 16 fixup to absolute address of ", this->getFixUpOffset()); - break; - case ppcFixupAbsHigh16AddLow: - sprintf(temp, "offset 0x%04llX, high 16 fixup to absolute address of ", this->getFixUpOffset()); - break; - case pointer32Difference: - case pointer64Difference: - // handled above - break; - case x86FixupBranch32: - sprintf(temp, "offset 0x%04llX, pc-rel fixup to ", this->getFixUpOffset()); - break; - } - // always quote by-name references - if ( fTargetName != NULL ) { - strcat(temp, "\""); - strcat(temp, fTargetName); - strcat(temp, "\""); - } - else if ( fTarget != NULL ) { - strcat(temp, fTarget->getDisplayName()); - } - else { - strcat(temp, "NULL target"); - } - if ( fTargetOffset != 0 ) - sprintf(&temp[strlen(temp)], " plus 0x%08llX", this->getTargetOffset()); - if ( (fKind==pointer) && fLazy ) { - strcat(temp, " initially bound to \""); - if ( (fFromTarget != NULL) || (fFromTargetName != NULL) ) { - strcat(temp, this->getFromTargetName()); - strcat(temp, "\""); - if ( this->getFromTargetOffset() != 0 ) - sprintf(&temp[strlen(temp)], " plus 0x%08llX", this->getFromTargetOffset()); - } - else { - strcat(temp, "\" << missing >>"); - } - } - } - return temp; -} - -}; - - - - - - - diff --git a/src/SectCreate.cpp b/src/SectCreate.cpp index 4d48a37..7296c1a 100644 --- a/src/SectCreate.cpp +++ b/src/SectCreate.cpp @@ -1,5 +1,6 @@ -/* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -48,9 +49,11 @@ public: virtual ~Reader(); virtual const char* getPath() { return fPath; } + virtual time_t getModificationTime() { return 0; } + virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; } virtual std::vector& getAtoms() { return fAtoms; } virtual std::vector* getJustInTimeAtomsFor(const char* name) { return NULL; } - virtual std::vector* getStabsDebugInfo() { return NULL; } + virtual std::vector* getStabs() { return NULL; } private: const char* fPath; @@ -61,17 +64,13 @@ private: class Atom : public ObjectFile::Atom { public: virtual ObjectFile::Reader* getFile() const { return &fOwner; } + virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; } virtual const char* getName() const { return NULL; } virtual const char* getDisplayName() const; 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 DefinitionKind getDefinitionKind() const { return kRegularDefinition; } + virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } 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 uint64_t getSize() const { return fFileLength; } virtual std::vector& getReferences() const { return fgEmptyReferenceList; } virtual bool mustRemainInSection() const { return false; } @@ -79,20 +78,17 @@ public: virtual 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 std::vector* getLineInfo() const { return NULL; } virtual uint8_t getAlignment() const { return 4; } - virtual WeakImportSetting getImportWeakness() const { return ObjectFile::Atom::kWeakUnset; } virtual void copyRawContent(uint8_t buffer[]) const; - virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const; virtual void setScope(Scope) { } - virtual void setImportWeakness(bool) { } protected: friend class Reader; Atom(Reader& owner, Segment& segment, const char* sectionName, const uint8_t fileContent[], uint64_t fileLength) - : fOwner(owner), fSegment(segment), fSectionName(sectionName), fFileContent(fileContent), fFileLength(fileLength) {} + : fOwner(owner), fSegment(segment), fSectionName(sectionName), fFileContent(fileContent), fFileLength(fileLength) { setDontDeadStrip(); } virtual ~Atom() {} Reader& fOwner; @@ -133,12 +129,6 @@ void Atom::copyRawContent(uint8_t buffer[]) const memcpy(buffer, fFileContent, fFileLength); } -void Atom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const -{ - writer.write(0, fFileContent, fFileLength); -} - - Reader* MakeReader(const char* segmentName, const char* sectionName, const char* path, const uint8_t fileContent[], uint64_t fileLength) { return new Reader(segmentName, sectionName, path, fileContent, fileLength); diff --git a/src/SectCreate.h b/src/SectCreate.h index 73b49a5..d5c7f4e 100644 --- a/src/SectCreate.h +++ b/src/SectCreate.h @@ -1,4 +1,5 @@ -/* +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ diff --git a/src/Writers/ExecutableFileMachO-all.cpp b/src/Writers/ExecutableFileMachO-all.cpp deleted file mode 100644 index 661a3df..0000000 --- a/src/Writers/ExecutableFileMachO-all.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/* - * 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@ - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "ObjectFile.h" -#include "ExecutableFile.h" -#include "Options.h" - -// buiild Writer for -arch ppc64 -#undef MACHO_32_SAME_ENDIAN -#undef MACHO_32_OPPOSITE_ENDIAN -#undef MACHO_64_SAME_ENDIAN -#undef MACHO_64_OPPOSITE_ENDIAN -#if __ppc__ || __ppc64__ - #define MACHO_64_SAME_ENDIAN -#elif __i386__ - #define MACHO_64_OPPOSITE_ENDIAN -#else - #error unknown architecture -#endif -namespace ppc64 { - #undef ARCH_PPC - #define ARCH_PPC64 - #undef ARCH_I386 - #include "MachOAbstraction.h" - #include "ExecutableFileMachO.cpp" -}; - -// buiild Writer for -arch ppc -#undef MACHO_32_SAME_ENDIAN -#undef MACHO_32_OPPOSITE_ENDIAN -#undef MACHO_64_SAME_ENDIAN -#undef MACHO_64_OPPOSITE_ENDIAN -#if __ppc__ || __ppc64__ - #define MACHO_32_SAME_ENDIAN -#elif __i386__ - #define MACHO_32_OPPOSITE_ENDIAN -#else - #error unknown architecture -#endif -namespace ppc { - #define ARCH_PPC - #undef ARCH_PPC64 - #undef ARCH_I386 - #include "MachOAbstraction.h" - #include "ExecutableFileMachO.cpp" -}; - -// buiild Writer for -arch i386 -#undef MACHO_32_SAME_ENDIAN -#undef MACHO_32_OPPOSITE_ENDIAN -#undef MACHO_64_SAME_ENDIAN -#undef MACHO_64_OPPOSITE_ENDIAN -#if __ppc__ || __ppc64__ - #define MACHO_32_OPPOSITE_ENDIAN -#elif __i386__ - #define MACHO_32_SAME_ENDIAN -#else - #error unknown architecture -#endif -#undef i386 // compiler sometimes #defines this -namespace i386 { - #undef ARCH_PPC - #undef ARCH_PPC64 - #define ARCH_I386 - #include "MachOAbstraction.h" - #include "ExecutableFileMachO.cpp" -}; - - diff --git a/src/Writers/ExecutableFileMachO-all.h b/src/Writers/ExecutableFileMachO-all.h deleted file mode 100644 index 08d4ae6..0000000 --- a/src/Writers/ExecutableFileMachO-all.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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@ - */ - - -#ifndef __EXECUTABLEFILEMACHO__ -#define __EXECUTABLEFILEMACHO__ - -class Options; - -namespace ppc { - namespace ExecutableFileMachO { - extern class ExecutableFile::Writer* MakeWriter(const char* path, Options&, std::vector&); - } -}; - -namespace ppc64 { - namespace ExecutableFileMachO { - extern class ExecutableFile::Writer* MakeWriter(const char* path, Options&, std::vector&); - } -}; - -#undef i386 // compiler sometimes #defines this -namespace i386 { - namespace ExecutableFileMachO { - extern class ExecutableFile::Writer* MakeWriter(const char* path, Options&, std::vector&); - } -}; - - - -#endif // __EXECUTABLEFILEMACHO__ - - - diff --git a/src/Writers/ExecutableFileMachO.cpp b/src/Writers/ExecutableFileMachO.cpp deleted file mode 100644 index 90a9527..0000000 --- a/src/Writers/ExecutableFileMachO.cpp +++ /dev/null @@ -1,2807 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * 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(); - bool addBranchIslands(); - 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; - bool fFixedAddress; - }; - - - 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 fReaderOrder; - 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, bool fixedAddress) - : fName(name), fReadable(readable), fWritable(writable), fExecutable(executable), fFixedAddress(fixedAddress) {} - virtual const char* getName() const { return fName; } - virtual bool isContentReadable() const { return fReadable; } - virtual bool isContentWritable() const { return fWritable; } - virtual bool isContentExecutable() const { return fExecutable; } - virtual bool hasFixedAddress() const { return fFixedAddress; } - private: - const char* fName; - const bool fReadable; - const bool fWritable; - const bool fExecutable; - const bool fFixedAddress; - }; - - 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, true); -WriterAtom::Segment WriterAtom::fgTextSegment("__TEXT", true, false, true, false); -WriterAtom::Segment WriterAtom::fgLinkEditSegment("__LINKEDIT", true, false, false, false); -WriterAtom::Segment WriterAtom::fgStackSegment("__UNIXSTACK", true, true, false, true); -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; -}; - -#if defined(ARCH_PPC) || defined(ARCH_PPC64) -class BranchIslandAtom : public WriterAtom -{ -public: - BranchIslandAtom(Writer& writer, const char* name, int islandRegion, ObjectFile::Atom& target, uint32_t targetOffset); - virtual const char* getName() const { return fName; } - virtual Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual uint64_t getSize() const { return 4; } - virtual const char* getSectionName() const { return "__text"; } - virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const; -private: - const char* fName; - ObjectFile::Atom& fTarget; - uint32_t fTargetOffset; -}; -#endif - -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), fFixedAddress(false) -{ - 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(); - - // if need to add branch islands, reassign file offsets - if ( addBranchIslands() ) - 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.fReaderOrder < rhs.fReaderOrder; - } - return lhs.fOrderInReader < rhs.fOrderInReader; -} - -unsigned int Writer::collectStabs() -{ - unsigned int count = 0; - - // collect all stabs chunks - std::set seenReaders; - std::map readerOrdinals; - const int atomCount = fAllAtoms->size(); - for (int i=0; i < atomCount; ++i) { - ObjectFile::Atom* atom = (*fAllAtoms)[i]; - ObjectFile::Reader* atomsReader = atom->getFile(); - unsigned int readerOrder = 0; - if ( atomsReader != NULL ) { - std::map::iterator pos = readerOrdinals.find(atomsReader); - if ( pos == readerOrdinals.end() ) { - readerOrder = readerOrdinals.size(); - readerOrdinals[atomsReader] = readerOrder; - std::vector* readerStabs = atomsReader->getStabsDebugInfo(); - if ( readerStabs != NULL ) { - StabChunks chunk; - chunk.fAtom = NULL; - chunk.fReader = atomsReader; - chunk.fReaderOrder = readerOrder; - chunk.fOrderInReader = 0; - chunk.fStabs = readerStabs; - fStabChunks.push_back(chunk); - count += readerStabs->size() + 1; // extra one is for trailing N_SO - } - } - else { - readerOrder = pos->second; - } - } - std::vector* atomStabs = atom->getStabsDebugInfo(); - if ( atomStabs != NULL ) { - StabChunks chunk; - chunk.fAtom = atom; - chunk.fReader = atomsReader; - chunk.fReaderOrder = readerOrder; - chunk.fOrderInReader = atom->getSortOrder(); - chunk.fStabs = atomStabs; - fStabChunks.push_back(chunk); - count += atomStabs->size(); - } - } - - // sort stabs: group by .o file - std::sort(fStabChunks.begin(), fStabChunks.end(), stabChunkCompare); - - //fprintf(stderr, "Sorted stabs:\n"); - //for (std::vector::iterator it=fStabChunks.begin(); it != fStabChunks.end(); it++) { - // ObjectFile::Atom* atom = (*it).fAtom; - // if ( atom != NULL ) - // fprintf(stderr, "\t%s\n", (*it).fAtom->getDisplayName()); - // else - // fprintf(stderr, "\t%s\n", (*it).fReader->getPath()); - //} - - 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 - int i = 0; - for(std::vector::iterator it=fImportedAtoms.begin(); it != fImportedAtoms.end(); ++it) { - if ( &atom == *it ) - return i + fSymbolTableImportStartIndex; - ++i; - } - - // search locals - i = 0; - for(std::vector::iterator it=fLocalSymbolAtoms.begin(); it != fLocalSymbolAtoms.end(); ++it) { - if ( &atom == *it ) - return i + fSymbolTableLocalStartIndex; - ++i; - } - - // search exports - i = 0; - for(std::vector::iterator it=fExportedAtoms.begin(); it != fExportedAtoms.end(); ++it) { - if ( &atom == *it ) - return i + fSymbolTableExportStartIndex; - ++i; - } - - 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]; - 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); - if ( curSection->fAllLazyPointers ) { - ObjectFile::Atom& target = ref->getTarget(); - ObjectFile::Atom& fromTarget = ref->getFromTarget(); - if ( &fromTarget == NULL ) { - fprintf(stderr, "lazy pointer %s missing initial binding\n", atom->getDisplayName()); - } - else { - bool isExtern = target.isImportProxy(); - uint32_t symbolIndex = 0; - if ( isExtern ) - symbolIndex = this->symbolIndex(target); - uint32_t sectionNum = target.getSection()->getIndex(); - uint32_t address = atom->getSectionOffset(); - macho_relocation_info reloc1; - 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); - ++relocIndex; - } - } - } - else { - relocIndex += this->addRelocs(atom, ref); - } - } - } - curSection->fRelocCount = relocIndex - curSection->fRelocOffset; - } - } - } - - // now reverse reloc entries - for(int i=0; i < segCount; ++i) { - SegmentInfo* curSegment = segmentInfos[i]; - std::vector& sectionInfos = curSegment->fSections; - const int sectionCount = sectionInfos.size(); - for(int j=0; j < sectionCount; ++j) { - SectionInfo* curSection = sectionInfos[j]; - curSection->fRelocOffset = relocIndex - curSection->fRelocOffset - curSection->fRelocCount; - } - } - -} - - -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]; - 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("wrong size 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; - //fprintf(stderr,"indirect pointer atom %p %s section offset = %d\n", atom, 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 }; - //fprintf(stderr,"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 vanilla internal relocation to fix up binding handler if image slides - macho_relocation_info pblaReloc; - SectionInfo* sectInfo = (SectionInfo*)ref->getFromTarget().getSection(); - uint32_t sectionNum = sectInfo->getIndex(); - pblaReloc.set_r_address(atom->getAddress()-fOptions.baseAddress()); - pblaReloc.set_r_symbolnum(sectionNum); - pblaReloc.set_r_pcrel(false); - pblaReloc.set_r_length(macho_relocation_info::pointer_length); - pblaReloc.set_r_extern(false); - pblaReloc.set_r_type(GENERIC_RELOC_VANILLA); - fInternalRelocs.push_back(pblaReloc); - } - } - else if ( ref->requiresRuntimeFixUp(slideable) ) { - 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 ( 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 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 - } - end = offset+atom->getSize(); - //fprintf(stderr, "writing 0x%08X -> 0x%08X, atom %s\n", offset, end, atom->getDisplayName()); - ContentWriter writer(fFileDescriptor, offset); - atom->writeContent(requireAllFixUps, writer); - } - } - } - } - } -} - - -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(); - currentSegmentInfo->fFixedAddress = atom->getSegment().hasFixedAddress(); - 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, "__la_sym_ptr2") == 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); - } -} - - -struct TargetAndOffset { ObjectFile::Atom* atom; uint32_t offset; }; -class TargetAndOffsetComparor -{ -public: - bool operator()(const TargetAndOffset& left, const TargetAndOffset& right) const - { - if ( left.atom != right.atom ) - return ( left.atom < right.atom ); - return ( left.offset < right.offset ); - } -}; - -// -// PowerPC can do PC relative branches as far as +/-16MB. -// If a branch target is >16MB then we insert one or more -// "branch islands" between the branch and its target that -// allows island hoping to the target. -// -// Branch Island Algorithm -// -// If the __TEXT segment < 16MB, then no branch islands needed -// Otherwise, every 15MB into the __TEXT segment is region is -// added which can contain branch islands. Every out of range -// bl instruction is checked. If it crosses a region, an island -// is added to that region with the same target and the bl is -// adjusted to target the island instead. -// -// In theory, if too many islands are added to one region, it -// could grow the __TEXT enough that other previously in-range -// bl branches could be pushed out of range. We reduce the -// probability this could happen by placing the ranges every -// 15MB which means the region would have to be 1MB (256K islands) -// before any branches could be pushed out of range. -// -bool Writer::addBranchIslands() -{ - bool result = false; -#if defined(ARCH_PPC) || defined(ARCH_PPC64) - // Can only possibly need branch islands if __TEXT segment > 16M - if ( fLoadCommandsSegment->fSize > 16000000 ) { - const uint32_t kBetweenRegions = 15000000; // place regions of islands every 15MB in __text section - SectionInfo* textSection = NULL; - for (std::vector::iterator it=fLoadCommandsSegment->fSections.begin(); it != fLoadCommandsSegment->fSections.end(); it++) { - if ( strcmp((*it)->fSectionName, "__text") == 0 ) - textSection = *it; - } - const int kIslandRegionsCount = textSection->fSize / kBetweenRegions; - typedef std::map AtomToIsland; - AtomToIsland regionsMap[kIslandRegionsCount]; - std::vector regionsIslands[kIslandRegionsCount]; - unsigned int islandCount = 0; - - // create islands for branch references that are out of range - for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - std::vector& references = atom->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - if ( ref->getKind() == ObjectFile::Reference::ppcFixupBranch24 ) { - ObjectFile::Atom& target = ref->getTarget(); - int64_t srcAddr = atom->getAddress() + ref->getFixUpOffset(); - int64_t dstAddr = target.getAddress() + ref->getTargetOffset(); - int64_t displacement = dstAddr - srcAddr; - const int64_t kFifteenMegLimit = kBetweenRegions; - if ( (displacement > kFifteenMegLimit) || (displacement < (-kFifteenMegLimit)) ) { - for (int i=0; i < kIslandRegionsCount; ++i) { - AtomToIsland* region=®ionsMap[i]; - int64_t islandRegionAddr = kBetweenRegions * (i+1); - if ( ((srcAddr < islandRegionAddr) && (dstAddr > islandRegionAddr)) - ||((dstAddr < islandRegionAddr) && (srcAddr > islandRegionAddr)) ) { - TargetAndOffset islandTarget = { &target, ref->getTargetOffset() }; - AtomToIsland::iterator pos = region->find(islandTarget); - if ( pos == region->end() ) { - BranchIslandAtom* island = new BranchIslandAtom(*this, target.getDisplayName(), i, target, ref->getTargetOffset()); - (*region)[islandTarget] = island; - regionsIslands[i].push_back(island); - ++islandCount; - ref->setTarget(*island, 0); - } - else { - ref->setTarget(*(pos->second), 0); - } - } - } - } - } - } - } - - // insert islands into __text section and adjust section offsets - if ( islandCount > 0 ) { - std::vector newAtomList; - newAtomList.reserve(textSection->fAtoms.size()+islandCount); - uint64_t islandRegionAddr = kBetweenRegions; - int regionIndex = 0; - uint64_t sectionOffset = 0; - for (std::vector::iterator it=textSection->fAtoms.begin(); it != textSection->fAtoms.end(); it++) { - ObjectFile::Atom* atom = *it; - newAtomList.push_back(atom); - if ( atom->getAddress() > islandRegionAddr ) { - std::vector* regionIslands = ®ionsIslands[regionIndex]; - for (std::vector::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) { - ObjectFile::Atom* islandAtom = *rit; - newAtomList.push_back(islandAtom); - islandAtom->setSection(textSection); - uint64_t alignment = 1 << (islandAtom->getAlignment()); - sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) ); - islandAtom->setSectionOffset(sectionOffset); - sectionOffset += islandAtom->getSize(); - } - ++regionIndex; - islandRegionAddr += kBetweenRegions; - } - uint64_t alignment = 1 << (atom->getAlignment()); - sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) ); - atom->setSectionOffset(sectionOffset); - sectionOffset += atom->getSize(); - } - textSection->fAtoms = newAtomList; - textSection->fSize = sectionOffset; - result = true; - } - - } -#endif - return result; -} - - -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 = 0; - bool baseAddressUsed = false; - 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->fFixedAddress ) { - // segment has fixed address already set - haveFixedSegments = true; - } - else { - // segment uses next address - if ( !baseAddressUsed ) { - baseAddressUsed = true; - if ( fOptions.baseAddress() != 0 ) - nextContiguousAddress = fOptions.baseAddress(); - } - curSegment->fBaseAddress = nextContiguousAddress; - } - 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; - } - } - // page align segment size - curSegment->fFileSize = (curSegment->fFileSize+4095) & (-4096); - curSegment->fSize = (curSegment->fSize+4095) & (-4096); - if ( curSegment->fBaseAddress == nextContiguousAddress ) - nextContiguousAddress = (curSegment->fBaseAddress+curSegment->fSize+4095) & (-4096); - } - - // check for segment overlaps - 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+4095) & (-4096); - } -} - - -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()); -#if defined(ARCH_PPC) || defined(ARCH_PPC64) - mh.set_cpusubtype(CPU_SUBTYPE_POWERPC_ALL); -#elif defined(ARCH_I386) - mh.set_cpusubtype(CPU_SUBTYPE_I386_ALL); -#else - #error unknown architecture -#endif - 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); - } - else if ( (strcmp(sectInfo->fSectionName, "__textcoal_nt") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { - sect->set_flags(S_COALESCED); - } - } - } - 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]; - bzero(buffer, size); - macho_dylinker_command* cmd = (macho_dylinker_command*)buffer; - if ( fWriter.fOptions.outputKind() == Options::kDyld ) - cmd->set_cmd(LC_ID_DYLINKER); - else - cmd->set_cmd(LC_LOAD_DYLINKER); - cmd->set_cmdsize(this->getSize()); - cmd->set_name_offset(); - strcpy((char*)&buffer[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 { - throwf("malformed indirect table. size=%d, index=%d", indirectTableSize, entry.indirectIndex); - } - } - 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; -} - -#if defined(ARCH_PPC) || defined(ARCH_PPC64) -BranchIslandAtom::BranchIslandAtom(Writer& writer, const char* name, int islandRegion, ObjectFile::Atom& target, uint32_t targetOffset) - : WriterAtom(writer, fgTextSegment), fTarget(target), fTargetOffset(targetOffset) -{ - char* buf = new char[strlen(name)+32]; - if ( targetOffset == 0 ) { - if ( islandRegion == 0 ) - sprintf(buf, "%s$island", name); - else - sprintf(buf, "%s$island_%d", name, islandRegion); - } - else { - sprintf(buf, "%s_plus_%d$island_%d", name, targetOffset, islandRegion); - } - fName = buf; -} - - -void BranchIslandAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const -{ - int64_t displacement = fTarget.getAddress() + fTargetOffset - this->getAddress(); - uint8_t instruction[4]; - int32_t branchInstruction = 0x48000000 | ((uint32_t)displacement & 0x03FFFFFC); - OSWriteBigInt32(&instruction, 0, branchInstruction); - writer.write(0, &instruction, 4); -} - - -#endif - - -}; - - - diff --git a/src/debugline.c b/src/debugline.c new file mode 100644 index 0000000..ff0e1d9 --- /dev/null +++ b/src/debugline.c @@ -0,0 +1,546 @@ +/* + * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#ifndef KLD +#include +#include +#include +#include +#include "dwarf2.h" +#include "debugline.h" + +struct line_reader_data +{ + bool little_endian; + + /* From the line number information header. */ + uint8_t minimum_instruction_length; + int8_t line_base; + uint8_t line_range; + uint8_t opcode_base; + const uint8_t * standard_opcode_lengths; + size_t numdir; + const uint8_t * * dirnames; + size_t numfile_orig; + size_t numfile; /* As updated during execution of the table. */ + const uint8_t * * filenames; + + /* Current position in the line table. */ + const uint8_t * cpos; + /* End of this part of the line table. */ + const uint8_t * end; + /* Start of the line table. */ + const uint8_t * init; + + struct line_info cur; +}; + +/* Read in a word of fixed size, which may be unaligned, in the + appropriate endianness. */ +#define read_16(p) (lnd->little_endian \ + ? ((p)[1] << 8 | (p)[0]) \ + : ((p)[0] << 8 | (p)[1])) +#define read_32(p) (lnd->little_endian \ + ? ((p)[3] << 24 | (p)[2] << 16 | (p)[1] << 8 | (p)[0]) \ + : ((p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3])) +#define read_64(p) (lnd->little_endian \ + ? ((uint64_t) (p)[7] << 56 | (uint64_t) (p)[6] << 48 \ + | (uint64_t) (p)[5] << 40 | (uint64_t) (p)[4] << 32 \ + | (uint64_t) (p)[3] << 24 | (uint64_t) (p)[2] << 16u \ + | (uint64_t) (p)[1] << 8 | (uint64_t) (p)[0]) \ + : ((uint64_t) (p)[0] << 56 | (uint64_t) (p)[1] << 48 \ + | (uint64_t) (p)[2] << 40 | (uint64_t) (p)[3] << 32 \ + | (uint64_t) (p)[4] << 24 | (uint64_t) (p)[5] << 16u \ + | (uint64_t) (p)[6] << 8 | (uint64_t) (p)[7])) + +/* Skip over a LEB128 value (signed or unsigned). */ +static void +skip_leb128 (struct line_reader_data * leb) +{ + while (leb->cpos != leb->end && *leb->cpos >= 0x80) + leb->cpos++; + if (leb->cpos != leb->end) + leb->cpos++; +} + +/* Read a ULEB128 into a 64-bit word. Return (uint64_t)-1 on overflow + or error. On overflow, skip past the rest of the uleb128. */ +static uint64_t +read_uleb128 (struct line_reader_data * leb) +{ + uint64_t result = 0; + int bit = 0; + + do { + uint64_t b; + + if (leb->cpos == leb->end) + return (uint64_t) -1; + + b = *leb->cpos & 0x7f; + + if (bit >= 64 || b << bit >> bit != b) + result = (uint64_t) -1; + else + result |= b << bit, bit += 7; + } while (*leb->cpos++ >= 0x80); + return result; +} + + +/* Read a SLEB128 into a 64-bit word. Return 0 on overflow or error + (which is not very helpful). On overflow, skip past the rest of + the SLEB128. For negative numbers, this actually overflows when + under -2^62, but since this is used for line numbers that ought to + be OK... */ +static int64_t +read_sleb128 (struct line_reader_data * leb) +{ + const uint8_t * start_pos = leb->cpos; + uint64_t v = read_uleb128 (leb); + uint64_t signbit; + + if (v >= 1ull << 63) + return 0; + if (leb->cpos - start_pos > 9) + return v; + + signbit = 1ull << ((leb->cpos - start_pos) * 7 - 1); + + return v | -(v & signbit); +} + +/* Free a line_reader_data structure. */ +void +line_free (struct line_reader_data * lnd) +{ + if (! lnd) + return; + if (lnd->dirnames) + free (lnd->dirnames); + if (lnd->filenames) + free (lnd->filenames); + free (lnd); +} + +/* Return the pathname of the file in S, or NULL on error. + The result will have been allocated with malloc. */ + +char * +line_file (struct line_reader_data *lnd, uint64_t n) +{ + const uint8_t * prev_pos = lnd->cpos; + size_t filelen, dirlen; + uint64_t dir; + char * result; + + /* I'm not sure if this is actually an error. */ + if (n == 0 + || n > lnd->numfile) + return NULL; + + filelen = strlen ((const char *)lnd->filenames[n - 1]); + lnd->cpos = lnd->filenames[n - 1] + filelen + 1; + dir = read_uleb128 (lnd); + lnd->cpos = prev_pos; + if (dir == 0 + || lnd->filenames[n - 1][0] == '/') + return strdup ((const char *)lnd->filenames[n - 1]); + else if (dir > lnd->numdir) + return NULL; + + dirlen = strlen ((const char *) lnd->dirnames[dir - 1]); + result = malloc (dirlen + filelen + 2); + memcpy (result, lnd->dirnames[dir - 1], dirlen); + result[dirlen] = '/'; + memcpy (result + dirlen + 1, lnd->filenames[n - 1], filelen); + result[dirlen + 1 + filelen] = '\0'; + return result; +} + +/* Initialize a state S. Return FALSE on error. */ + +static void +init_state (struct line_info *s) +{ + s->file = 1; + s->line = 1; + s->col = 0; + s->pc = 0; + s->end_of_sequence = false; +} + +/* Read a debug_line section. */ + +struct line_reader_data * +line_open (const uint8_t * debug_line, size_t debug_line_size, + int little_endian) +{ + struct line_reader_data * lnd = NULL; + bool dwarf_size_64; + + uint64_t lnd_length, header_length; + const uint8_t * table_start; + + if (debug_line_size < 12) + return NULL; + + lnd = malloc (sizeof (struct line_reader_data)); + if (! lnd) + goto error; + + lnd->little_endian = little_endian; + lnd->cpos = debug_line; + + lnd_length = read_32 (lnd->cpos); + lnd->cpos += 4; + if (lnd_length == 0xffffffff) + { + lnd_length = read_64 (lnd->cpos); + lnd->cpos += 8; + dwarf_size_64 = true; + } + else if (lnd_length > 0xfffffff0) + /* Not a format we understand. */ + goto error; + else + dwarf_size_64 = false; + + if (debug_line_size < lnd_length + (dwarf_size_64 ? 12 : 4) + || lnd_length < (dwarf_size_64 ? 15 : 11)) + /* Too small. */ + goto error; + + if (read_16 (lnd->cpos) != 2) + /* Unknown line number format. */ + goto error; + lnd->cpos += 2; + + header_length = dwarf_size_64 ? (uint64_t)read_64(lnd->cpos) : (uint64_t)read_32(lnd->cpos); + lnd->cpos += dwarf_size_64 ? 8 : 4; + if (lnd_length < header_length + (lnd->cpos - debug_line) + || header_length < 7) + goto error; + + lnd->minimum_instruction_length = lnd->cpos[0]; + /* Ignore default_is_stmt. */ + lnd->line_base = lnd->cpos[2]; + lnd->line_range = lnd->cpos[3]; + lnd->opcode_base = lnd->cpos[4]; + + if (lnd->opcode_base == 0) + /* Every valid line number program must use at least opcode 0 + for DW_LNE_end_sequence. */ + goto error; + + lnd->standard_opcode_lengths = lnd->cpos + 5; + if (header_length < (uint64_t)(5 + (lnd->opcode_base - 1))) + /* Header not long enough. */ + goto error; + lnd->cpos += 5 + lnd->opcode_base - 1; + lnd->end = debug_line + header_length + (dwarf_size_64 ? 22 : 10); + + /* Make table of offsets to directory names. */ + table_start = lnd->cpos; + lnd->numdir = 0; + while (lnd->cpos != lnd->end && *lnd->cpos) + { + lnd->cpos = memchr (lnd->cpos, 0, lnd->end - lnd->cpos); + if (! lnd->cpos) + goto error; + lnd->cpos++; + lnd->numdir++; + } + if (lnd->cpos == lnd->end) + goto error; + lnd->dirnames = malloc (lnd->numdir * sizeof (const uint8_t *)); + if (! lnd->dirnames) + goto error; + lnd->numdir = 0; + lnd->cpos = table_start; + while (*lnd->cpos) + { + lnd->dirnames[lnd->numdir++] = lnd->cpos; + lnd->cpos = memchr (lnd->cpos, 0, lnd->end - lnd->cpos) + 1; + } + lnd->cpos++; + + /* Make table of offsets to file entries. */ + table_start = lnd->cpos; + lnd->numfile = 0; + while (lnd->cpos != lnd->end && *lnd->cpos) + { + lnd->cpos = memchr (lnd->cpos, 0, lnd->end - lnd->cpos); + if (! lnd->cpos) + goto error; + lnd->cpos++; + skip_leb128 (lnd); + skip_leb128 (lnd); + skip_leb128 (lnd); + lnd->numfile++; + } + if (lnd->cpos == lnd->end) + goto error; + lnd->filenames = malloc (lnd->numfile * sizeof (const uint8_t *)); + if (! lnd->filenames) + goto error; + lnd->numfile = 0; + lnd->cpos = table_start; + while (*lnd->cpos) + { + lnd->filenames[lnd->numfile++] = lnd->cpos; + lnd->cpos = memchr (lnd->cpos, 0, lnd->end - lnd->cpos) + 1; + skip_leb128 (lnd); + skip_leb128 (lnd); + skip_leb128 (lnd); + } + lnd->cpos++; + + lnd->numfile_orig = lnd->numfile; + lnd->cpos = lnd->init = lnd->end; + lnd->end = debug_line + lnd_length + (dwarf_size_64 ? 12 : 4); + + init_state (&lnd->cur); + + return lnd; + + error: + line_free (lnd); + return NULL; +} + +/* Reset back to the beginning. */ +void +line_reset (struct line_reader_data * lnd) +{ + lnd->cpos = lnd->init; + lnd->numfile = lnd->numfile_orig; + init_state (&lnd->cur); +} + +/* Is there no more line data available? */ +int +line_at_eof (struct line_reader_data * lnd) +{ + return lnd->cpos == lnd->end; +} + +static bool +next_state (struct line_reader_data *lnd) +{ + if (lnd->cur.end_of_sequence) + init_state (&lnd->cur); + + for (;;) + { + uint8_t op; + uint64_t tmp; + + if (lnd->cpos == lnd->end) + return false; + op = *lnd->cpos++; + if (op >= lnd->opcode_base) + { + op -= lnd->opcode_base; + + lnd->cur.line += op % lnd->line_range + lnd->line_base; + lnd->cur.pc += (op / lnd->line_range + * lnd->minimum_instruction_length); + return true; + } + else switch (op) + { + case DW_LNS_extended_op: + { + uint64_t sz = read_uleb128 (lnd); + const uint8_t * op = lnd->cpos; + + if ((uint64_t)(lnd->end - op) < sz || sz == 0) + return false; + lnd->cpos += sz; + switch (*op++) + { + case DW_LNE_end_sequence: + lnd->cur.end_of_sequence = true; + return true; + + case DW_LNE_set_address: + if (sz == 9) + lnd->cur.pc = read_64 (op); + else if (sz == 5) + lnd->cur.pc = read_32 (op); + else + return false; + break; + + case DW_LNE_define_file: + { + const uint8_t * * filenames; + filenames = realloc + (lnd->filenames, + (lnd->numfile + 1) * sizeof (const uint8_t *)); + if (! filenames) + return false; + /* Check for zero-termination. */ + if (! memchr (op, 0, lnd->cpos - op)) + return false; + filenames[lnd->numfile++] = op; + lnd->filenames = filenames; + + /* There's other data here, like file sizes and modification + times, but we don't need to read it so skip it. */ + } + break; + + default: + /* Don't understand it, so skip it. */ + break; + } + break; + } + + case DW_LNS_copy: + //fprintf(stderr, "DW_LNS_copy\n"); + return true; + case DW_LNS_advance_pc: + //fprintf(stderr, "DW_LNS_advance_pc\n"); + tmp = read_uleb128 (lnd); + if (tmp == (uint64_t) -1) + return false; + lnd->cur.pc += tmp * lnd->minimum_instruction_length; + break; + case DW_LNS_advance_line: + //fprintf(stderr, "DW_LNS_advance_line\n"); + lnd->cur.line += read_sleb128 (lnd); + break; + case DW_LNS_set_file: + //fprintf(stderr, "DW_LNS_set_file\n"); + lnd->cur.file = read_uleb128 (lnd); + break; + case DW_LNS_set_column: + //fprintf(stderr, "DW_LNS_set_column\n"); + lnd->cur.col = read_uleb128 (lnd); + break; + case DW_LNS_const_add_pc: + //fprintf(stderr, "DW_LNS_const_add_pc\n"); + lnd->cur.pc += ((255 - lnd->opcode_base) / lnd->line_range + * lnd->minimum_instruction_length); + break; + case DW_LNS_fixed_advance_pc: + //fprintf(stderr, "DW_LNS_fixed_advance_pc\n"); + if (lnd->end - lnd->cpos < 2) + return false; + lnd->cur.pc += read_16 (lnd->cpos); + lnd->cpos += 2; + break; + default: + { + /* Don't know what it is, so skip it. */ + int i; + for (i = 0; i < lnd->standard_opcode_lengths[op - 1]; i++) + skip_leb128 (lnd); + break; + } + } + } +} + + +/* Set RESULT to the next 'interesting' line state, as indicated + by STOP, or return FALSE on error. The final (end-of-sequence) + line state is always considered interesting. */ +int +line_next (struct line_reader_data * lnd, + struct line_info * result, + enum line_stop_constants stop) +{ + for (;;) + { + struct line_info prev = lnd->cur; + + if (! next_state (lnd)) + return false; + + if (lnd->cur.end_of_sequence) + break; + if (stop == line_stop_always) + break; + if ((stop & line_stop_pc) && lnd->cur.pc != prev.pc) + break; + if ((stop & line_stop_pos_mask) && lnd->cur.file != prev.file) + break; + if ((stop & line_stop_pos_mask) >= line_stop_line + && lnd->cur.line != prev.line) + break; + if ((stop & line_stop_pos_mask) >= line_stop_col + && lnd->cur.col != prev.col) + break; + } + *result = lnd->cur; + return true; +} + +/* Find the region (START->pc through END->pc) in the debug_line + information which contains PC. This routine starts searching at + the current position (which is returned as END), and will go all + the way around the debug_line information. It will return false if + an error occurs or if there is no matching region; these may be + distinguished by looking at START->end_of_sequence, which will be + false on error and true if there was no matching region. + You could write this routine using line_next, but this version + will be slightly more efficient, and of course more convenient. */ + +int +line_find_addr (struct line_reader_data * lnd, + struct line_info * start, + struct line_info * end, + uint64_t pc) +{ + const uint8_t * startpos; + struct line_info prev; + + if (lnd->cur.end_of_sequence && lnd->cpos == lnd->end) + line_reset (lnd); + + startpos = lnd->cpos; + + do { + prev = lnd->cur; + if (! next_state (lnd)) + { + start->end_of_sequence = false; + return false; + } + if (lnd->cur.end_of_sequence && lnd->cpos == lnd->end) + line_reset (lnd); + if (lnd->cpos == startpos) + { + start->end_of_sequence = true; + return false; + } + } while (lnd->cur.pc <= pc || prev.pc > pc || prev.end_of_sequence); + *start = prev; + *end = lnd->cur; + return true; +} +#endif /* ! KLD */ + diff --git a/src/debugline.h b/src/debugline.h new file mode 100644 index 0000000..51d585e --- /dev/null +++ b/src/debugline.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Information about a line. + DIRECTORY is to be ignored if FILENAME is absolute. + PC will be relative to the file the debug_line section is in. */ +struct line_info +{ + uint64_t file; + int64_t line; + uint64_t col; + uint64_t pc; + int end_of_sequence; +}; + +/* Opaque status structure for the line readers. */ +struct line_reader_data; + +/* Create a line_reader_data, given address and size of the debug_line section. + SIZE may be (size_t)-1 if unknown, although this suppresses checking + for an incorrectly large size in the debug_line section. + LITTLE_ENDIAN is set if the debug_line section is for a little-endian + machine. + Returns NULL on error. */ +struct line_reader_data * line_open (const uint8_t * debug_line, + size_t debug_line_size, + int little_endian); + +/* The STOP parameter to line_next is one of line_stop_{file,line,col}, + perhaps ORed with line_stop_pc; or line_stop_atend, or line_stop_always. */ +enum line_stop_constants { + line_stop_atend = 0, /* Stop only at the end of a sequence. */ + line_stop_file = 1, /* Stop if DIRECTORY or FILENAME change. */ + line_stop_line = 2, /* Stop if LINE, DIRECTORY, or FILENAME change. */ + line_stop_col = 3, /* Stop if COL, LINE, DIRECTORY, or FILENAME change. */ + line_stop_pos_mask = 3, + line_stop_pc = 4, /* Stop if PC changes. */ + line_stop_always = 8 /* Stop always. */ +}; + +/* Either return FALSE on an error, in which case the line_reader_data + may be invalid and should be passed immediately to line_free; or + fill RESULT with the first 'interesting' line, as determined by STOP. + The last line data in a sequence is always considered 'interesting'. */ +int line_next (struct line_reader_data * lnd, + struct line_info * result, + enum line_stop_constants stop); + +/* Find the region (START->pc through END->pc) in the debug_line + information which contains PC. This routine starts searching at + the current position (which is returned as END), and will go all + the way around the debug_line information. It will return false if + an error occurs or if there is no matching region; these may be + distinguished by looking at START->end_of_sequence, which will be + false on error and true if there was no matching region. + You could write this routine using line_next, but this version + will be slightly more efficient, and of course more convenient. */ + +int line_find_addr (struct line_reader_data * lnd, + struct line_info * start, + struct line_info * end, + uint64_t pc); + +/* Return TRUE if there is more line data to be fetched. + If line_next has not been called or it has been called but did not + set END_OF_SEQUENCE, you can assume there is more line data, + but it's safe to call this routine anyway. */ +int line_at_eof (struct line_reader_data * lnd); + +/* Return the pathname of the file in S, or NULL on error. + The result will have been allocated with malloc. */ +char * line_file (struct line_reader_data *lnd, uint64_t file); + +/* Reset the line_reader_data: go back to the beginning. */ +void line_reset (struct line_reader_data * lnd); + +/* Free a line_reader_data structure. */ +void line_free (struct line_reader_data * lnd); + +#ifdef __cplusplus +} +#endif + diff --git a/src/dwarf2.h b/src/dwarf2.h new file mode 100644 index 0000000..530b465 --- /dev/null +++ b/src/dwarf2.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* These constants were taken from version 3 of the DWARF standard, + which is Copyright (c) 2005 Free Standards Group, and + Copyright (c) 1992, 1993 UNIX International, Inc. */ + +/* This is not a complete list. */ +enum { + DW_TAG_compile_unit = 17, + DW_TAG_partial_unit = 60 +}; + +/* This is not a complete list. */ +enum { + DW_AT_sibling = 1, + DW_AT_name = 3, + DW_AT_stmt_list = 16, + DW_AT_comp_dir = 27 +}; + +enum { + DW_FORM_addr = 1, + DW_FORM_block2 = 3, + DW_FORM_block4, + DW_FORM_data2, + DW_FORM_data4, + DW_FORM_data8, + DW_FORM_string, + DW_FORM_block, + DW_FORM_block1, + DW_FORM_data1, + DW_FORM_flag, + DW_FORM_sdata, + DW_FORM_strp, + DW_FORM_udata, + DW_FORM_ref_addr, + DW_FORM_ref1, + DW_FORM_ref2, + DW_FORM_ref4, + DW_FORM_ref8, + DW_FORM_ref_udata, + DW_FORM_indirect /* 22 */ +}; + +enum { + DW_LNS_extended_op = 0, + DW_LNS_copy, + DW_LNS_advance_pc, + DW_LNS_advance_line, + DW_LNS_set_file, + DW_LNS_set_column, + DW_LNS_negate_stmt, + DW_LNS_set_basic_block, + DW_LNS_const_add_pc, + DW_LNS_fixed_advance_pc, + DW_LNS_set_prologue_end, + DW_LNS_set_epilogue_begin, + DW_LNS_set_isa +}; + +enum { + DW_LNE_end_sequence = 1, + DW_LNE_set_address, + DW_LNE_define_file +}; diff --git a/src/ld.cpp b/src/ld.cpp index 3b68059..e98df10 100644 --- a/src/ld.cpp +++ b/src/ld.cpp @@ -1,16 +1,16 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ - * + * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. - * + * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -18,18 +18,24 @@ * 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@ */ #include #include #include #include +#include #include #include -#include +#include +#include +#include +#include +#include #include + #include #include #include @@ -41,10 +47,11 @@ #include "Options.h" #include "ObjectFile.h" -#include "ObjectFileMachO-all.h" -#include "ExecutableFile.h" -#include "ExecutableFileMachO-all.h" +#include "MachOReaderRelocatable.hpp" +#include "MachOReaderArchive.hpp" +#include "MachOReaderDylib.hpp" +#include "MachOWriterExecutable.hpp" #include "SectCreate.h" @@ -52,10 +59,10 @@ static void dumpAtom(ObjectFile::Atom* atom) { //printf("atom: %p\n", atom); - + // name printf("name: %s\n", atom->getDisplayName()); - + // scope switch ( atom->getScope() ) { case ObjectFile::Atom::scopeTranslationUnit: @@ -70,29 +77,42 @@ static void dumpAtom(ObjectFile::Atom* atom) default: printf("scope: unknown\n"); } - + + // kind + switch ( atom->getDefinitinonKind() ) { + case ObjectFile::Atom::kRegularDefinition: + printf("kind: regular\n"); + break; + case ObjectFile::Atom::kWeakDefinition: + printf("kind: weak\n"); + break; + case ObjectFile::Atom::kTentativeDefinition: + printf("kind: tentative\n"); + break; + case ObjectFile::Atom::kExternalDefinition: + printf("kind: import\n"); + break; + case ObjectFile::Atom::kExternalWeakDefinition: + printf("kind: weak import\n"); + break; + default: + printf("scope: unknown\n"); + } + // segment and section printf("section: %s,%s\n", atom->getSegment().getName(), atom->getSectionName()); // attributes printf("attrs: "); - if ( atom->isTentativekDefinition() ) - printf("tentative "); - else if ( atom->isWeakDefinition() ) - printf("weak "); - if ( atom->isCoalesableByName() ) - printf("coalesce-by-name "); - if ( atom->isCoalesableByValue() ) - printf("coalesce-by-value "); if ( atom->dontDeadStrip() ) printf("dont-dead-strip "); if ( atom->isZeroFill() ) printf("zero-fill "); printf("\n"); - + // size printf("size: 0x%012llX\n", atom->getSize()); - + // content uint8_t content[atom->getSize()]; atom->copyRawContent(content); @@ -105,7 +125,7 @@ static void dumpAtom(ObjectFile::Atom* atom) printf("%02X ", content[i]); } printf("\n"); - + // references std::vector& references = atom->getReferences(); const int refCount = references.size(); @@ -114,9 +134,9 @@ static void dumpAtom(ObjectFile::Atom* atom) ObjectFile::Reference* ref = references[i]; printf(" %s\n", ref->getDescription()); } - + // attributes - + } #endif @@ -169,13 +189,6 @@ Section::Section(const char* sectionName, const char* segmentName, bool zeroFill Section* Section::find(const char* sectionName, const char* segmentName, bool zeroFill) { -#if 0 - std::pair range = fgMapping.equal_range(sectionName); - for (NameToSection::iterator it=range.first; it != range.second; it++) { - if ( strcmp(it->second->fSegmentName, segmentName) == 0 ) - return it->second; - } -#endif NameToSection::iterator pos = fgMapping.find(sectionName); if ( pos != fgMapping.end() ) { if ( strcmp(pos->second->fSegmentName, segmentName) == 0 ) @@ -186,12 +199,18 @@ Section* Section::find(const char* sectionName, const char* segmentName, bool ze return *it; } } - + // does not exist, so make a new one Section* sect = new Section(sectionName, segmentName, zeroFill); sect->fIndex = fgMapping.size(); fgMapping[sectionName] = sect; fgSections.push_back(sect); + + if ( (strcmp(sectionName, "__text") == 0) && (strcmp(segmentName, "__TEXT") == 0) ) { + // special case __textcoal_nt to be right after __text + find("__textcoal_nt", "__TEXT", false); + } + return sect; } @@ -207,7 +226,7 @@ int Section::Sorter::segmentOrdinal(const char* segName) return 4; if ( strcmp(segName, "__LINKEDIT") == 0 ) return INT_MAX; // linkedit segment should always sort last - else + else return 5; } @@ -248,10 +267,10 @@ void Section::assignIndexes() // sort it std::sort(fgSections.begin(), fgSections.end(), Section::Sorter()); - + // assign correct section ordering to each Section object unsigned int newOrder = 1; - for (std::vector::iterator it=fgSections.begin(); it != fgSections.end(); it++) + for (std::vector::iterator it=fgSections.begin(); it != fgSections.end(); it++) (*it)->fIndex = newOrder++; //printf("sorted:\n"); @@ -269,8 +288,8 @@ public: void addInputFile(ObjectFile::Reader* reader); void setOutputFile(ExecutableFile::Writer* writer); void link(); - - + + private: ObjectFile::Reader* createReader(const Options::FileInfo&); void addAtom(ObjectFile::Atom& atom); @@ -282,17 +301,35 @@ private: void deadStrip(); void sortAtoms(); void tweakLayout(); + void writeDotOutput(); + static bool minimizeStab(ObjectFile::Reader::Stab& stab); + static const char* truncateStabString(const char* str); + void collectStabs(); void writeOutput(); - + ObjectFile::Atom* entryPoint(); + ObjectFile::Atom* dyldHelper(); + const char* assureFullPath(const char* path); + void markLive(ObjectFile::Atom* atom, std::set& liveAtoms); + void collectStabs(ObjectFile::Reader* reader); + void synthesizeStabs(ObjectFile::Reader* reader); + void printStatistics(); + void printTime(const char* msg, uint64_t partTime, uint64_t totalTime); + char* commatize(uint64_t in, char* out); + void getVMInfo(vm_statistics_data_t& info); + cpu_type_t inferArchitecture(); + void resolve(ObjectFile::Reference* reference); void resolveFrom(ObjectFile::Reference* reference); void addJustInTimeAtoms(const char* name); - - void addDylib(ObjectFile::Reader* reader, const Options::FileInfo& info); + + ObjectFile::Reader* addDylib(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen); + ObjectFile::Reader* addObject(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen); + ObjectFile::Reader* addArchive(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen); void addIndirectLibraries(ObjectFile::Reader* reader); bool haveIndirectLibrary(const char* path, ObjectFile::Reader* reader); bool haveDirectLibrary(const char* path); - + + void logTraceInfo(const char* format, ...); class SymbolTable { public: @@ -303,27 +340,29 @@ private: unsigned int getRequireCount() { return fRequireCount; } void getNeededNames(bool andWeakDefintions, std::vector& undefines); private: - typedef std::map Mapper; + typedef __gnu_cxx::hash_map, CStringEquals> Mapper; Linker& fOwner; Mapper fTable; unsigned int fRequireCount; }; - + struct AtomSorter { bool operator()(ObjectFile::Atom* left, ObjectFile::Atom* right); }; - + typedef std::map SectionOrder; - + struct IndirectLibrary { const char* path; uint64_t fileLen; ObjectFile::Reader* reader; std::set parents; - ObjectFile::Reader* reExportParent; + ObjectFile::Reader* reExportedViaDirectLibrary; }; - + + ObjectFile::Reader* findDirectLibraryWhichReExports(struct IndirectLibrary& indirectLib); + Options fOptions; SymbolTable fGlobalSymbolTable; unsigned int fWeakSymbolsAddedCount; @@ -331,22 +370,96 @@ private: ExecutableFile::Writer* fOutputFile; std::vector fDynamicLibraries; std::list fIndirectDynamicLibraries; + std::vector fReadersThatHaveSuppliedAtoms; std::vector fAllAtoms; std::set fDeadAtoms; + std::vector fStabs; + bool fCreateUUID; SectionOrder fSectionOrder; unsigned int fNextSortOrder; + unsigned int fNextObjectFileOrder; + cpu_type_t fArchitecture; bool fDirectLibrariesComplete; + uint64_t fOutputFileSize; + uint64_t fStartTime; + uint64_t fStartCreateReadersTime; + uint64_t fStartCreateWriterTime; + uint64_t fStartBuildAtomsTime; + uint64_t fStartLoadUndefinesTime; + uint64_t fStartResolveTime; + uint64_t fStartSortTime; + uint64_t fStartWriteTime; + uint64_t fEndTime; + uint64_t fTotalObjectSize; + uint64_t fTotalArchiveSize; + uint32_t fTotalObjectLoaded; + uint32_t fTotalArchivesLoaded; + uint32_t fTotalDylibsLoaded; + vm_statistics_data_t fStartVMInfo; }; - Linker::Linker(int argc, const char* argv[]) - : fOptions(argc, argv), fGlobalSymbolTable(*this), fOutputFile(NULL), fNextSortOrder(1), fDirectLibrariesComplete(false) + : fOptions(argc, argv), fGlobalSymbolTable(*this), fOutputFile(NULL), fCreateUUID(false), fNextSortOrder(1), + fNextObjectFileOrder(1), fArchitecture(0), fDirectLibrariesComplete(false), fOutputFileSize(0), fTotalObjectSize(0), + fTotalArchiveSize(0), fTotalObjectLoaded(0), fTotalArchivesLoaded(0), fTotalDylibsLoaded(0) { + fStartTime = mach_absolute_time(); + if ( fOptions.printStatistics() ) + getVMInfo(fStartVMInfo); + + fArchitecture = fOptions.architecture(); + if ( fArchitecture == 0 ) { + // -arch not specified, scan .o files to figure out what it should be + fArchitecture = inferArchitecture(); + } } +cpu_type_t Linker::inferArchitecture() +{ + // scan all input files, looking for a thin .o file. + // the first one found is presumably the architecture to link + uint8_t buffer[sizeof(mach_header_64)]; + std::vector& files = fOptions.getInputFiles(); + for (std::vector::iterator it = files.begin(); it != files.end(); ++it) { + int fd = ::open(it->path, O_RDONLY, 0); + if ( fd != -1 ) { + ssize_t amount = read(fd, buffer, sizeof(buffer)); + ::close(fd); + if ( amount >= (ssize_t)sizeof(buffer) ) { + if ( mach_o::relocatable::Reader::validFile(buffer) ) { + fprintf(stderr, "ld64 warning: -arch not used, infering -arch ppc based on %s\n", it->path); + return CPU_TYPE_POWERPC; + } + else if ( mach_o::relocatable::Reader::validFile(buffer) ) { + fprintf(stderr, "ld64 warning: -arch not used, infering -arch ppc64 based on %s\n", it->path); + return CPU_TYPE_POWERPC64; + } + else if ( mach_o::relocatable::Reader::validFile(buffer) ) { + fprintf(stderr, "ld64 warning: -arch not used, infering -arch i386 based on %s\n", it->path); + return CPU_TYPE_I386; + } + } + } + } + + // no thin .o files found, so default to same architecture this was built as + fprintf(stderr, "ld64 warning: -arch not specified\n"); +#if __ppc__ + return CPU_TYPE_POWERPC; +#elif __i386__ + return CPU_TYPE_I386; +#elif __ppc64__ + return CPU_TYPE_POWERPC64; +#else + #error unknown default architecture +#endif +} + + void Linker::addInputFile(ObjectFile::Reader* reader) { + reader->setSortOrder(fNextObjectFileOrder++); fInputFiles.push_back(reader); } @@ -356,21 +469,106 @@ void Linker::setOutputFile(ExecutableFile::Writer* writer) } void Linker::link() -{ +{ this->buildAtomList(); this->loadUndefines(); this->resolveReferences(); this->deadStrip(); this->sortAtoms(); this->tweakLayout(); + this->writeDotOutput(); + this->collectStabs(); this->writeOutput(); + this->printStatistics(); + + if ( fOptions.pauseAtEnd() ) + sleep(10); +} + +void Linker::printTime(const char* msg, uint64_t partTime, uint64_t totalTime) +{ + static uint64_t sUnitsPerSecond = 0; + if ( sUnitsPerSecond == 0 ) { + struct mach_timebase_info timeBaseInfo; + if ( mach_timebase_info(&timeBaseInfo) == KERN_SUCCESS ) { + sUnitsPerSecond = 1000000000LL * timeBaseInfo.denom / timeBaseInfo.numer; + //fprintf(stderr, "sUnitsPerSecond=%llu\n", sUnitsPerSecond); + } + } + if ( partTime < sUnitsPerSecond ) { + uint32_t milliSecondsTimeTen = (partTime*10000)/sUnitsPerSecond; + uint32_t milliSeconds = milliSecondsTimeTen/10; + uint32_t percentTimesTen = (partTime*1000)/totalTime; + uint32_t percent = percentTimesTen/10; + fprintf(stderr, "%s: %u.%u milliseconds (%u.%u%%)\n", msg, milliSeconds, milliSecondsTimeTen-milliSeconds*10, percent, percentTimesTen-percent*10); + } + else { + uint32_t secondsTimeTen = (partTime*10)/sUnitsPerSecond; + uint32_t seconds = secondsTimeTen/10; + uint32_t percentTimesTen = (partTime*1000)/totalTime; + uint32_t percent = percentTimesTen/10; + fprintf(stderr, "%s: %u.%u seconds (%u.%u%%)\n", msg, seconds, secondsTimeTen-seconds*10, percent, percentTimesTen-percent*10); + } +} + +char* Linker::commatize(uint64_t in, char* out) +{ + char* result = out; + char rawNum[30]; + sprintf(rawNum, "%llu", in); + const int rawNumLen = strlen(rawNum); + for(int i=0; i < rawNumLen-1; ++i) { + *out++ = rawNum[i]; + if ( ((rawNumLen-i) % 3) == 1 ) + *out++ = ','; + } + *out++ = rawNum[rawNumLen-1]; + *out = '\0'; + return result; +} + +void Linker::getVMInfo(vm_statistics_data_t& info) +{ + mach_msg_type_number_t count = sizeof(vm_statistics_data_t) / sizeof(natural_t); + kern_return_t error = host_statistics(mach_host_self(), HOST_VM_INFO, + (host_info_t)&info, &count); + if (error != KERN_SUCCESS) { + bzero(&info, sizeof(vm_statistics_data_t)); + } +} + +void Linker::printStatistics() +{ + fEndTime = mach_absolute_time(); + if ( fOptions.printStatistics() ) { + vm_statistics_data_t endVMInfo; + getVMInfo(endVMInfo); + + uint64_t totalTime = fEndTime - fStartTime; + printTime("ld64 total time", totalTime, totalTime); + printTime(" option parsing time", fStartCreateReadersTime - fStartTime, totalTime); + printTime(" object file processing",fStartCreateWriterTime - fStartCreateReadersTime, totalTime); + printTime(" output file setup", fStartBuildAtomsTime - fStartCreateWriterTime, totalTime); + printTime(" build atom list", fStartLoadUndefinesTime - fStartBuildAtomsTime, totalTime); + printTime(" load undefines", fStartResolveTime - fStartLoadUndefinesTime, totalTime); + printTime(" resolve references", fStartSortTime - fStartResolveTime, totalTime); + printTime(" sort output", fStartWriteTime - fStartSortTime, totalTime); + printTime(" write output", fEndTime - fStartWriteTime, totalTime); + fprintf(stderr, "pageins=%u, pageouts=%u, faults=%u\n", endVMInfo.pageins-fStartVMInfo.pageins, + endVMInfo.pageouts-fStartVMInfo.pageouts, endVMInfo.faults-fStartVMInfo.faults); + char temp[40]; + fprintf(stderr, "processed %3u object files, totaling %15s bytes\n", fTotalObjectLoaded, commatize(fTotalObjectSize, temp)); + fprintf(stderr, "processed %3u archive files, totaling %15s bytes\n", fTotalArchivesLoaded, commatize(fTotalArchiveSize, temp)); + fprintf(stderr, "processed %3u dylib files\n", fTotalDylibsLoaded); + fprintf(stderr, "wrote output file totaling %15s bytes\n", commatize(fOutputFileSize, temp)); + } } inline void Linker::addAtom(ObjectFile::Atom& atom) { // add to list of all atoms fAllAtoms.push_back(&atom); - + // add atom's references's names to symbol table as to-be-resolved-later std::vector& references = atom.getReferences(); for (std::vector::iterator it=references.begin(); it != references.end(); it++) { @@ -381,13 +579,13 @@ inline void Linker::addAtom(ObjectFile::Atom& atom) if ( reference->hasFromTarget() && reference->isFromTargetUnbound() ) fGlobalSymbolTable.require(reference->getFromTargetName()); } - + // if in global namespace, add atom itself to symbol table ObjectFile::Atom::Scope scope = atom.getScope(); const char* name = atom.getName(); if ( (scope != ObjectFile::Atom::scopeTranslationUnit) && (name != NULL) ) { fGlobalSymbolTable.add(atom); - + // update scope based on export list (possible that globals are downgraded to private_extern) if ( (scope == ObjectFile::Atom::scopeGlobal) && fOptions.hasExportRestrictList() ) { bool doExport = fOptions.shouldExport(name); @@ -396,10 +594,10 @@ inline void Linker::addAtom(ObjectFile::Atom& atom) } } } - + // record section orders so output file can have same order atom.setSection(Section::find(atom.getSectionName(), atom.getSegment().getName(), atom.isZeroFill())); - + // assign order in which this atom was originally seen if ( atom.getSortOrder() == 0 ) fNextSortOrder = atom.setSortOrder(fNextSortOrder); @@ -407,28 +605,39 @@ inline void Linker::addAtom(ObjectFile::Atom& atom) inline void Linker::addAtoms(std::vector& atoms) { + bool first = true; // assume all atoms are from same reader for (std::vector::iterator it=atoms.begin(); it != atoms.end(); it++) { + if ( first ) { + // update fReadersThatHaveSuppliedAtoms + ObjectFile::Reader* reader = (*it)->getFile(); + if ( std::find(fReadersThatHaveSuppliedAtoms.begin(), fReadersThatHaveSuppliedAtoms.end(), reader) + == fReadersThatHaveSuppliedAtoms.end() ) { + fReadersThatHaveSuppliedAtoms.push_back(reader); + } + } this->addAtom(**it); + first = false; } } void Linker::buildAtomList() { + fStartBuildAtomsTime = mach_absolute_time(); // add initial undefines from -u option std::vector& initialUndefines = fOptions.initialUndefines(); for (std::vector::iterator it=initialUndefines.begin(); it != initialUndefines.end(); it++) { fGlobalSymbolTable.require(*it); } - - // writer can contribute atoms + + // writer can contribute atoms this->addAtoms(fOutputFile->getAtoms()); - + // each reader contributes atoms const int readerCount = fInputFiles.size(); for (int i=0; i < readerCount; ++i) { this->addAtoms(fInputFiles[i]->getAtoms()); } - + // extra command line section always at end std::vector& extraSections = fOptions.extraSections(); for( std::vector::iterator it=extraSections.begin(); it != extraSections.end(); ++it) { @@ -436,23 +645,33 @@ void Linker::buildAtomList() } } +static const char* pathLeafName(const char* path) +{ + const char* shortPath = strrchr(path, '/'); + if ( shortPath == NULL ) + return path; + else + return &shortPath[1]; +} + void Linker::loadUndefines() { + fStartLoadUndefinesTime = mach_absolute_time(); // keep looping until no more undefines were added in last loop unsigned int undefineCount = 0xFFFFFFFF; while ( undefineCount != fGlobalSymbolTable.getRequireCount() ) { undefineCount = fGlobalSymbolTable.getRequireCount(); std::vector undefineNames; - fGlobalSymbolTable.getNeededNames(true, undefineNames); - const int undefineCount = undefineNames.size(); - for (int i=0; i < undefineCount; ++i) { - const char* name = undefineNames[i]; + fGlobalSymbolTable.getNeededNames(false, undefineNames); + for(std::vector::iterator it = undefineNames.begin(); it != undefineNames.end(); ++it) { + const char* name = *it; ObjectFile::Atom* possibleAtom = fGlobalSymbolTable.find(name); - if ( (possibleAtom == NULL) || (possibleAtom->isWeakDefinition() && (fOptions.outputKind() != Options::kObjectFile)) ) + if ( (possibleAtom == NULL) + || ((possibleAtom->getDefinitionKind()==ObjectFile::Atom::kWeakDefinition) && (fOptions.outputKind() != Options::kObjectFile) && (possibleAtom->getScope() == ObjectFile::Atom::scopeGlobal)) ) this->addJustInTimeAtoms(name); } } - + if ( fOptions.outputKind() != Options::kObjectFile ) { // error out on any remaining undefines bool doPrint = true; @@ -480,16 +699,7 @@ void Linker::loadUndefines() fprintf(stderr, "can't resolve symbols:\n"); for (int i=0; i < unresolvableCount; ++i) { const char* name = unresolvableUndefines[i]; - const unsigned int nameLen = strlen(name); fprintf(stderr, " %s, referenced from:\n", name); - char stubName[nameLen+6]; - strcpy(stubName, name); - strcat(stubName, "$stub"); - char nonLazyName[nameLen+16]; - strcpy(nonLazyName, name); - strcat(nonLazyName, "$non_lazy_ptr"); - ObjectFile::Atom* lastStubAtomWithUnresolved = NULL; - ObjectFile::Atom* lastNonLazyAtomWithUnresolved = NULL; // scan all atoms for references bool foundAtomReference = false; for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { @@ -498,50 +708,14 @@ void Linker::loadUndefines() for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { ObjectFile::Reference* reference = *rit; if ( reference->isTargetUnbound() ) { - if ( (atom != lastStubAtomWithUnresolved) && (strcmp(reference->getTargetName(), stubName) == 0) ) { - const char* path = atom->getFile()->getPath(); - const char* shortPath = strrchr(path, '/'); - if ( shortPath == NULL ) - shortPath = path; - else - shortPath = &shortPath[1]; - fprintf(stderr, " %s in %s\n", atom->getDisplayName(), shortPath); - lastStubAtomWithUnresolved = atom; - foundAtomReference = true; - } - else if ( (atom != lastNonLazyAtomWithUnresolved) && (strcmp(reference->getTargetName(), nonLazyName) == 0) ) { - const char* path = atom->getFile()->getPath(); - const char* shortPath = strrchr(path, '/'); - if ( shortPath == NULL ) - shortPath = path; - else - shortPath = &shortPath[1]; - fprintf(stderr, " %s in %s\n", atom->getDisplayName(), shortPath); - lastNonLazyAtomWithUnresolved = atom; + if ( strcmp(reference->getTargetName(), name) == 0 ) { + fprintf(stderr, " %s in %s\n", atom->getDisplayName(), pathLeafName(atom->getFile()->getPath())); foundAtomReference = true; } } if ( reference->hasFromTarget() && reference->isFromTargetUnbound() ) { - if ( (atom != lastStubAtomWithUnresolved) && (strcmp(reference->getFromTargetName(), stubName) == 0) ) { - const char* path = atom->getFile()->getPath(); - const char* shortPath = strrchr(path, '/'); - if ( shortPath == NULL ) - shortPath = path; - else - shortPath = &shortPath[1]; - fprintf(stderr, " %s in %s\n", atom->getDisplayName(), shortPath); - lastStubAtomWithUnresolved = atom; - foundAtomReference = true; - } - else if ( (atom != lastNonLazyAtomWithUnresolved) && (strcmp(reference->getFromTargetName(), nonLazyName) == 0) ) { - const char* path = atom->getFile()->getPath(); - const char* shortPath = strrchr(path, '/'); - if ( shortPath == NULL ) - shortPath = path; - else - shortPath = &shortPath[1]; - fprintf(stderr, " %s in %s\n", atom->getDisplayName(), shortPath); - lastNonLazyAtomWithUnresolved = atom; + if ( strcmp(reference->getFromTargetName(), name) == 0 ) { + fprintf(stderr, " %s in %s\n", atom->getDisplayName(), pathLeafName(atom->getFile()->getPath())); foundAtomReference = true; } } @@ -557,7 +731,7 @@ void Linker::loadUndefines() if ( doError && (unresolvableCount > unresolvableExportsCount) ) // last check should be removed. It exists so broken projects still build throw "symbol(s) not found"; } - + // now verify that -init routine exists if ( fOptions.initFunctionName() != NULL ) { if ( fGlobalSymbolTable.find(fOptions.initFunctionName()) == NULL ) @@ -570,29 +744,33 @@ void Linker::loadUndefines() void Linker::addJustInTimeAtoms(const char* name) { - // when creating final linked image, write gets first chance + // when creating final linked image, writer gets first chance if ( fOptions.outputKind() != Options::kObjectFile ) { - ObjectFile::Atom* atom = fOutputFile->getUndefinedProxyAtom(name); - if ( atom != NULL ) { - this->addAtom(*atom);\ - return; - } - } - - // give direct readers a chance - const int readerCount = fInputFiles.size(); - for (int i=0; i < readerCount; ++i) { - // if this reader is a static archive that has the symbol we need, pull in all atoms in that module - // if this reader is a dylib that exports the symbol we need, have it synthesize an atom for us. - std::vector* atoms = fInputFiles[i]->getJustInTimeAtomsFor(name); + std::vector* atoms = fOutputFile->getJustInTimeAtomsFor(name); if ( atoms != NULL ) { this->addAtoms(*atoms); delete atoms; + //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, fOutputFile->getPath() ); return; // found a definition, no need to search anymore - //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file #%d\n", name, i); } } - + + // give direct readers a chance + for (std::vector::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) { + ObjectFile::Reader* reader = *it; + if ( reader != NULL ) { + // if this reader is a static archive that has the symbol we need, pull in all atoms in that module + // if this reader is a dylib that exports the symbol we need, have it synthesize an atom for us. + std::vector* atoms = reader->getJustInTimeAtomsFor(name); + if ( atoms != NULL ) { + this->addAtoms(*atoms); + delete atoms; + //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, fInputFiles[i]->getPath() ); + return; // found a definition, no need to search anymore + } + } + } + // give indirect readers a chance for (std::list::iterator it=fIndirectDynamicLibraries.begin(); it != fIndirectDynamicLibraries.end(); it++) { ObjectFile::Reader* reader = it->reader; @@ -600,107 +778,37 @@ void Linker::addJustInTimeAtoms(const char* name) std::vector* atoms = reader->getJustInTimeAtomsFor(name); if ( atoms != NULL ) { this->addAtoms(*atoms); + //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() ); delete atoms; - break; - //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file #%d\n", name, i); + return; // found a definition, no need to search anymore } } } - + // when creating .o file, writer goes last (this is so any static archives will be searched above) - if ( fOptions.outputKind() == Options::kObjectFile ) { + if ( (fOptions.outputKind() == Options::kObjectFile) || (fOptions.undefinedTreatment() != Options::kUndefinedError) ) { ObjectFile::Atom* atom = fOutputFile->getUndefinedProxyAtom(name); if ( atom != NULL ) { this->addAtom(*atom); return; } } - + //fprintf(stderr, "addJustInTimeAtoms(%s) => not found\n", name); } void Linker::resolve(ObjectFile::Reference* reference) { - ObjectFile::Atom* target = NULL; - const char* targetName = reference->getTargetName(); - const int targetNameLen = strlen(targetName); - if ( (targetNameLen > 5) && (strcmp(&targetName[targetNameLen-5], "$stub") == 0) ) { - // when looking up "_foo$stub", first look for "_foo" - char nonStubTarget[targetNameLen+1]; - strcpy(nonStubTarget, targetName); - nonStubTarget[targetNameLen-5] = '\0'; - // unless interposing and the symbol is exported - if ( !fOptions.interposable() || !fOptions.shouldExport(nonStubTarget) ) { - target = fGlobalSymbolTable.find(nonStubTarget); - // also need indirection to all exported weak symbols for C++ support - if ( (target != NULL) && !target->isImportProxy() && (!target->isWeakDefinition() || (target->getScope() != ObjectFile::Atom::scopeGlobal)) ) { - reference->setTarget(*target, reference->getTargetOffset()); - // mark stub as no longer being needed - ObjectFile::Atom* stub = fGlobalSymbolTable.find(targetName); - if ( stub != NULL ) { - char lazySymbol[targetNameLen+8]; - strcpy(lazySymbol, nonStubTarget); - strcat(lazySymbol, "$lazy_ptr"); - ObjectFile::Atom* lazyPtr = fGlobalSymbolTable.find(lazySymbol); - fDeadAtoms.insert(stub); - if ( lazyPtr != NULL ) - fDeadAtoms.insert(lazyPtr); - } - return; - } - } - } - // look in global symbol table - target = fGlobalSymbolTable.find(targetName); + const char* targetName = reference->getTargetName(); + ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); if ( target == NULL ) { fprintf(stderr, "can't resolve: %s\n", targetName); } reference->setTarget(*target, reference->getTargetOffset()); - - // handle weak-imports - if ( target->isImportProxy() ) { - bool mismatch = false; - if ( reference->isWeakReference() ) { - switch(target->getImportWeakness()) { - case ObjectFile::Atom::kWeakUnset: - target->setImportWeakness(true); - break; - case ObjectFile::Atom::kWeakImport: - break; - case ObjectFile::Atom::kNonWeakImport: - mismatch = true; - break; - } - } - else { - switch(target->getImportWeakness()) { - case ObjectFile::Atom::kWeakUnset: - target->setImportWeakness(false); - break; - case ObjectFile::Atom::kWeakImport: - mismatch = true; - break; - case ObjectFile::Atom::kNonWeakImport: - break; - } - } - if ( mismatch ) { - switch ( fOptions.weakReferenceMismatchTreatment() ) { - case Options::kWeakReferenceMismatchError: - throwf("mismatching weak references for symbol: %s", target->getName()); - case Options::kWeakReferenceMismatchWeak: - target->setImportWeakness(true); - break; - case Options::kWeakReferenceMismatchNonWeak: - target->setImportWeakness(false); - break; - } - } - } } void Linker::resolveFrom(ObjectFile::Reference* reference) -{ +{ // handle references that have two (from and to) targets const char* fromTargetName = reference->getFromTargetName(); ObjectFile::Atom* fromTarget = fGlobalSymbolTable.find(fromTargetName); @@ -713,15 +821,16 @@ void Linker::resolveFrom(ObjectFile::Reference* reference) void Linker::resolveReferences() { + fStartResolveTime = mach_absolute_time(); // note: the atom list may grow during this loop as libraries supply needed atoms for (unsigned int j=0; j < fAllAtoms.size(); ++j) { ObjectFile::Atom* atom = fAllAtoms[j]; std::vector& references = atom->getReferences(); for (std::vector::iterator it=references.begin(); it != references.end(); it++) { ObjectFile::Reference* reference = *it; - if ( reference->isTargetUnbound() ) + if ( reference->isTargetUnbound() ) this->resolve(reference); - if ( reference->hasFromTarget() && reference->isFromTargetUnbound() ) + if ( reference->hasFromTarget() && reference->isFromTargetUnbound() ) this->resolveFrom(reference); } } @@ -731,31 +840,112 @@ class InSet { public: InSet(std::set& deadAtoms) : fDeadAtoms(deadAtoms) {} - - bool operator()(ObjectFile::Atom*& atom) const { + + bool operator()(ObjectFile::Atom*& atom) const { return ( fDeadAtoms.count(atom) != 0 ); } - + private: std::set& fDeadAtoms; }; +// used to remove stabs associated with atoms that won't be in output file +class NotInSet +{ +public: + NotInSet(std::set& theSet) : fSet(theSet) {} + + bool operator()(const ObjectFile::Reader::Stab& stab) const { + if ( stab.atom == NULL ) + return false; // leave stabs that are not associated with any atome + else + return ( fSet.count(stab.atom) == 0 ); + } + +private: + std::set& fSet; +}; + + +class DeadStrippable +{ +public: + DeadStrippable() {} + + bool operator()(ObjectFile::Atom*& atom) const { + //if ( ! atom->dontDeadStrip() ) + // fprintf(stderr, "dead strip %s\n", atom->getDisplayName()); + return ( ! atom->dontDeadStrip() ); + } +}; + +void Linker::markLive(ObjectFile::Atom* atom, std::set& liveAtoms) +{ + if ( liveAtoms.count(atom) == 0 ) { + // this atom is live + liveAtoms.insert(atom); + // so don't dead strip + atom->setDontDeadStrip(); + + // and all atoms it references + std::vector& references = atom->getReferences(); + for (std::vector::iterator it=references.begin(); it != references.end(); it++) { + ObjectFile::Reference* reference = *it; + // mark target of reference as live + this->markLive(&reference->getTarget(), liveAtoms); + // mark from-target (if it exists) as live + if ( reference->hasFromTarget() ) + this->markLive(&reference->getFromTarget(), liveAtoms); + } + } +} void Linker::deadStrip() { - //printf("Stripping atoms:\n"); - //for (std::set::iterator it=fDeadAtoms.begin(); it != fDeadAtoms.end(); it++) { - // printf("\t%s\n", (*it)->getDisplayName()); - //} + if ( fOptions.deadStrip() != Options::kDeadStripOff ) { + std::set liveAtoms; + + // mark main() and all atoms reachable from it as live + ObjectFile::Atom* entryPoint = this->entryPoint(); + if ( entryPoint != NULL ) + markLive(entryPoint, liveAtoms); + + ObjectFile::Atom* dyldHelper = this->dyldHelper(); + if ( dyldHelper != NULL ) + markLive(dyldHelper, liveAtoms); + + // mark initializers and all atoms reachable from them as live + // mark all exported atoms and atoms reachable from them as live + Section* initSection = Section::find("__mod_init_func", "__DATA", false); + Section* termSection = Section::find("__mod_term_func", "__DATA", false); + const bool globalsAreRoots = ( fOptions.outputKind() != Options::kDynamicExecutable ); + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + ObjectFile::Atom* atom = *it; + //fprintf(stderr,"%d %s\n", atom->dontDeadStrip(), atom->getDisplayName()); + if ( (globalsAreRoots && (atom->getScope() == ObjectFile::Atom::scopeGlobal)) + || (atom->getSection() == initSection) || (atom->getSection() == termSection) ) + markLive(atom, liveAtoms); + } + + // now remove dead strippable atoms from fAllAtoms + fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), DeadStrippable()), fAllAtoms.end()); + } + else { + //printf("Stripping atoms:\n"); + //for (std::set::iterator it=fDeadAtoms.begin(); it != fDeadAtoms.end(); it++) { + // printf("\t%s\n", (*it)->getDisplayName()); + //} - // for now, just remove atoms weak atoms that have been overridden - fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), InSet(fDeadAtoms)), fAllAtoms.end()); + // if not dead stripping, just remove atoms weak atoms that have been overridden + fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), InSet(fDeadAtoms)), fAllAtoms.end()); + } } void Linker::sortAtoms() { + fStartSortTime = mach_absolute_time(); Section::assignIndexes(); std::sort(fAllAtoms.begin(), fAllAtoms.end(), Linker::AtomSorter()); //fprintf(stderr, "Sorted atoms:\n"); @@ -771,7 +961,130 @@ void Linker::tweakLayout() { } -void Linker::writeOutput() + +void Linker::writeDotOutput() +{ + const char* dotOutFilePath = fOptions.dotOutputFile(); + if ( dotOutFilePath != NULL ) { + FILE* out = fopen(dotOutFilePath, "w"); + if ( out != NULL ) { + // print header + fprintf(out, "digraph dg\n{\n"); + fprintf(out, "\tconcentrate = true;\n"); + fprintf(out, "\trankdir = LR;\n"); + + // print each atom as a node + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + ObjectFile::Atom* atom = *it; + if ( atom->getFile() != fOutputFile ) { + const char* name = atom->getDisplayName(); + // don't create nodes for stubs, lazy pointers or non-lazy pointers + const char* lastDollar = strrchr(name, '$'); + if ( lastDollar != NULL ) { + if ( (strcmp(lastDollar, "$stub") == 0) || (strcmp(lastDollar, "$lazy_ptr") == 0) || (strcmp(lastDollar, "$non_lazy_ptr") == 0) ) + continue; + } + if ( (atom->getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) + || (atom->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { + char temp[strlen(name)+2]; + strcpy(temp, name); + temp[strlen(name)-7] = '\0'; // strip trailing "$import" + fprintf(out, "\t%s [ shape = plaintext ];\n", temp); + } + else if ( strcmp(atom->getSectionName(), "__cstring") == 0 ) { + char cstring[atom->getSize()+2]; + atom->copyRawContent((uint8_t*)cstring); + fprintf(out, "\taddr%p [ label = \"string: '", atom); + for (const char* s=cstring; *s != '\0'; ++s) { + if ( *s == '\n' ) + fprintf(out, "\\\\n"); + else + fputc(*s, out); + } + fprintf(out, "'\" ];\n"); + } + else { + fprintf(out, "\taddr%p [ label = \"%s\" ];\n", atom, name); + } + } + } + fprintf(out, "\n"); + + // print each reference as an edge + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + ObjectFile::Atom* fromAtom = *it; + if ( fromAtom->getFile() != fOutputFile ) { + std::vector& references = fromAtom->getReferences(); + std::set seenTargets; + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* reference = *rit; + ObjectFile::Atom* toAtom = &(reference->getTarget()); + if ( seenTargets.count(toAtom) == 0 ) { + seenTargets.insert(toAtom); + const char* toName = toAtom->getDisplayName(); + const char* lastDollar = strrchr(toName, '$'); + if ( (lastDollar != NULL) && (strcmp(lastDollar, "$stub") == 0) ) { + char temp[strlen(toName)+2]; + strcpy(temp, toName); + temp[strlen(toName)-5] = '\0'; // strip trailing "$stub" + fprintf(out, "\taddr%p -> %s;\n", fromAtom, temp); + } + else if ( lastDollar != NULL && (strcmp(lastDollar, "$non_lazy_ptr") == 0) ) { + ObjectFile::Atom* nonLazyTargetAtom = &(toAtom->getReferences()[0]->getTarget()); + if ( (nonLazyTargetAtom->getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) + || (nonLazyTargetAtom->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { + char temp[strlen(toName)+2]; + strcpy(temp, toName); + temp[strlen(toName)-13] = '\0'; // strip trailing "$non_lazy_ptr" + fprintf(out, "\taddr%p -> %s;\n", fromAtom, temp); + } + else { + fprintf(out, "\taddr%p -> addr%p;\n", fromAtom, nonLazyTargetAtom); + } + } + else { + // don't list references from stubs, lazy pointers or non-lazy pointers + const char* name = fromAtom->getDisplayName(); + const char* lastDollar = strrchr(name, '$'); + if ( lastDollar != NULL ) { + if ( (strcmp(lastDollar, "$stub") == 0) || (strcmp(lastDollar, "$lazy_ptr") == 0) || (strcmp(lastDollar, "$non_lazy_ptr") == 0) ) + continue; + } + fprintf(out, "\taddr%p -> addr%p;\n", fromAtom, toAtom); + } + } + } + } + } + fprintf(out, "\n"); + + // push all imports to bottom of graph + fprintf(out, "{ rank = same; "); + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + ObjectFile::Atom* atom = *it; + if ( atom->getFile() != fOutputFile ) + if ( (atom->getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) + || (atom->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { + const char* name = atom->getDisplayName(); + char temp[strlen(name)+2]; + strcpy(temp, name); + temp[strlen(name)-7] = '\0'; // strip trailing "$import" + fprintf(out, "%s; ", temp); + } + } + fprintf(out, "};\n "); + + // print footer + fprintf(out, "}\n"); + fclose(out); + } + else { + fprintf(stderr, "ld64 warning: could not write dot output file: %s\n", dotOutFilePath); + } + } +} + +ObjectFile::Atom* Linker::entryPoint() { // if main executable, find entry point atom ObjectFile::Atom* entryPoint = NULL; @@ -797,13 +1110,495 @@ void Linker::writeOutput() entryPoint = NULL; break; } + return entryPoint; +} + +ObjectFile::Atom* Linker::dyldHelper() +{ + return fGlobalSymbolTable.find("dyld_stub_binding_helper"); +} + +const char* Linker::assureFullPath(const char* path) +{ + if ( path[0] == '/' ) + return path; + char cwdbuff[MAXPATHLEN]; + if ( getcwd(cwdbuff, MAXPATHLEN) != NULL ) { + char* result; + asprintf(&result, "%s/%s", cwdbuff, path); + if ( result != NULL ) + return result; + } + return path; +} + + +// +// The stab strings are of the form: +// ':' +// but the contain a colon. +// For C++ may contain a double colon (e.g. std::string:f(0,1) ) +// For Objective-C name may contain a colon instead square bracket (e.g. [Foo doit:]:f(0,1) ) +// +const char* Linker::truncateStabString(const char* str) +{ + enum { start, inObjc } state = start; + for (const char* s = str; *s != 0; ++s) { + char c = *s; + switch (state) { + case start: + if ( c == '[' ) { + state = inObjc; + } + else { + if ( c == ':' ) { + if ( s[1] == ':' ) { + ++s; + } + else { + // found colon + // Duplicate strndup behavior here. + int trunStrLen = s-str+2; + char* temp = new char[trunStrLen+1]; + memcpy(temp, str, trunStrLen); + temp[trunStrLen] = '\0'; + return temp; + } + } + } + break; + case inObjc: + if ( c == ']' ) { + state = start; + } + break; + } + } + // malformed + return str; +} + + +bool Linker::minimizeStab(ObjectFile::Reader::Stab& stab) +{ + switch(stab.type){ + case N_GSYM: + case N_STSYM: + case N_LCSYM: + case N_FUN: + // these all need truncated strings + stab.string = truncateStabString(stab.string); + return true; + case N_SO: + case N_OSO: + case N_OPT: + case N_SOL: + // these are included in the minimal stabs, but they keep their full string + return true; + default: + return false; + } +} + +struct HeaderRange { + std::vector::iterator begin; + std::vector::iterator end; + int parentRangeIndex; + uint32_t sum; + bool sumPrecomputed; + bool useEXCL; + bool cannotEXCL; // because of SLINE, etc stabs +}; + + +typedef __gnu_cxx::hash_map, __gnu_cxx::hash, CStringEquals> PathToSums; + +// hash table that maps header path to a vector of known checksums for that path +static PathToSums sKnownBINCLs; + + +void Linker::collectStabs(ObjectFile::Reader* reader) +{ + bool log = false; + bool minimal = ( fOptions.readerOptions().fDebugInfoStripping == ObjectFile::ReaderOptions::kDebugInfoMinimal ); + std::vector* readerStabs = reader->getStabs(); + if ( readerStabs == NULL ) + return; + + if ( log ) fprintf(stderr, "processesing %lu stabs for %s\n", readerStabs->size(), reader->getPath()); + // Find all (possibly nested) BINCL/EINCL ranges and their checksums + std::vector ranges; + int curRangeIndex = -1; + int count = 0; + for(std::vector::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) { + ++count; + switch ( it->type ) { + case N_BINCL: + { + HeaderRange range; + range.begin = it; + range.end = readerStabs->end(); + range.parentRangeIndex = curRangeIndex; + range.sum = it->value; + range.sumPrecomputed = (range.sum != 0); + range.useEXCL = false; + range.cannotEXCL = false; + curRangeIndex = ranges.size(); + if ( log ) fprintf(stderr, "[%d]BINCL %s\n", curRangeIndex, it->string); + ranges.push_back(range); + } + break; + case N_EINCL: + if ( curRangeIndex == -1 ) { + fprintf(stderr, "ld64 warning: EINCL missing BINCL in %s\n", reader->getPath()); + } + else { + ranges[curRangeIndex].end = it+1; + if ( log ) fprintf(stderr, "[%d->%d]EINCL %s\n", curRangeIndex, ranges[curRangeIndex].parentRangeIndex, it->string); + curRangeIndex = ranges[curRangeIndex].parentRangeIndex; + } + break; + case N_FUN: + case N_BNSYM: + case N_ENSYM: + case N_LBRAC: + case N_RBRAC: + case N_SLINE: + case N_STSYM: + case N_LCSYM: + if ( curRangeIndex != -1 ) { + ranges[curRangeIndex].cannotEXCL = true; + if ( fOptions.warnStabs() ) + fprintf(stderr, "ld64: cannot do BINCL/EINCL optimzation because of stabs kinds in %s for %s\n", ranges[curRangeIndex].begin->string, reader->getPath()); + } + break; + default: + if ( curRangeIndex != -1 ) { + if ( ! ranges[curRangeIndex].sumPrecomputed ) { + uint32_t sum = 0; + const char* s = it->string; + char c; + while ( (c = *s++) != 0 ) { + sum += c; + // don't checkusm first number (file index) after open paren in string + if ( c == '(' ) { + while(isdigit(*s)) + ++s; + } + } + ranges[curRangeIndex].sum += sum; + } + } + + } + } + if ( log ) fprintf(stderr, "processesed %d stabs for %s\n", count, reader->getPath()); + if ( curRangeIndex != -1 ) + fprintf(stderr, "ld64 warning: BINCL (%s) missing EINCL in %s\n", ranges[curRangeIndex].begin->string, reader->getPath()); + + // if no BINCLs + if ( ranges.size() == 0 ) { + if ( minimal ) { + // only copy minimal stabs + for(std::vector::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) { + if ( minimizeStab(*it) ) + fStabs.push_back(*it); + } + } + else { + // copy all stabs + fStabs.insert(fStabs.end(), readerStabs->begin(), readerStabs->end()); + } + return; + } + + //fprintf(stderr, "BINCL/EINCL info for %s\n", reader->getPath()); + //for(std::vector::iterator it=ranges.begin(); it != ranges.end(); ++it) { + // fprintf(stderr, "%08X %s\n", it->sum, it->begin->string); + //} - // tell writer about each segment's atoms - fOutputFile->write(fAllAtoms, entryPoint); + // see if any of these BINCL/EINCL ranges have already been seen and therefore can be replaced with EXCL + for(std::vector::iterator it=ranges.begin(); it != ranges.end(); ++it) { + if ( ! it->cannotEXCL ) { + const char* header = it->begin->string; + uint32_t sum = it->sum; + PathToSums::iterator pos = sKnownBINCLs.find(header); + if ( pos != sKnownBINCLs.end() ) { + std::vector& sums = pos->second; + for(std::vector::iterator sit=sums.begin(); sit != sums.end(); ++sit) { + if (*sit == sum) { + //fprintf(stderr, "use EXCL for %s in %s\n", header, reader->getPath()); + it->useEXCL = true; + break; + } + } + if ( ! it->useEXCL ) { + // have seen this path, but not this checksum + //fprintf(stderr, "registering another checksum %08X for %s\n", sum, header); + sums.push_back(sum); + } + } + else { + // have not seen this path, so add to known BINCLs + std::vector empty; + sKnownBINCLs[header] = empty; + sKnownBINCLs[header].push_back(sum); + //fprintf(stderr, "registering checksum %08X for %s\n", sum, header); + } + } + } + + // add a new set of stabs with BINCL/EINCL runs that have been seen before, replaced with EXCLs + curRangeIndex = -1; + const int maxRangeIndex = ranges.size(); + for(std::vector::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) { + switch ( it->type ) { + case N_BINCL: + for(int i=curRangeIndex+1; i < maxRangeIndex; ++i) { + if ( ranges[i].begin == it ) { + curRangeIndex = i; + HeaderRange& range = ranges[curRangeIndex]; + ObjectFile::Reader::Stab stab = *it; + stab.value = range.sum; // BINCL and EXCL have n_value set to checksum + if ( range.useEXCL ) + stab.type = N_EXCL; // transform BINCL into EXCL + if ( !minimal ) + fStabs.push_back(stab); + break; + } + } + break; + case N_EINCL: + if ( curRangeIndex != -1 ) { + if ( !ranges[curRangeIndex].useEXCL && !minimal ) + fStabs.push_back(*it); + curRangeIndex = ranges[curRangeIndex].parentRangeIndex; + } + break; + default: + if ( (curRangeIndex == -1) || !ranges[curRangeIndex].useEXCL ) { + if ( !minimal || minimizeStab(*it) ) + fStabs.push_back(*it); + } + } + } + } +void Linker::synthesizeStabs(ObjectFile::Reader* reader) +{ + // synthesize "debug notes" and add them to master stabs vector + const char* dirPath = NULL; + const char* filename = NULL; + bool wroteStartSO = false; + std::vector seenFiles; + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); ++it) { + ObjectFile::Atom* atom = *it; + if ( atom->getFile() == reader ) { + const char* name = atom->getName(); + if ( (name != NULL) && (name[0] == '_' ) && (atom->getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn) ) { + const char* newDirPath; + const char* newFilename; + if ( atom->getTranslationUnitSource(&newDirPath, &newFilename) ) { + // need SO's whenever the translation unit source file changes + if ( newFilename != filename ) { + if ( filename != NULL ) { + // translation unit change, emit ending SO + ObjectFile::Reader::Stab endFileStab; + endFileStab.atom = NULL; + endFileStab.type = N_SO; + endFileStab.other = 1; + endFileStab.desc = 0; + endFileStab.value = 0; + endFileStab.string = ""; + fStabs.push_back(endFileStab); + } + // new translation unit, emit start SO's + ObjectFile::Reader::Stab dirPathStab; + dirPathStab.atom = NULL; + dirPathStab.type = N_SO; + dirPathStab.other = 0; + dirPathStab.desc = 0; + dirPathStab.value = 0; + dirPathStab.string = newDirPath; + fStabs.push_back(dirPathStab); + ObjectFile::Reader::Stab fileStab; + fileStab.atom = NULL; + fileStab.type = N_SO; + fileStab.other = 0; + fileStab.desc = 0; + fileStab.value = 0; + fileStab.string = newFilename; + fStabs.push_back(fileStab); + // Synthesize OSO for start of file + ObjectFile::Reader::Stab objStab; + objStab.atom = NULL; + objStab.type = N_OSO; + objStab.other = 0; + objStab.desc = 1; + objStab.value = reader->getModificationTime(); + objStab.string = assureFullPath(reader->getPath()); + fStabs.push_back(objStab); + wroteStartSO = true; + } + filename = newFilename; + dirPath = newDirPath; + seenFiles.push_back(filename); + if ( atom->getSegment().isContentExecutable() && (strncmp(atom->getSectionName(), "__text", 6) == 0) ) { + // Synthesize BNSYM and start FUN stabs + ObjectFile::Reader::Stab beginSym; + beginSym.atom = atom; + beginSym.type = N_BNSYM; + beginSym.other = 1; + beginSym.desc = 0; + beginSym.value = 0; + beginSym.string = ""; + fStabs.push_back(beginSym); + ObjectFile::Reader::Stab startFun; + startFun.atom = atom; + startFun.type = N_FUN; + startFun.other = 1; + startFun.desc = 0; + startFun.value = 0; + startFun.string = name; + fStabs.push_back(startFun); + // Synthesize any SOL stabs needed + std::vector* lineInfo = atom->getLineInfo(); + if ( lineInfo != NULL ) { + // might be nice to set the source file path to seenFiles so it does not show up in SOLs + const char* curFile = NULL; + for (std::vector::iterator it = lineInfo->begin(); it != lineInfo->end(); ++it) { + if ( it->fileName != curFile ) { + bool alreadySeen = false; + for (std::vector::iterator sit = seenFiles.begin(); sit != seenFiles.end(); ++sit) { + if ( strcmp(it->fileName, *sit) == 0 ) { + alreadySeen = true; + break; + } + } + if ( ! alreadySeen ) { + seenFiles.push_back(it->fileName); + ObjectFile::Reader::Stab sol; + sol.atom = 0; + sol.type = N_SOL; + sol.other = 0; + sol.desc = 0; + sol.value = 0; + sol.string = it->fileName; + fStabs.push_back(sol); + } + curFile = it->fileName; + } + } + } + // Synthesize end FUN and ENSYM stabs + ObjectFile::Reader::Stab endFun; + endFun.atom = atom; + endFun.type = N_FUN; + endFun.other = 0; + endFun.desc = 0; + endFun.value = 0; + endFun.string = ""; + fStabs.push_back(endFun); + ObjectFile::Reader::Stab endSym; + endSym.atom = atom; + endSym.type = N_ENSYM; + endSym.other = 1; + endSym.desc = 0; + endSym.value = 0; + endSym.string = ""; + fStabs.push_back(endSym); + } + else { + ObjectFile::Reader::Stab globalsStab; + if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) { + // Synthesize STSYM stab for statics + const char* name = atom->getName(); + if ( name[0] == '_' ) { + globalsStab.atom = atom; + globalsStab.type = N_STSYM; + globalsStab.other = 1; + globalsStab.desc = 0; + globalsStab.value = 0; + globalsStab.string = name; + fStabs.push_back(globalsStab); + } + } + else { + // Synthesize GSYM stab for other globals (but not .eh exception frame symbols) + const char* name = atom->getName(); + if ( (name[0] == '_') && (strcmp(atom->getSectionName(), "__eh_frame") != 0) ) { + globalsStab.atom = atom; + globalsStab.type = N_GSYM; + globalsStab.other = 1; + globalsStab.desc = 0; + globalsStab.value = 0; + globalsStab.string = name; + fStabs.push_back(globalsStab); + } + } + } + } + } + } + } + if ( wroteStartSO ) { + // emit ending SO + ObjectFile::Reader::Stab endFileStab; + endFileStab.atom = NULL; + endFileStab.type = N_SO; + endFileStab.other = 1; + endFileStab.desc = 0; + endFileStab.value = 0; + endFileStab.string = ""; + fStabs.push_back(endFileStab); + } +} + +void Linker::collectStabs() +{ + if ( fOptions.readerOptions().fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone ) { + fStabs.reserve(1024); // try to minimize re-allocations + // get stabs from each reader, in command line order + for (std::vector::iterator it=fReadersThatHaveSuppliedAtoms.begin(); + it != fReadersThatHaveSuppliedAtoms.end(); + it++) { + ObjectFile::Reader* reader = *it; + if ( reader != NULL ) { + switch ( reader->getDebugInfoKind() ) { + case ObjectFile::Reader::kDebugInfoNone: + // do nothing + break; + case ObjectFile::Reader::kDebugInfoStabs: + collectStabs(reader); + break; + case ObjectFile::Reader::kDebugInfoDwarf: + synthesizeStabs(reader); + fCreateUUID = true; + break; + case ObjectFile::Reader::kDebugInfoStabsUUID: + collectStabs(reader); + fCreateUUID = true; + break; + default: + throw "Unhandled type of debug information"; + } + } + } + // remove stabs associated with atoms that won't be in output + std::set allAtomsSet; + allAtomsSet.insert(fAllAtoms.begin(), fAllAtoms.end()); + fStabs.erase(std::remove_if(fStabs.begin(), fStabs.end(), NotInSet(allAtomsSet)), fStabs.end()); + } +} +void Linker::writeOutput() +{ + fStartWriteTime = mach_absolute_time(); + // tell writer about each segment's atoms + fOutputFileSize = fOutputFile->write(fAllAtoms, fStabs, this->entryPoint(), this->dyldHelper(), (fCreateUUID && fOptions.emitUUID())); +} ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) { @@ -814,30 +1609,69 @@ ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) throwf("can't open file, errno=%d", errno); if ( info.fileLen < 20 ) throw "file too small"; - - char* p = (char*)::mmap(NULL, info.fileLen, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); - if ( p == (char*)(-1) ) + + uint8_t* p = (uint8_t*)::mmap(NULL, info.fileLen, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if ( p == (uint8_t*)(-1) ) throwf("can't map file, errno=%d", errno); - ::close(fd); - + // if fat file, skip to architecture we want - const mach_header* mh = (mach_header*)p; - if ( mh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + const fat_header* fh = (fat_header*)p; + if ( fh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { // Fat header is always big-endian - const struct fat_header* fh = (struct fat_header*)p; const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { - if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)fOptions.architecture() ) { - mh = (struct mach_header*)((char*)p + OSSwapBigToHostInt32(archs[i].offset)); + if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)fArchitecture ) { + uint32_t fileOffset = OSSwapBigToHostInt32(archs[i].offset); len = OSSwapBigToHostInt32(archs[i].size); + // if requested architecture is page aligned within fat file, then remap just that portion of file + if ( (fileOffset && 0x00000FFF) == 0 ) { + // unmap whole file + munmap((caddr_t)p, info.fileLen); + // re-map just part we need + p = (uint8_t*)::mmap(NULL, len, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, fileOffset); + if ( p == (uint8_t*)(-1) ) + throwf("can't re-map file, errno=%d", errno); + } + else { + p = &p[fileOffset]; + } break; } } } - - if ( mh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + ::close(fd); + + switch (fArchitecture) { + case CPU_TYPE_POWERPC: + if ( mach_o::relocatable::Reader::validFile(p) ) + return this->addObject(mach_o::relocatable::Reader::make(p, info.path, info.modTime, fOptions.readerOptions()), info, len); + else if ( mach_o::dylib::Reader::validFile(p) ) + return this->addDylib(mach_o::dylib::Reader::make(p, len, info.path, fOptions.readerOptions()), info, len); + else if ( mach_o::archive::Reader::validFile(p, len) ) + return this->addArchive(mach_o::archive::Reader::make(p, len, info.path, info.modTime, fOptions.readerOptions()), info, len); + break; + case CPU_TYPE_POWERPC64: + if ( mach_o::relocatable::Reader::validFile(p) ) + return this->addObject(mach_o::relocatable::Reader::make(p, info.path, info.modTime, fOptions.readerOptions()), info, len); + else if ( mach_o::dylib::Reader::validFile(p) ) + return this->addDylib(mach_o::dylib::Reader::make(p, len, info.path, fOptions.readerOptions()), info, len); + else if ( mach_o::archive::Reader::validFile(p, len) ) + return this->addArchive(mach_o::archive::Reader::make(p, len, info.path, info.modTime, fOptions.readerOptions()), info, len); + break; + case CPU_TYPE_I386: + if ( mach_o::relocatable::Reader::validFile(p) ) + return this->addObject(mach_o::relocatable::Reader::make(p, info.path, info.modTime, fOptions.readerOptions()), info, len); + else if ( mach_o::dylib::Reader::validFile(p) ) + return this->addDylib(mach_o::dylib::Reader::make(p, len, info.path, fOptions.readerOptions()), info, len); + else if ( mach_o::archive::Reader::validFile(p, len) ) + return this->addArchive(mach_o::archive::Reader::make(p, len, info.path, info.modTime, fOptions.readerOptions()), info, len); + break; + } + + // error handling + if ( ((fat_header*)p)->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { const char* archName = "unknown"; - switch (fOptions.architecture()) { + switch (fArchitecture) { case CPU_TYPE_POWERPC: archName = "ppc"; break; @@ -850,86 +1684,15 @@ ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) } throwf("missing required architecture %s in fat file", archName); } - - // pull out cpu-type and file-type in endian-safe way - cpu_type_t cpuType = 0; - uint32_t fileType = 0; - if ( mh->magic == MH_MAGIC ) { - fileType = mh->filetype; - cpuType = mh->cputype; - } - else if ( mh->magic == OSSwapInt32(MH_MAGIC) ) { - fileType = OSSwapInt32(mh->filetype); - cpuType = OSSwapInt32(mh->cputype); - } - else if ( mh->magic == MH_MAGIC_64 ) { - fileType = ((mach_header_64*)mh)->filetype; - cpuType = ((mach_header_64*)mh)->cputype; - } - else if ( mh->magic == OSSwapInt32(MH_MAGIC_64) ) { - fileType = OSSwapInt32(((mach_header_64*)mh)->filetype); - cpuType = OSSwapInt32(((mach_header_64*)mh)->cputype); - } - else if ( strncmp((const char*)mh, "!\n", 8) == 0 ) { - // is static archive - switch ( fOptions.architecture() ) { - case CPU_TYPE_POWERPC: - return ppc::ObjectFileArchiveMachO::MakeReader((const uint8_t*)mh, len, info.path, fOptions.readerOptions()); - case CPU_TYPE_POWERPC64: - return ppc64::ObjectFileArchiveMachO::MakeReader((const uint8_t*)mh, len, info.path, fOptions.readerOptions()); - case CPU_TYPE_I386: - return i386::ObjectFileArchiveMachO::MakeReader((const uint8_t*)mh, len, info.path, fOptions.readerOptions()); - } - throw "no matching archive reader"; - } else { - throw "unknown file type"; - } - - // bail out if cpu-type does not match requrired architecture - if ( fOptions.architecture() == cpuType ) { - // make appropriate reader object - if ( fileType == MH_OBJECT ) { - switch ( cpuType ) { - case CPU_TYPE_POWERPC: - return ppc::ObjectFileMachO::MakeReader((class ppc::macho_header*)mh, info.path, fOptions.readerOptions()); - case CPU_TYPE_POWERPC64: - return ppc64::ObjectFileMachO::MakeReader((class ppc64::macho_header*)mh, info.path, fOptions.readerOptions()); - case CPU_TYPE_I386: - return i386::ObjectFileMachO::MakeReader((class i386::macho_header*)mh, info.path, fOptions.readerOptions()); - default: - throw "wrong architecture in object file"; - } - } - else if ( (fileType == MH_DYLIB) || (fileType == MH_DYLIB_STUB) ) { - ObjectFile::Reader* dylibReader = NULL; - switch ( cpuType ) { - case CPU_TYPE_POWERPC: - dylibReader = ppc::ObjectFileDylibMachO::MakeReader((class ppc::macho_header*)mh, info.path, fOptions.readerOptions()); - break; - case CPU_TYPE_POWERPC64: - dylibReader = ppc64::ObjectFileDylibMachO::MakeReader((class ppc64::macho_header*)mh, info.path, fOptions.readerOptions()); - break; - case CPU_TYPE_I386: - dylibReader = i386::ObjectFileDylibMachO::MakeReader((class i386::macho_header*)mh, info.path, fOptions.readerOptions()); - break; - default: - throw "wrong architecture in dylib"; - } - this->addDylib(dylibReader, info); - return dylibReader; - } - throw "unknown mach-o file type"; + throw "file is not of required architecture"; } - else { - throw "file does not contain requested architecture"; - } - } void Linker::createReaders() { + fStartCreateReadersTime = mach_absolute_time(); std::vector& files = fOptions.getInputFiles(); const int count = files.size(); if ( count == 0 ) @@ -957,13 +1720,13 @@ void Linker::createReaders() } } } - + // add first level of indirect dylibs fDirectLibrariesComplete = true; for (std::vector::iterator it=fDynamicLibraries.begin(); it != fDynamicLibraries.end(); it++) { this->addIndirectLibraries(it->reader); } - + // indirect handling depends on namespace switch ( fOptions.nameSpace() ) { case Options::kFlatNameSpace: @@ -973,13 +1736,14 @@ void Linker::createReaders() for (std::list::iterator it=fIndirectDynamicLibraries.begin(); it != fIndirectDynamicLibraries.end(); it++) { try { it->reader = this->createReader(fOptions.findFile(it->path)); + it->reader->setSortOrder(fNextObjectFileOrder++); } catch (const char* msg) { fprintf(stderr, "ld64 warning: indirect library %s could not be loaded: %s\n", it->path, msg); } } break; - + case Options::kTwoLevelNameSpace: // with two-level namespace we only want to use indirect libraries that are re-exported through a library that is used { @@ -991,6 +1755,7 @@ void Linker::createReaders() if ( it->reader == NULL ) { try { it->reader = this->createReader(fOptions.findFile(it->path)); + it->reader->setSortOrder(fNextObjectFileOrder++); indirectAdded = true; } catch (const char* msg) { @@ -998,55 +1763,174 @@ void Linker::createReaders() } } // if an indirect library does not have an assigned parent, look for one - if ( (it->reader != NULL) && (it->reExportParent == NULL) ) { - // ask each parent if they re-export this dylib - for (std::set::iterator pit=it->parents.begin(); pit != it->parents.end(); pit++) { - if ( (*pit)->reExports(it->reader) ) { - it->reExportParent = *pit; - break; - } - } + if ( (it->reader != NULL) && (it->reExportedViaDirectLibrary == NULL) ) { + it->reExportedViaDirectLibrary = this->findDirectLibraryWhichReExports(*it); } } } } break; } - + // add relevant indirect libraries to the end of fDynamicLibraries for (std::list::iterator it=fIndirectDynamicLibraries.begin(); it != fIndirectDynamicLibraries.end(); it++) { - if ( (it->reExportParent != NULL) || (fOptions.nameSpace() != Options::kTwoLevelNameSpace) ) { + if ( (it->reader != NULL) && (it->reExportedViaDirectLibrary != NULL) || (fOptions.nameSpace() != Options::kTwoLevelNameSpace) ) { ExecutableFile::DyLibUsed dylibInfo; dylibInfo.reader = it->reader; dylibInfo.options.fWeakImport = false; dylibInfo.options.fReExport = false; dylibInfo.options.fInstallPathOverride = NULL; dylibInfo.indirect = true; - dylibInfo.directReader = it->reExportParent; + dylibInfo.directReader = it->reExportedViaDirectLibrary; fDynamicLibraries.push_back(dylibInfo); - if ( fOptions.readerOptions().fTraceIndirectDylibs ) - printf("[Logging for Build & Integration] Used indirect dynamic library: %s\n", it->reader->getPath()); + if ( fOptions.readerOptions().fTraceIndirectDylibs ) { + const char* fullPath = it->reader->getPath(); + char realName[MAXPATHLEN]; + if ( realpath(fullPath, realName) != NULL ) + fullPath = realName; + logTraceInfo("[Logging for XBS] Used indirect dynamic library: %s\n", fullPath); + } + } + } +} + + +ObjectFile::Reader* Linker::findDirectLibraryWhichReExports(IndirectLibrary& indirectLib) +{ + // ask each parent if they re-export this dylib + for (std::set::iterator pit=indirectLib.parents.begin(); pit != indirectLib.parents.end(); pit++) { + if ( (*pit)->reExports(indirectLib.reader) ) { + ObjectFile::Reader* lib = *pit; + // first check if we found a direct library, if so return it + for (std::vector::iterator dit=fDynamicLibraries.begin(); dit != fDynamicLibraries.end(); dit++) { + if ( dit->reader == lib && dit->indirect == false ) + return lib; + } + // otherwise search indirects for parent and see how it is reexported + for (std::list::iterator iit=fIndirectDynamicLibraries.begin(); iit != fIndirectDynamicLibraries.end(); iit++) { + if ( iit->reader == lib ) { + ObjectFile::Reader* lib2 = this->findDirectLibraryWhichReExports(*iit); + if ( lib2 != NULL ) + return lib2; + } + } } } + return NULL; } -void Linker::addDylib(ObjectFile::Reader* reader, const Options::FileInfo& info) +ObjectFile::Reader* Linker::addArchive(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen) { + if (fOptions.readerOptions().fTraceArchives) { + const char* fullPath = reader->getPath(); + char realName[MAXPATHLEN]; + if ( realpath(fullPath, realName) != NULL ) + fullPath = realName; + logTraceInfo("[Logging for XBS] Used static archive: %s\n", fullPath); + } + + // update stats + fTotalArchiveSize += mappedLen; + ++fTotalArchivesLoaded; + return reader; +} + +ObjectFile::Reader* Linker::addObject(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen) +{ + // update stats + fTotalObjectSize += mappedLen; + ++fTotalObjectLoaded; + return reader; +} + +ObjectFile::Reader* Linker::addDylib(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen) +{ + if ( reader->getInstallPath() == NULL ) { + // this is a "blank" stub + // silently ignore it + return reader; + } + if ( fDirectLibrariesComplete ) { this->addIndirectLibraries(reader); } else { - if ( fOptions.readerOptions().fTraceDylibs ) - printf("[Logging for Build & Integration] Used dynamic library: %s\n", reader->getPath()); + if ( fOptions.readerOptions().fTraceDylibs ) { + const char* fullPath = reader->getPath(); + char realName[MAXPATHLEN]; + if ( realpath(fullPath, realName) != NULL ) + fullPath = realName; + logTraceInfo("[Logging for XBS] Used dynamic library: %s\n", fullPath); + } ExecutableFile::DyLibUsed dylibInfo; dylibInfo.reader = reader; dylibInfo.options = info.options; dylibInfo.indirect = false; dylibInfo.directReader = NULL; fDynamicLibraries.push_back(dylibInfo); + + + // Verify that a client is allowed to link to this dylib. There are three cases. + bool okToLink = true; + const char* outputFilePath = fOptions.installPath(); + const char* outputFilePathLastSlash = strrchr(outputFilePath, '/'); + if ( reader->parentUmbrella() != NULL ) { + // case 1) The dylib has a parent umbrella, and we are creating the parent umbrella + okToLink = ( (outputFilePathLastSlash != NULL) && (strcmp(&outputFilePathLastSlash[1], reader->parentUmbrella()) == 0) ); + } + + if ( !okToLink && (reader->parentUmbrella() != NULL) ) { + // case 2) The dylib has a parent umbrella, and we are creating a sibling with the same parent + okToLink = ( (outputFilePathLastSlash != NULL) + && (fOptions.umbrellaName() != NULL) + && (strcmp(fOptions.umbrellaName(), reader->parentUmbrella()) == 0) ); + } + + std::vector* clients = reader->getAllowableClients(); + if ( !okToLink && (clients != NULL) ) { + // case 3) the dylib has a list of allowable clients, and we are creating one of them + const char* clientName = fOptions.clientName(); + int clientNameLen = 0; + if ( clientName != NULL ) { + // use client name as specified on command line + clientNameLen = strlen(clientName); + } + else { + // infer client name from output path (e.g. xxx/libfoo.A.dylib --> foo, Bar.framework/Bar --> Bar) + clientName = outputFilePath; + // starts after last slash + if ( outputFilePathLastSlash != NULL ) + clientName = &outputFilePathLastSlash[1]; + if ( strncmp(clientName, "lib", 3) == 0 ) + clientName = &clientName[3]; + // up to first dot + const char* firstDot = strchr(clientName, '.'); + if ( firstDot == NULL ) + clientNameLen = strlen(clientName); + else + clientNameLen = firstDot - clientName; + } + + // Use clientName to check if this dylib is able to link against the allowable clients. + for (std::vector::iterator it = clients->begin(); it != clients->end(); it++) { + if ( strncmp(*it, clientName, clientNameLen) == 0 ) + okToLink = true; + } + } + + // error out if we are not allowed to link + if ( ! okToLink ) + //throwf("'%s' is a subframework. Link against the umbrella framework '%s.framework' instead.", + fprintf(stderr, "'%s' is a subframework. Link against the umbrella framework '%s.framework' instead.", + reader->getPath(), reader->parentUmbrella()); } + + // update stats + ++fTotalDylibsLoaded; + + return reader; } @@ -1068,7 +1952,7 @@ void Linker::addIndirectLibraries(ObjectFile::Reader* reader) indirectLib.fileLen = 0; indirectLib.reader = NULL; indirectLib.parents.insert(reader); - indirectLib.reExportParent = NULL; + indirectLib.reExportedViaDirectLibrary = NULL; fIndirectDynamicLibraries.push_back(indirectLib); //fprintf(stderr, "add indirect library: %s\n", *it); } @@ -1104,21 +1988,57 @@ bool Linker::haveDirectLibrary(const char* path) return false; } +void Linker::logTraceInfo (const char* format, ...) +{ + static int trace_file = -1; + char trace_buffer[MAXPATHLEN * 2]; + char *buffer_ptr; + int length; + ssize_t amount_written; + const char *trace_file_path = fOptions.readerOptions().fTraceOutputFile; + + if(trace_file == -1) { + if(trace_file_path != NULL) { + trace_file = open(trace_file_path, O_WRONLY | O_APPEND | O_CREAT, 0666); + if(trace_file == -1) + throwf("Could not open or create trace file: %s\n", trace_file_path); + } + else { + trace_file = fileno(stderr); + } + } + + va_list ap; + va_start(ap, format); + length = vsnprintf(trace_buffer, sizeof(trace_buffer), format, ap); + va_end(ap); + buffer_ptr = trace_buffer; + + while(length > 0) { + amount_written = write(trace_file, buffer_ptr, length); + if(amount_written == -1) + /* Failure to write shouldn't fail the build. */ + return; + buffer_ptr += amount_written; + length -= amount_written; + } +} void Linker::createWriter() { + fStartCreateWriterTime = mach_absolute_time(); const char* path = fOptions.getOutputFilePath(); - switch ( fOptions.architecture() ) { + switch ( fArchitecture ) { case CPU_TYPE_POWERPC: - this->setOutputFile(ppc::ExecutableFileMachO::MakeWriter(path, fOptions, fDynamicLibraries)); + this->setOutputFile(new mach_o::executable::Writer(path, fOptions, fDynamicLibraries)); break; case CPU_TYPE_POWERPC64: - this->setOutputFile(ppc64::ExecutableFileMachO::MakeWriter(path, fOptions, fDynamicLibraries)); + this->setOutputFile(new mach_o::executable::Writer(path, fOptions, fDynamicLibraries)); break; case CPU_TYPE_I386: - this->setOutputFile(i386::ExecutableFileMachO::MakeWriter(path, fOptions, fDynamicLibraries)); + this->setOutputFile(new mach_o::executable::Writer(path, fOptions, fDynamicLibraries)); break; default: throw "unknown architecture"; @@ -1141,132 +2061,194 @@ void Linker::SymbolTable::require(const char* name) } } -bool Linker::SymbolTable::add(ObjectFile::Atom& atom) +// convenience labels for 2-dimensional switch statement +enum { + kRegAndReg = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kRegularDefinition, + kRegAndWeak = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kWeakDefinition, + kRegAndTent = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, + kRegAndExtern = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kExternalDefinition, + kRegAndExternWeak = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, + kWeakAndReg = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kRegularDefinition, + kWeakAndWeak = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kWeakDefinition, + kWeakAndTent = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, + kWeakAndExtern = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kExternalDefinition, + kWeakAndExternWeak = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, + kTentAndReg = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kRegularDefinition, + kTentAndWeak = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kWeakDefinition, + kTentAndTent = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, + kTentAndExtern = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kExternalDefinition, + kTentAndExternWeak = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, + kExternAndReg = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kRegularDefinition, + kExternAndWeak = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kWeakDefinition, + kExternAndTent = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, + kExternAndExtern = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kExternalDefinition, + kExternAndExternWeak = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, + kExternWeakAndReg = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kRegularDefinition, + kExternWeakAndWeak = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kWeakDefinition, + kExternWeakAndTent = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, + kExternWeakAndExtern = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kExternalDefinition, + kExternWeakAndExternWeak= (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition +}; + +bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) { - const bool log = false; - const char* name = atom.getName(); - //fprintf(stderr, "map.add(%p: %s => %p)\n", &fTable, name, &atom); + bool useNew = true; + const char* name = newAtom.getName(); + //fprintf(stderr, "map.add(%s => %p from %s)\n", name, &newAtom, newAtom.getFile()->getPath()); Mapper::iterator pos = fTable.find(name); - if ( pos != fTable.end() ) { - ObjectFile::Atom* existingAtom = pos->second; - if ( existingAtom != NULL ) { - if ( existingAtom->isTentativeDefinition() ) { - if ( atom.isTentativeDefinition() ) { - if ( atom.getSize() > existingAtom->getSize() ) { - // replace common-symbol atom with another larger common-symbol - if ( fOwner.fOptions.warnCommons() ) - fprintf(stderr, "ld64: replacing common symbol %s size %lld from %s with larger symbol size %lld from %s\n", - existingAtom->getName(), existingAtom->getSize(), existingAtom->getFile()->getPath(), atom.getSize(), atom.getFile()->getPath()); - fOwner.fDeadAtoms.insert(existingAtom); - fTable[name] = &atom; - return true; - } - else { - // keep existing common-symbol atom - if ( fOwner.fOptions.warnCommons() ) { - if ( atom.getSize() == existingAtom->getSize() ) - fprintf(stderr, "ld64: ignoring common symbol %s from %s because already have common from %s with same size\n", - atom.getName(), atom.getFile()->getPath(), existingAtom->getFile()->getPath()); - else - fprintf(stderr, "ld64: ignoring common symbol %s size %lld from %s because already have larger symbol size %lld from %s\n", - atom.getName(), atom.getSize(), atom.getFile()->getPath(), existingAtom->getSize(), existingAtom->getFile()->getPath()); - } - fOwner.fDeadAtoms.insert(&atom); - return false; - } + ObjectFile::Atom* existingAtom = NULL; + if ( pos != fTable.end() ) + existingAtom = pos->second; + if ( existingAtom != NULL ) { + // already have atom with same name in symbol table + switch ( (existingAtom->getDefinitionKind() << 3) | newAtom.getDefinitionKind() ) { + case kRegAndReg: + throwf("duplicate symbol %s in %s and %s\n", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); + case kRegAndWeak: + // ignore new weak atom, because we already have a non-weak one + useNew = false; + break; + case kRegAndTent: + // ignore new tentative atom, because we already have a regular one + useNew = false; + break; + case kRegAndExtern: + // ignore external atom, because we already have a one + useNew = false; + break; + case kRegAndExternWeak: + // ignore external atom, because we already have a one + useNew = false; + break; + case kWeakAndReg: + // replace existing weak atom with regular one + break; + case kWeakAndWeak: + // have another weak atom, use whichever has largest alignment requirement + // because codegen of some client may require alignment + useNew = ( newAtom.getAlignment() > existingAtom->getAlignment() ); + break; + case kWeakAndTent: + // replace existing weak atom with tentative one ??? + break; + case kWeakAndExtern: + // keep weak atom, at runtime external one may override + useNew = false; + break; + case kWeakAndExternWeak: + // keep weak atom, at runtime external one may override + useNew = false; + break; + case kTentAndReg: + // replace existing tentative atom with regular one + break; + case kTentAndWeak: + // replace existing tentative atom with weak one ??? + break; + case kTentAndTent: + // use largest + if ( newAtom.getSize() < existingAtom->getSize() ) { + useNew = false; } - else { - // have common symbol, now found true defintion - if ( atom.isImportProxy() ) { - // definition is in a dylib, so commons-mode decides how to handle - switch ( fOwner.fOptions.commonsMode() ) { - case Options::kCommonsIgnoreDylibs: - if ( fOwner.fOptions.warnCommons() ) - fprintf(stderr, "ld64: using common symbol %s from %s and ignoring defintion from dylib %s\n", - existingAtom->getName(), existingAtom->getFile()->getPath(), atom.getFile()->getPath()); - fOwner.fDeadAtoms.insert(&atom); - return false; - case Options::kCommonsOverriddenByDylibs: - if ( fOwner.fOptions.warnCommons() ) - fprintf(stderr, "ld64: replacing common symbol %s from %s with true definition from %s\n", - existingAtom->getName(), existingAtom->getFile()->getPath(), atom.getFile()->getPath()); - fOwner.fDeadAtoms.insert(existingAtom); - fTable[name] = &atom; - return true; - case Options::kCommonsConflictsDylibsError: - throwf("common symbol %s from %s conflicts with defintion from dylib %s", - existingAtom->getName(), existingAtom->getFile()->getPath(), atom.getFile()->getPath()); - } - } - else { - // replace common-symbol atom with true definition from .o file - if ( fOwner.fOptions.warnCommons() ) { - if ( atom.getSize() < existingAtom->getSize() ) - fprintf(stderr, "ld64: warning: replacing common symbol %s size %lld from %s with smaller true definition size %lld from %s\n", - existingAtom->getName(), existingAtom->getSize(), existingAtom->getFile()->getPath(), atom.getSize(), atom.getFile()->getPath()); - else - fprintf(stderr, "ld64: replacing common symbol %s from %s with true definition from %s\n", - existingAtom->getName(), existingAtom->getFile()->getPath(), atom.getFile()->getPath()); - } - fOwner.fDeadAtoms.insert(existingAtom); - fTable[name] = &atom; - return true; - } + break; + case kTentAndExtern: + case kTentAndExternWeak: + // a tentative definition and a dylib definition, so commons-mode decides how to handle + switch ( fOwner.fOptions.commonsMode() ) { + case Options::kCommonsIgnoreDylibs: + if ( fOwner.fOptions.warnCommons() ) + fprintf(stderr, "ld64: using common symbol %s from %s and ignoring defintion from dylib %s\n", + existingAtom->getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); + useNew = false; + break; + case Options::kCommonsOverriddenByDylibs: + if ( fOwner.fOptions.warnCommons() ) + fprintf(stderr, "ld64: replacing common symbol %s from %s with true definition from dylib %s\n", + existingAtom->getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); + break; + case Options::kCommonsConflictsDylibsError: + throwf("common symbol %s from %s conflicts with defintion from dylib %s", + existingAtom->getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); } - } - else if ( atom.isTentativeDefinition() ) { - // keep existing true definition, ignore new tentative definition - if ( fOwner.fOptions.warnCommons() ) { - if ( atom.getSize() > existingAtom->getSize() ) - fprintf(stderr, "ld64: warning: ignoring common symbol %s size %lld from %s because already have definition from %s size %lld, even though definition is smaller\n", - atom.getName(), atom.getSize(), atom.getFile()->getPath(), existingAtom->getFile()->getPath(), existingAtom->getSize()); - else - fprintf(stderr, "ld64: ignoring common symbol %s from %s because already have definition from %s\n", - atom.getName(), atom.getFile()->getPath(), existingAtom->getFile()->getPath()); + break; + case kExternAndReg: + // replace external atom with regular one + break; + case kExternAndWeak: + // replace external atom with weak one + break; + case kExternAndTent: + // a tentative definition and a dylib definition, so commons-mode decides how to handle + switch ( fOwner.fOptions.commonsMode() ) { + case Options::kCommonsIgnoreDylibs: + if ( fOwner.fOptions.warnCommons() ) + fprintf(stderr, "ld64: using common symbol %s from %s and ignoring defintion from dylib %s\n", + newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); + break; + case Options::kCommonsOverriddenByDylibs: + if ( fOwner.fOptions.warnCommons() ) + fprintf(stderr, "ld64: replacing defintion of %s from dylib %s with common symbol from %s\n", + newAtom.getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); + useNew = false; + break; + case Options::kCommonsConflictsDylibsError: + throwf("common symbol %s from %s conflicts with defintion from dylib %s", + newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); } - fOwner.fDeadAtoms.insert(&atom); - return false; - } - else { - // neither existing nor new atom are tentative definitions - // if existing is weak, we may replace it - if ( existingAtom->isWeakDefinition() ) { - if ( atom.isImportProxy() ) { - // keep weak definition even though one exists in a dylib, because coalescing means dylib's copy may not be used - if ( log ) fprintf(stderr, "keep weak atom even though also in a dylib: %s\n", atom.getName()); - fOwner.fDeadAtoms.insert(&atom); - return false; - } - else if ( atom.isWeakDefinition() ) { - // have another weak atom, use existing, mark new as dead - if ( log ) fprintf(stderr, "already have weak atom: %s\n", atom.getName()); - fOwner.fDeadAtoms.insert(&atom); - return false; - } - else { - // replace weak atom with non-weak atom - if ( log ) fprintf(stderr, "replacing weak atom %p from %s with %p from %s: %s\n", existingAtom, existingAtom->getFile()->getPath(), &atom, atom.getFile()->getPath(), atom.getName()); - fOwner.fDeadAtoms.insert(existingAtom); - fTable[name] = &atom; - return true; - } + break; + case kExternAndExtern: + throwf("duplicate symbol %s in %s and %s\n", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); + case kExternAndExternWeak: + // keep strong dylib atom, ignore weak one + useNew = false; + break; + case kExternWeakAndReg: + // replace existing weak external with regular + break; + case kExternWeakAndWeak: + // replace existing weak external with weak (let dyld decide at runtime which to use) + break; + case kExternWeakAndTent: + // a tentative definition and a dylib definition, so commons-mode decides how to handle + switch ( fOwner.fOptions.commonsMode() ) { + case Options::kCommonsIgnoreDylibs: + if ( fOwner.fOptions.warnCommons() ) + fprintf(stderr, "ld64: using common symbol %s from %s and ignoring defintion from dylib %s\n", + newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); + break; + case Options::kCommonsOverriddenByDylibs: + if ( fOwner.fOptions.warnCommons() ) + fprintf(stderr, "ld64: replacing defintion of %s from dylib %s with common symbol from %s\n", + newAtom.getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); + useNew = false; + break; + case Options::kCommonsConflictsDylibsError: + throwf("common symbol %s from %s conflicts with defintion from dylib %s", + newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); } - } - if ( atom.isWeakDefinition() ) { - // ignore new weak atom, because we already have a non-weak one - return false; - } - if ( atom.isCoalesableByName() && existingAtom->isCoalesableByName() ) { - // both coalesable, so ignore duplicate - return false; - } - fprintf(stderr, "duplicate symbol %s in %s and %s\n", name, atom.getFile()->getPath(), existingAtom->getFile()->getPath()); + break; + case kExternWeakAndExtern: + // replace existing weak external with external + break; + case kExternWeakAndExternWeak: + // keep existing external weak + useNew = false; + break; } } - fTable[name] = &atom; - return true; + if ( useNew ) { + fTable[name] = &newAtom; + if ( existingAtom != NULL ) + fOwner.fDeadAtoms.insert(existingAtom); + } + else { + fOwner.fDeadAtoms.insert(&newAtom); + } + return useNew; } + + ObjectFile::Atom* Linker::SymbolTable::find(const char* name) { Mapper::iterator pos = fTable.find(name); @@ -1280,7 +2262,7 @@ ObjectFile::Atom* Linker::SymbolTable::find(const char* name) void Linker::SymbolTable::getNeededNames(bool andWeakDefintions, std::vector& undefines) { for (Mapper::iterator it=fTable.begin(); it != fTable.end(); it++) { - if ( (it->second == NULL) || (andWeakDefintions && it->second->isWeakDefinition()) ) { + if ( (it->second == NULL) || (andWeakDefintions && (it->second->getDefinitionKind()==ObjectFile::Atom::kWeakDefinition)) ) { undefines.push_back(it->first); } } @@ -1296,8 +2278,14 @@ bool Linker::AtomSorter::operator()(ObjectFile::Atom* left, ObjectFile::Atom* ri unsigned int rightSectionIndex = right->getSection()->getIndex(); if ( leftSectionIndex != rightSectionIndex) return (leftSectionIndex < rightSectionIndex); - - // with a section, sort by original atom order (.o file order and atom order in .o files) + + // then sort by .o file order + ObjectFile::Reader* leftReader = left->getFile(); + ObjectFile::Reader* rightReader = right->getFile(); + if ( leftReader != rightReader ) + return leftReader->getSortOrder() < rightReader->getSortOrder(); + + // lastly sort by atom within a .o file return left->getSortOrder() < right->getSortOrder(); } @@ -1307,13 +2295,13 @@ int main(int argc, const char* argv[]) try { // create linker object given command line arguments Linker ld(argc, argv); - + // open all input files ld.createReaders(); - + // open output file ld.createWriter(); - + // do linking ld.link(); } @@ -1321,13 +2309,7 @@ int main(int argc, const char* argv[]) fprintf(stderr, "ld64 failed: %s\n", msg); return 1; } - - return 0; -} - - - - - + return 0; +} diff --git a/src/machochecker.cpp b/src/machochecker.cpp new file mode 100644 index 0000000..55f9fd9 --- /dev/null +++ b/src/machochecker.cpp @@ -0,0 +1,519 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "MachOFileAbstraction.hpp" +#include "Architectures.hpp" + + + __attribute__((noreturn)) +void throwf(const char* format, ...) +{ + va_list list; + char* p; + va_start(list, format); + vasprintf(&p, format, list); + va_end(list); + + const char* t = p; + throw t; +} + + +template +class MachOChecker +{ +public: + static bool validFile(const uint8_t* fileContent); + static MachOChecker* make(const uint8_t* fileContent, uint32_t fileLength, const char* path) + { return new MachOChecker(fileContent, fileLength, path); } + virtual ~MachOChecker() {} + + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path); + void checkMachHeader(); + void checkLoadCommands(); + void checkSection(const macho_segment_command

* segCmd, const macho_section

* sect); + uint8_t loadCommandSizeMask(); + void checkIndirectSymbolTable(); + + const char* fPath; + const macho_header

* fHeader; + uint32_t fLength; + const char* fStrings; + const char* fStringsEnd; + const macho_nlist

* fSymbols; + uint32_t fSymbolCount; + const uint32_t* fIndirectTable; + uint32_t fIndirectTableCount; + +}; + + + +template <> +bool MachOChecker::validFile(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return false; + if ( header->cputype() != CPU_TYPE_POWERPC ) + return false; + switch (header->filetype()) { + case MH_EXECUTE: + case MH_DYLIB: + case MH_BUNDLE: + case MH_DYLINKER: + return true; + } + return false; +} + +template <> +bool MachOChecker::validFile(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC_64 ) + return false; + if ( header->cputype() != CPU_TYPE_POWERPC64 ) + return false; + switch (header->filetype()) { + case MH_EXECUTE: + case MH_DYLIB: + case MH_BUNDLE: + case MH_DYLINKER: + return true; + } + return false; +} + +template <> +bool MachOChecker::validFile(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return false; + if ( header->cputype() != CPU_TYPE_I386 ) + return false; + switch (header->filetype()) { + case MH_EXECUTE: + case MH_DYLIB: + case MH_BUNDLE: + case MH_DYLINKER: + return true; + } + return false; +} + + + +template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x03; } +template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x07; } +template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x03; } + + +template +MachOChecker::MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path) + : fHeader(NULL), fLength(fileLength), fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fIndirectTableCount(0) +{ + // sanity check + if ( ! validFile(fileContent) ) + throw "not a mach-o file that can be checked"; + + fPath = strdup(path); + fHeader = (const macho_header

*)fileContent; + + // sanity check header + checkMachHeader(); + + // check load commands + checkLoadCommands(); + + checkIndirectSymbolTable(); + +} + + +template +void MachOChecker::checkMachHeader() +{ + if ( (fHeader->sizeofcmds() + sizeof(macho_header

)) > fLength ) + throw "sizeofcmds in mach_header is larger than file"; + + uint32_t flags = fHeader->flags(); + uint32_t invalidBits = MH_INCRLINK | MH_LAZY_INIT | 0xFFFC0000; + if ( flags & invalidBits ) + throw "invalid bits in mach_header flags"; + +} + +template +void MachOChecker::checkLoadCommands() +{ + // check that all load commands fit within the load command space file + const uint8_t* const endOfFile = (uint8_t*)fHeader + fLength; + const uint8_t* const endOfLoadCommands = (uint8_t*)fHeader + sizeof(macho_header

) + fHeader->sizeofcmds(); + const uint32_t cmd_count = fHeader->ncmds(); + const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)fHeader + sizeof(macho_header

)); + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + uint32_t size = cmd->cmdsize(); + if ( (size & this->loadCommandSizeMask()) != 0 ) + throwf("load command #%d has a unaligned size", i); + const uint8_t* endOfCmd = ((uint8_t*)cmd)+cmd->cmdsize(); + if ( endOfCmd > endOfLoadCommands ) + throwf("load command #%d extends beyond the end of the load commands", i); + if ( endOfCmd > endOfFile ) + throwf("load command #%d extends beyond the end of the file", i); + switch ( cmd->cmd() ) { + case macho_segment_command

::CMD: + case LC_SYMTAB: + case LC_UNIXTHREAD: + case LC_DYSYMTAB: + case LC_LOAD_DYLIB: + case LC_ID_DYLIB: + case LC_LOAD_DYLINKER: + case LC_ID_DYLINKER: + case macho_routines_command

::CMD: + case LC_SUB_FRAMEWORK: + case LC_SUB_UMBRELLA: + case LC_SUB_CLIENT: + case LC_TWOLEVEL_HINTS: + case LC_PREBIND_CKSUM: + case LC_LOAD_WEAK_DYLIB: + case LC_UUID: + break; + default: + throwf("load command #%d is an unknown kind 0x%X", i, cmd->cmd()); + } + cmd = (const macho_load_command

*)endOfCmd; + } + + // check segments + cmd = cmds; + std::vector > segmentAddressRanges; + std::vector > segmentFileOffsetRanges; + const macho_segment_command

* linkEditSegment = NULL; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd() == macho_segment_command

::CMD ) { + const macho_segment_command

* segCmd = (const macho_segment_command

*)cmd; + if ( segCmd->cmdsize() != (sizeof(macho_segment_command

) + segCmd->nsects() * sizeof(macho_section_content

)) ) + throw "invalid segment load command size"; + + // see if this overlaps another segment address range + uint64_t startAddr = segCmd->vmaddr(); + uint64_t endAddr = startAddr + segCmd->vmsize(); + for (typename std::vector >::iterator it = segmentAddressRanges.begin(); it != segmentAddressRanges.end(); ++it) { + if ( it->first < startAddr ) { + if ( it->second > startAddr ) + throw "overlapping segment vm addresses"; + } + else if ( it->first > startAddr ) { + if ( it->first < endAddr ) + throw "overlapping segment vm addresses"; + } + else { + throw "overlapping segment vm addresses"; + } + segmentAddressRanges.push_back(std::make_pair(startAddr, endAddr)); + } + // see if this overlaps another segment file offset range + uint64_t startOffset = segCmd->fileoff(); + uint64_t endOffset = startOffset + segCmd->filesize(); + for (typename std::vector >::iterator it = segmentFileOffsetRanges.begin(); it != segmentFileOffsetRanges.end(); ++it) { + if ( it->first < startOffset ) { + if ( it->second > startOffset ) + throw "overlapping segment file data"; + } + else if ( it->first > startOffset ) { + if ( it->first < endOffset ) + throw "overlapping segment file data"; + } + else { + throw "overlapping segment file data"; + } + segmentFileOffsetRanges.push_back(std::make_pair(startOffset, endOffset)); + // check is within file bounds + if ( (startOffset > fLength) || (endOffset > fLength) ) + throw "segment file data is past end of file"; + } + // verify it fits in file + if ( startOffset > fLength ) + throw "segment fileoff does not fit in file"; + if ( endOffset > fLength ) + throw "segment fileoff+filesize does not fit in file"; + + // keep LINKEDIT segment + if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) + linkEditSegment = segCmd; + + // check section ranges + const macho_section

* const sectionsStart = (macho_section

*)((char*)segCmd + sizeof(macho_segment_command

)); + const macho_section

* const sectionsEnd = §ionsStart[segCmd->nsects()]; + for(const macho_section

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + // check all sections are within segment + if ( sect->addr() < startAddr ) + throwf("section %s vm address not within segment", sect->sectname()); + if ( (sect->addr()+sect->size()) > endAddr ) + throwf("section %s vm address not within segment", sect->sectname()); + if ( (sect->flags() &SECTION_TYPE) != S_ZEROFILL ) { + if ( sect->offset() < startOffset ) + throwf("section %s file offset not within segment", sect->sectname()); + if ( (sect->offset()+sect->size()) > endOffset ) + throwf("section %s file offset not within segment", sect->sectname()); + } + checkSection(segCmd, sect); + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + + // verify there was a LINKEDIT segment + if ( linkEditSegment == NULL ) + throw "no __LINKEDIT segment"; + + // checks for executables + bool isStaticExecutable = false; + if ( fHeader->filetype() == MH_EXECUTE ) { + isStaticExecutable = true; + cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch ( cmd->cmd() ) { + case LC_LOAD_DYLINKER: + // the existence of a dyld load command makes a executable dynamic + isStaticExecutable = false; + break; + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + if ( isStaticExecutable ) { + if ( fHeader->flags() != MH_NOUNDEFS ) + throw "invalid bits in mach_header flags for static executable"; + } + } + + // check LC_SYMTAB and LC_DYSYMTAB + cmd = cmds; + bool foundDynamicSymTab = false; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch ( cmd->cmd() ) { + case LC_SYMTAB: + { + const macho_symtab_command

* symtab = (macho_symtab_command

*)cmd; + fSymbolCount = symtab->nsyms(); + fSymbols = (const macho_nlist

*)((char*)fHeader + symtab->symoff()); + if ( symtab->symoff() < linkEditSegment->fileoff() ) + throw "symbol table not in __LINKEDIT"; + if ( (symtab->symoff() + fSymbolCount*sizeof(macho_nlist

*)) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) + throw "symbol table end not in __LINKEDIT"; + fStrings = (char*)fHeader + symtab->stroff(); + fStringsEnd = fStrings + symtab->strsize(); + if ( symtab->stroff() < linkEditSegment->fileoff() ) + throw "string pool not in __LINKEDIT"; + if ( (symtab->stroff()+symtab->strsize()) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) + throw "string pool extends beyond __LINKEDIT"; + } + break; + case LC_DYSYMTAB: + { + if ( isStaticExecutable ) + throw "LC_DYSYMTAB should not be used in static executable"; + foundDynamicSymTab = true; + const macho_dysymtab_command

* dsymtab = (struct macho_dysymtab_command

*)cmd; + fIndirectTable = (uint32_t*)((char*)fHeader + dsymtab->indirectsymoff()); + fIndirectTableCount = dsymtab->nindirectsyms(); + if ( dsymtab->indirectsymoff() < linkEditSegment->fileoff() ) + throw "indirect symbol table not in __LINKEDIT"; + if ( (dsymtab->indirectsymoff()+fIndirectTableCount*8) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) + throw "indirect symbol table not in __LINKEDIT"; + } + break; + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + if ( !isStaticExecutable && !foundDynamicSymTab ) + throw "missing dynamic symbol table"; + if ( fStrings == NULL ) + throw "missing symbol table"; + + + +} + +template +void MachOChecker::checkSection(const macho_segment_command

* segCmd, const macho_section

* sect) +{ + uint8_t sectionType = (sect->flags() & SECTION_TYPE); + if ( sectionType == S_ZEROFILL ) { + if ( sect->offset() != 0 ) + throwf("section offset should be zero for zero-fill section %s", sect->sectname()); + } + + // more section tests here +} + +template +void MachOChecker::checkIndirectSymbolTable() +{ + const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)fHeader + sizeof(macho_header

)); + const uint32_t cmd_count = fHeader->ncmds(); + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd() == macho_segment_command

::CMD ) { + const macho_segment_command

* segCmd = (const macho_segment_command

*)cmd; + const macho_section

* const sectionsStart = (macho_section

*)((char*)segCmd + sizeof(macho_segment_command

)); + const macho_section

* const sectionsEnd = §ionsStart[segCmd->nsects()]; + for(const macho_section

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + // make sure all magic sections that use indirect symbol table fit within it + uint32_t start = 0; + uint32_t elementSize = 0; + switch ( sect->flags() & SECTION_TYPE ) { + case S_SYMBOL_STUBS: + elementSize = sect->reserved2(); + start = sect->reserved1(); + break; + case S_LAZY_SYMBOL_POINTERS: + case S_NON_LAZY_SYMBOL_POINTERS: + elementSize = sizeof(pint_t); + start = sect->reserved1(); + break; + } + if ( elementSize != 0 ) { + uint32_t count = sect->size() / elementSize; + if ( (count*elementSize) != sect->size() ) + throwf("%s section size is not an even multiple of element size", sect->sectname()); + if ( (start+count) > fIndirectTableCount ) + throwf("%s section references beyond end of indirect symbol table (%d > %d)", sect->sectname(), start+count, fIndirectTableCount ); + } + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } +} + + +static void check(const char* path) +{ + struct stat stat_buf; + + try { + int fd = ::open(path, O_RDONLY, 0); + if ( fd == -1 ) + throw "cannot open file"; + ::fstat(fd, &stat_buf); + uint32_t length = stat_buf.st_size; + uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE, fd, 0); + ::close(fd); + const mach_header* mh = (mach_header*)p; + if ( mh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + const struct fat_header* fh = (struct fat_header*)p; + const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); + for (unsigned long i=0; i < fh->nfat_arch; ++i) { + if ( archs[i].cputype == CPU_TYPE_POWERPC ) { + if ( MachOChecker::validFile(p + archs[i].offset) ) + MachOChecker::make(p + archs[i].offset, archs[i].size, path); + else + throw "in universal file, ppc slice does not contain ppc mach-o"; + } + else if ( archs[i].cputype == CPU_TYPE_I386 ) { + if ( MachOChecker::validFile(p + archs[i].offset) ) + MachOChecker::make(p + archs[i].offset, archs[i].size, path); + else + throw "in universal file, i386 slice does not contain i386 mach-o"; + } + else if ( archs[i].cputype == CPU_TYPE_POWERPC64 ) { + if ( MachOChecker::validFile(p + archs[i].offset) ) + MachOChecker::make(p + archs[i].offset, archs[i].size, path); + else + throw "in universal file, ppc64 slice does not contain ppc64 mach-o"; + } + else { + throw "in universal file, unknown architecture slice"; + } + } + } + else if ( MachOChecker::validFile(p) ) { + MachOChecker::make(p, length, path); + } + else if ( MachOChecker::validFile(p) ) { + MachOChecker::make(p, length, path); + } + else if ( MachOChecker::validFile(p) ) { + MachOChecker::make(p, length, path); + } + else { + throw "not a known file type"; + } + } + catch (const char* msg) { + throwf("%s in %s", msg, path); + } +} + + +int main(int argc, const char* argv[]) +{ + try { + for(int i=1; i < argc; ++i) { + const char* arg = argv[i]; + if ( arg[0] == '-' ) { + if ( strcmp(arg, "-no_content") == 0 ) { + + } + else { + throwf("unknown option: %s\n", arg); + } + } + else { + check(arg); + } + } + } + catch (const char* msg) { + fprintf(stderr, "machocheck failed: %s\n", msg); + return 1; + } + + return 0; +} + + + diff --git a/unit-tests/README b/unit-tests/README new file mode 100644 index 0000000..a0fd0a2 --- /dev/null +++ b/unit-tests/README @@ -0,0 +1,28 @@ + +The easy way to run all tests is within Xcode. Just select "unit-tests" as the target and click Build. + +When run from within Xcode, the just built linker will be used. If you cd into a test case and run it, the +installed linker (e.g. /usr/bin/ld) will be used. + +Each test case is a directory with a Makefile. The Makefile default target should do whatever work is necessary +to perform the test. If successful is should print "PASS xxx" where xxx is the name of the test case. Otherwise +it should print "FAIL xxx reason". If nothing is printed (for instance a tool crashed), the harness will +automatically print that it failed. The harness will always pass ARCH to the Makefile to specify which +architecture to test. The Makefile should also have a "clean" target which removes and generated files. + + +There are some utility functions for use in Makefiles for generating the PASS/FAIL strings: + + ${PASS_IFF} can be put in front of the last command in the make rule and it will print PASS + if the command returned 0 or FAIL otherwise. Example: + ${PASS_IFF} ${CC} foo.c -o foo + Will print PASS if and only if the compilation succeeded + + ${PASS_IFF_EMPTY} can have data piped into it. It prints PASS if there is no data, otherwise FAIL. + Example: + otool -hv foo.o | grep SUBSECTIONS_VIA_SYMBOLS | ${PASS_IFF_EMPTY} + Will print PASS if and only if the output of otool does not contain SUBSECTIONS_VIA_SYMBOLS + + + + diff --git a/unit-tests/bin/exit-non-zero-pass.pl b/unit-tests/bin/exit-non-zero-pass.pl new file mode 100755 index 0000000..dd4c3e5 --- /dev/null +++ b/unit-tests/bin/exit-non-zero-pass.pl @@ -0,0 +1,22 @@ +#!/usr/bin/perl -w + +# +# Usage: +# +# ${PASS_UNLESS} "test name" command +# + +use strict; + +my $string = shift @ARGV; + +if(0 == system(@ARGV)) +{ + printf("FAIL $string\n"); +} +else +{ + printf("PASS $string\n"); +} +exit 0; + diff --git a/unit-tests/bin/fail-if-exit-non-zero.pl b/unit-tests/bin/fail-if-exit-non-zero.pl new file mode 100755 index 0000000..24ca895 --- /dev/null +++ b/unit-tests/bin/fail-if-exit-non-zero.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl -w + +use strict; + +my $test_name = ""; +if ( exists $ENV{UNIT_TEST_NAME} ) { + $test_name = $ENV{UNIT_TEST_NAME}; +} + +if(system(@ARGV) != 0) +{ + printf("FAIL $test_name\n"); + exit 1; +} +else +{ + exit 0; +} + + + diff --git a/unit-tests/bin/fail-if-exit-zero.pl b/unit-tests/bin/fail-if-exit-zero.pl new file mode 100755 index 0000000..f1610c9 --- /dev/null +++ b/unit-tests/bin/fail-if-exit-zero.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl -w + +use strict; + +my $test_name = ""; +if ( exists $ENV{UNIT_TEST_NAME} ) { + $test_name = $ENV{UNIT_TEST_NAME}; +} + +if(system(@ARGV) == 0) +{ + printf("FAIL $test_name\n"); + exit 1; +} +else +{ + exit 0; +} diff --git a/unit-tests/bin/fail-if-no-stdin.pl b/unit-tests/bin/fail-if-no-stdin.pl new file mode 100755 index 0000000..0c8ae66 --- /dev/null +++ b/unit-tests/bin/fail-if-no-stdin.pl @@ -0,0 +1,20 @@ +#!/usr/bin/perl -w + +# +# Usage: +# +# command | ${FAIL_IF_EMPTY} +# + +use strict; + +my $test_name = ""; +if ( exists $ENV{UNIT_TEST_NAME} ) { + $test_name = $ENV{UNIT_TEST_NAME}; +} + +if( eof STDIN ) +{ + printf("FAIL $test_name\n"); +} +exit 0; diff --git a/unit-tests/bin/fail-if-stdin.pl b/unit-tests/bin/fail-if-stdin.pl new file mode 100755 index 0000000..f5e2fdf --- /dev/null +++ b/unit-tests/bin/fail-if-stdin.pl @@ -0,0 +1,24 @@ +#!/usr/bin/perl -w + +# +# Usage: +# +# command | ${FAIL_IF_STDIN} +# + +use strict; + +my $test_name = ""; +if ( exists $ENV{UNIT_TEST_NAME} ) { + $test_name = $ENV{UNIT_TEST_NAME}; +} + +if( eof STDIN ) +{ + +} +else +{ + printf("FAIL $test_name\n"); +} +exit 0; diff --git a/unit-tests/bin/fail-iff-exit-zero.pl b/unit-tests/bin/fail-iff-exit-zero.pl new file mode 100755 index 0000000..f62b797 --- /dev/null +++ b/unit-tests/bin/fail-iff-exit-zero.pl @@ -0,0 +1,25 @@ +#!/usr/bin/perl -w + +# +# Usage: +# +# ${FALL_IFF} command +# + +use strict; + +my $test_name = ""; +if ( exists $ENV{UNIT_TEST_NAME} ) { + $test_name = $ENV{UNIT_TEST_NAME}; +} + +if(0 == system(@ARGV)) +{ + printf("FAIL $test_name\n"); +} +else +{ + printf("PASS $test_name\n"); +} +exit 0; + diff --git a/unit-tests/bin/make-recursive.pl b/unit-tests/bin/make-recursive.pl new file mode 100755 index 0000000..a441350 --- /dev/null +++ b/unit-tests/bin/make-recursive.pl @@ -0,0 +1,120 @@ +#!/usr/bin/perl + +use strict; +use Data::Dumper; +use File::Find; +use Cwd qw(realpath); + +my @args = @ARGV; + +my $makefiles = +{ + 'makefile' => undef, + 'Makefile' => undef, +}; + +my $find_opts = +{ + 'wanted' => \&find_callback, +}; + +my $keywords = +{ + 'root' => '', + 'cwd' => '', + 'cmd' => '', + 'exit' => '', + 'stdout' => [], + 'stderr' => [], +}; + +my $keyword; +my $max_keyword_len = 0; +foreach $keyword (keys %$keywords) +{ if($max_keyword_len < length($keyword)) { $max_keyword_len = length($keyword); } } +my $delim = ':'; +$max_keyword_len += length($delim) + length(' '); + +my $last_keyword = ''; + +sub print_line +{ + my ($keyword, $val) = @_; + + if(!exists($$keywords{$keyword})) + { + print STDERR "error: keyword $keyword not in \$keywords set\n"; + exit(1); + } + + my $keyword_len = 0; + + if($keyword ne $last_keyword) + { + print("$keyword"); print($delim); + $keyword_len = length($keyword) + length($delim); + } + if($max_keyword_len > $keyword_len) + { + my $num_spaces = $max_keyword_len - $keyword_len; + print(' ' x $num_spaces); + } + print("$val"); + if(0) + { + $last_keyword = $keyword; + } +} + +my $root = '.'; +$root = &realpath($root); +print_line("root", "$root\n"); + +find($find_opts, $root); + +sub find_callback +{ + if(exists($$makefiles{$_})) + { + my $makefile = $_; + my $reldir = $File::Find::dir; + $reldir =~ s|^$root/||; + + &print_line("cwd", "\$root/$reldir\n"); + my $cmd = [ "make" ]; + + my $arg; foreach $arg (@ARGV) { push @$cmd, $arg; } # better way to do this? + &print_line("cmd", "@$cmd\n"); + + open(SAVEOUT, ">&STDOUT") || die("$!"); + open(SAVEERR, ">&STDERR") || die("$!"); + open(STDOUT, ">/tmp/unit-tests-stdout") || die("$!"); + open(STDERR, ">/tmp/unit-tests-stderr") || die("$!"); + + $ENV{UNIT_TEST_NAME} = $reldir; + my $exit = system(@$cmd); + + close(STDOUT) || die("$!"); + close(STDERR) || die("$!"); + open(STDOUT, ">&SAVEOUT") || die("$!"); + open(STDERR, ">&SAVEERR") || die("$!"); + + &print_line("exit", "$exit\n"); + + open(OUT, ") + { + &print_line("stdout", "$_"); + } + close(OUT) || die("$!"); + unlink("/tmp/unit-tests-stdout"); + + open(ERR, ") + { + &print_line("stderr", "$_"); + } + close(ERR) || die("$!"); + } + unlink("/tmp/unit-tests-stderr"); +} diff --git a/unit-tests/bin/pass-iff-exit-zero.pl b/unit-tests/bin/pass-iff-exit-zero.pl new file mode 100755 index 0000000..ff0f1ed --- /dev/null +++ b/unit-tests/bin/pass-iff-exit-zero.pl @@ -0,0 +1,25 @@ +#!/usr/bin/perl -w + +# +# Usage: +# +# ${PASS_IFF} command +# + +use strict; + +my $test_name = ""; +if ( exists $ENV{UNIT_TEST_NAME} ) { + $test_name = $ENV{UNIT_TEST_NAME}; +} + +if(0 == system(@ARGV)) +{ + printf("PASS $test_name\n"); +} +else +{ + printf("FAIL $test_name\n"); +} +exit 0; + diff --git a/unit-tests/bin/pass-iff-no-stdin.pl b/unit-tests/bin/pass-iff-no-stdin.pl new file mode 100755 index 0000000..08903f6 --- /dev/null +++ b/unit-tests/bin/pass-iff-no-stdin.pl @@ -0,0 +1,25 @@ +#!/usr/bin/perl -w + +# +# Usage: +# +# command | ${PASS_IFF_EMPTY} +# + +use strict; + +my $test_name = ""; +if ( exists $ENV{UNIT_TEST_NAME} ) { + $test_name = $ENV{UNIT_TEST_NAME}; +} + +if( eof STDIN ) +{ + printf("PASS $test_name\n"); +} +else +{ + printf("FAIL $test_name\n"); +} +exit 0; + diff --git a/unit-tests/bin/pass-iff-stdin.pl b/unit-tests/bin/pass-iff-stdin.pl new file mode 100755 index 0000000..0b56925 --- /dev/null +++ b/unit-tests/bin/pass-iff-stdin.pl @@ -0,0 +1,25 @@ +#!/usr/bin/perl -w + +# +# Usage: +# +# command | ${PASS_IFF_STDIN} +# + +use strict; + +my $test_name = ""; +if ( exists $ENV{UNIT_TEST_NAME} ) { + $test_name = $ENV{UNIT_TEST_NAME}; +} + +if( eof STDIN ) +{ + printf("FAIL $test_name\n"); +} +else +{ + printf("PASS $test_name\n"); +} +exit 0; + diff --git a/unit-tests/bin/result-filter.pl b/unit-tests/bin/result-filter.pl new file mode 100755 index 0000000..9443452 --- /dev/null +++ b/unit-tests/bin/result-filter.pl @@ -0,0 +1,127 @@ +#!/usr/bin/perl -w + +use strict; +use Data::Dumper; +use File::Find; +use Cwd; + +$Data::Dumper::Terse = 1; + +my $root = undef; +my $entry = ''; +my $pass_count = 0; +my $total_count = 0; + +# first match "root: " + +# a line starting with "cwd:" marks the beginning of a new test case +# call process_entry() on each test case +while(<>) +{ + if(m/^root:\s+(.*?)$/) + { + $root = $1; + } + elsif(m/^cwd:\s+(.*?)$/) + { + if(length($entry)) + { + &process_entry($root, $entry); + $entry = ''; + } + $entry .= $_; + } + else + { + $entry .= $_; + } +} +# don't forget last test case (no cwd: to mark end) +if(length($entry)) +{ + &process_entry($root, $entry); +} + +# show totals +my $percentage = $pass_count * 100 / $total_count; +printf " * * * %d of %d unit-tests passed (%.1f percent) * * *\n", $pass_count, $total_count, $percentage; + + +sub process_entry +{ + my ($root, $lines) = @_; + + # build an associative array of keys to value(s) + my $lines_seq = [split /\n/, $lines]; + #print Dumper($lines_seq); + my $tbl = { 'root' => $root, 'stdout' => [], 'stderr' => [] }; + my $line; + foreach $line (@$lines_seq) + { + if($line =~ m/^(\w+):\s+(.*)$/) + { + my $key = $1; + my $val = $2; + if(!exists($$tbl{$key})) + { $$tbl{$key} = ''; } + + if($key eq 'stdout' || $key eq 'stderr') # if type is @array + { + push @{$$tbl{$key}}, $val; + } + else + { + $$tbl{$key} .= $val; + } + } + else + { + print "ERROR: $line"; + } + } + #print Dumper($tbl); + #return; + + my $test_name = $$tbl{cwd}; + if ($test_name =~ m|.*/([a-zA-Z0-9-+_]+)$|) + { + $test_name = $1; + } + + #if make failed (exit was non-zero), mark this as a failure + if(0 ne $$tbl{exit}) + { + printf "%-40s FAIL Makefile failure\n", $test_name; + $total_count++; + return; + } + + #if there was any output to stderr, mark this as a failure + foreach $line (@{$$tbl{stderr}}) + { + printf "%-40s FAIL spurious stderr failure: %s\n", $test_name, $line; + $total_count++; + return; + } + + # scan all stdout looking for lines that start with PASS or FAIL + my $seen_result = 0; + foreach $line (@{$$tbl{stdout}}) + { + if($line =~ m/^(PASS|XPASS|FAIL|XFAIL).+/) + { + printf "%-40s %s\n", $test_name, $line; + $total_count++; + if($line =~ m/^PASS.+/) + { + $pass_count++; + } + $seen_result = 1; + } + } + if(!$seen_result) + { + printf "%-40s AMBIGIOUS missing [X]PASS/[X]FAIL\n", $test_name; + $total_count++; + } +} diff --git a/unit-tests/include/common.makefile b/unit-tests/include/common.makefile new file mode 100644 index 0000000..7d41e76 --- /dev/null +++ b/unit-tests/include/common.makefile @@ -0,0 +1,45 @@ +# stuff to include in every test Makefile + +SHELL = /bin/sh + +# set default to be host +ARCH ?= $(shell arch) + +# set default to be all +VALID_ARCHS ?= "ppc ppc64 i386" + +# if run within Xcode, add the just built tools to the command path +ifdef BUILT_PRODUCTS_DIR + PATH := ${BUILT_PRODUCTS_DIR}:${PATH} +endif + +LD = ld +OBJECTDUMP = ObjectDump +MACHOCHECK = machocheck + +OTOOL = otool +ifeq (${ARCH},ppc64) +OTOOL = otool64 +endif + + +CC = gcc-4.0 -arch ${ARCH} +CCFLAGS = -Wall -g -std=c99 +ASMFLAGS = + +CXX = g++-4.0 -arch ${ARCH} +CXXFLAGS = -Wall -g + +RM = rm +RMFLAGS = -rf + +# utilites for Makefiles +PASS_IFF = ${TESTROOT}/bin/pass-iff-exit-zero.pl +PASS_IFF_EMPTY = ${TESTROOT}/bin/pass-iff-no-stdin.pl +PASS_IFF_STDIN = ${TESTROOT}/bin/pass-iff-stdin.pl +PASS_IFF_GOOD_MACHO = ${TESTROOT}/bin/pass-iff-exit-zero.pl ${MACHOCHECK} +FAIL_IFF = ${TESTROOT}/bin/fail-iff-exit-zero.pl +FAIL_IF_BAD_MACHO = ${TESTROOT}/bin/fail-if-exit-non-zero.pl ${MACHOCHECK} +FAIL_IF_SUCCESS = ${TESTROOT}/bin/fail-if-exit-zero.pl +FAIL_IF_EMPTY = ${TESTROOT}/bin/fail-if-no-stdin.pl +FAIL_IF_STDIN = ${TESTROOT}/bin/fail-if-stdin.pl diff --git a/unit-tests/include/test.h b/unit-tests/include/test.h new file mode 100644 index 0000000..faca714 --- /dev/null +++ b/unit-tests/include/test.h @@ -0,0 +1,35 @@ +#include +#include + +#define DEFINE_TEST_FUNC(name) \ + static \ + inline \ + void \ + name(const char *format, ...) \ + { \ + va_list args; \ + va_start(args, format); \ + common(stdout, #name, format, args); \ + va_end(args); \ + return; \ + } + +static +inline +void +common(FILE *file, const char *prefix, const char *format, va_list args) +{ + fprintf(file, "%s \"", prefix); + vfprintf(file, format, args); + fprintf(file, "\"\n"); // should check for trailing newline + return; +} + +DEFINE_TEST_FUNC(PASS); +DEFINE_TEST_FUNC(XPASS); +DEFINE_TEST_FUNC(FAIL); +DEFINE_TEST_FUNC(XFAIL); + +DEFINE_TEST_FUNC(UNTESTED); +DEFINE_TEST_FUNC(UNSUPPORTED); +DEFINE_TEST_FUNC(UNRESOLVED); diff --git a/unit-tests/run-all-unit-tests b/unit-tests/run-all-unit-tests new file mode 100755 index 0000000..a18560a --- /dev/null +++ b/unit-tests/run-all-unit-tests @@ -0,0 +1,23 @@ +#!/bin/sh + +# cd into test-cases directory +cd `echo "$0" | sed 's/run-all-unit-tests/test-cases/'` + +all_archs="ppc ppc64 i386" + +for arch in $all_archs +do + echo "" + echo " * * * Running all unit tests for architecture $arch * * *" + + # build architecture + ../bin/make-recursive.pl ARCH=$arch VALID_ARCHS="$all_archs" | ../bin/result-filter.pl + + # clean up so svn is happy + ../bin/make-recursive.pl ARCH=$arch clean > /dev/null + + echo "" +done + + + diff --git a/unit-tests/test-cases/allow-stack-execute/Makefile b/unit-tests/test-cases/allow-stack-execute/Makefile new file mode 100644 index 0000000..9c2aaba --- /dev/null +++ b/unit-tests/test-cases/allow-stack-execute/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test the we set the stack execution bit properly. +# + +run: all + +all: + +# Test with the flag + ${CC} ${CCFLAGS} foo.c -o foo-${ARCH} -Wl,-allow_stack_execute + ${FAIL_IF_BAD_MACHO} foo-${ARCH} + ${OTOOL} -hv foo-${ARCH} | grep ALLOW_STACK_EXECUTION | ${FAIL_IF_EMPTY} + rm -f foo-${ARCH} + +# Test without the flag + ${CC} ${CCFLAGS} foo.c -o foo-${ARCH} + ${FAIL_IF_BAD_MACHO} foo-${ARCH} + ${OTOOL} -hv foo-${ARCH} | grep ALLOW_STACK_EXECUTION | ${PASS_IFF_EMPTY} + +clean: + rm -rf foo-${ARCH} diff --git a/unit-tests/test-cases/allow-stack-execute/foo.c b/unit-tests/test-cases/allow-stack-execute/foo.c new file mode 100644 index 0000000..57ed6ba --- /dev/null +++ b/unit-tests/test-cases/allow-stack-execute/foo.c @@ -0,0 +1,4 @@ +int main (void) +{ + return 0; +} diff --git a/unit-tests/test-cases/allowable-client/Makefile b/unit-tests/test-cases/allowable-client/Makefile new file mode 100644 index 0000000..88e215b --- /dev/null +++ b/unit-tests/test-cases/allowable-client/Makefile @@ -0,0 +1,90 @@ +## +# 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@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that the -allowable_client and -client options +# work when linking against subframeworks. +# + +run: all + +all: +# build with two allowable_clients + ${CC} ${CCFLAGS} -dynamiclib -umbrella foo -allowable_client bar -allowable_client baz foo.c -o foo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} foo.${ARCH}.dylib + +# test that -o works + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.${ARCH}.dylib foo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} libbar.${ARCH}.dylib + +# test that a framework style output works + mkdir -p bar.framework + ${CC} ${CCFLAGS} -dynamiclib bar.c -o bar.framework/bar foo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} bar.framework/bar + +# test that second -o works + ${CC} ${CCFLAGS} -dynamiclib baz.c -o libbaz.${ARCH}.dylib foo.${ARCH}.dylib + +# test that -o and -install_name works with install_name as an allowable + ${CC} ${CCFLAGS} -dynamiclib bar.c -o temp.${ARCH}.dylib -install_name /tmp/libbar.${ARCH}.dylib foo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} temp.${ARCH}.dylib + +# test that -o and -install_name fails with install_name different than allowable +### ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} -dynamiclib bar.c -o bar.${ARCH}.dylib -install_name /tmp/fail.${ARCH}.dylib foo.${ARCH}.dylib >& fail.log + +# test that a bundle and no client_name fails +### ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} -bundle bar.c -o temp.${ARCH}.bundle foo.${ARCH}.dylib >& fail.log + +# test that a bundle and an allowable client_name passes + ${CC} ${CCFLAGS} -bundle bar.c -client_name bar -o bar.${ARCH}.bundle foo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} bar.${ARCH}.bundle + +# test umbrella can link against subs + mkdir -p foo.framework + ${CC} ${CCFLAGS} -dynamiclib foo.${ARCH}.dylib -o foo.framework/foo + ${FAIL_IF_BAD_MACHO} foo.framework/foo + +# test sibling in umbrella can link against subs + ${CC} ${CCFLAGS} -dynamiclib main.c -umbrella foo foo.${ARCH}.dylib -o ./main.dylib + ${FAIL_IF_BAD_MACHO} main.dylib + +# test anyone can link against umbrella + ${CC} ${CCFLAGS} main.c -o main.${ARCH} -framework foo -F. + ${FAIL_IF_BAD_MACHO} main.${ARCH} + +# test that an executable and no client_name fails +### ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c -o main.${ARCH} foo.${ARCH}.dylib >& fail.log + +# test that an executable and an allowable client_name passes +# ${CC} ${CCFLAGS} main.c -o main.${ARCH} -client_name bar foo.${ARCH}.dylib +# ${PASS_IFF_GOOD_MACHO} main.${ARCH} + + +# print final pass + ${PASS_IFF_GOOD_MACHO} foo.${ARCH}.dylib + +clean: + + rm -rf *.${ARCH}.dylib *.${ARCH}.bundle main.${ARCH} fail.log foo.framework bar.framework main.dylib diff --git a/unit-tests/test-cases/allowable-client/bar.c b/unit-tests/test-cases/allowable-client/bar.c new file mode 100644 index 0000000..dbaeef8 --- /dev/null +++ b/unit-tests/test-cases/allowable-client/bar.c @@ -0,0 +1,6 @@ +extern int foo (); + +int bar (void) +{ + return foo(); +} diff --git a/unit-tests/test-cases/allowable-client/baz.c b/unit-tests/test-cases/allowable-client/baz.c new file mode 100644 index 0000000..dbaeef8 --- /dev/null +++ b/unit-tests/test-cases/allowable-client/baz.c @@ -0,0 +1,6 @@ +extern int foo (); + +int bar (void) +{ + return foo(); +} diff --git a/unit-tests/test-cases/allowable-client/foo.c b/unit-tests/test-cases/allowable-client/foo.c new file mode 100644 index 0000000..d0cdf47 --- /dev/null +++ b/unit-tests/test-cases/allowable-client/foo.c @@ -0,0 +1,4 @@ +int foo (void) +{ + return 1; +} diff --git a/unit-tests/test-cases/allowable-client/main.c b/unit-tests/test-cases/allowable-client/main.c new file mode 100644 index 0000000..7b76173 --- /dev/null +++ b/unit-tests/test-cases/allowable-client/main.c @@ -0,0 +1,6 @@ +extern int foo (); + +int main (void) +{ + return foo(); +} diff --git a/unit-tests/test-cases/archive-basic/Makefile b/unit-tests/test-cases/archive-basic/Makefile new file mode 100644 index 0000000..721cf2a --- /dev/null +++ b/unit-tests/test-cases/archive-basic/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that .o files +# can be found in archives. +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -c -o foo-${ARCH}.o + ${CC} ${CCFLAGS} bar.c -c -o bar-${ARCH}.o + libtool -static foo-${ARCH}.o bar-${ARCH}.o -o libfoobar-${ARCH}.a + ${CC} ${CCFLAGS} main.c -lfoobar-${ARCH} -L. -o main-${ARCH} + ${FAIL_IF_BAD_MACHO} main-${ARCH} + nm main-${ARCH} | grep "_bar" | ${PASS_IFF_EMPTY} + ${CC} ${CCFLAGS} main.c -all_load -lfoobar-${ARCH} -L. -o main-${ARCH} + ${FAIL_IF_BAD_MACHO} main-${ARCH} + +clean: + rm -rf main-${ARCH} foo-${ARCH}.o bar-${ARCH}.o libfoobar-${ARCH}.a + + diff --git a/unit-tests/test-cases/archive-basic/bar.c b/unit-tests/test-cases/archive-basic/bar.c new file mode 100644 index 0000000..7fe6403 --- /dev/null +++ b/unit-tests/test-cases/archive-basic/bar.c @@ -0,0 +1 @@ +int bar() { return 0; } diff --git a/unit-tests/test-cases/archive-basic/foo.c b/unit-tests/test-cases/archive-basic/foo.c new file mode 100644 index 0000000..a60f28c --- /dev/null +++ b/unit-tests/test-cases/archive-basic/foo.c @@ -0,0 +1 @@ +int foo() { return 1; } diff --git a/unit-tests/test-cases/archive-basic/main.c b/unit-tests/test-cases/archive-basic/main.c new file mode 100644 index 0000000..57c4c68 --- /dev/null +++ b/unit-tests/test-cases/archive-basic/main.c @@ -0,0 +1,32 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +extern int foo(); + +int main() +{ + fprintf(stdout, "hello\n"); + return foo(); +} \ No newline at end of file diff --git a/unit-tests/test-cases/archive-weak/Makefile b/unit-tests/test-cases/archive-weak/Makefile new file mode 100644 index 0000000..676d1a4 --- /dev/null +++ b/unit-tests/test-cases/archive-weak/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test if the linker already has a weak definition +# it does not try to find another copy in an archive +# +# There are two case to test: +# 1) both the main .o files and the archive have the same weak symbol (_foo) +# 2) main.o has a weak symbol and the archive has a non-weak symbol (_baz) +# In both cases the linker should ignore the archive. +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -c -o foo-${ARCH}.o + ${CC} ${CCFLAGS} bar.c -c -o bar-${ARCH}.o + ${CC} ${CCFLAGS} baz.c -c -o baz-${ARCH}.o + libtool -static foo-${ARCH}.o bar-${ARCH}.o baz-${ARCH}.o -o libfoobar-${ARCH}.a + ${CC} ${CCFLAGS} main.c foo.c -lfoobar-${ARCH} -L. -o main-${ARCH} + ${FAIL_IF_BAD_MACHO} main-${ARCH} + nm -m main-${ARCH} | grep _baz | grep weak | ${PASS_IFF_STDIN} + +clean: + rm -rf main-${ARCH} foo-${ARCH}.o bar-${ARCH}.o baz-${ARCH}.o libfoobar-${ARCH}.a + + diff --git a/unit-tests/test-cases/archive-weak/bar.c b/unit-tests/test-cases/archive-weak/bar.c new file mode 100644 index 0000000..7fe6403 --- /dev/null +++ b/unit-tests/test-cases/archive-weak/bar.c @@ -0,0 +1 @@ +int bar() { return 0; } diff --git a/unit-tests/test-cases/archive-weak/baz.c b/unit-tests/test-cases/archive-weak/baz.c new file mode 100644 index 0000000..387ccc1 --- /dev/null +++ b/unit-tests/test-cases/archive-weak/baz.c @@ -0,0 +1,11 @@ + + + +// intentionally not-weak +int baz() +{ + return 1; +} + + + diff --git a/unit-tests/test-cases/archive-weak/foo.c b/unit-tests/test-cases/archive-weak/foo.c new file mode 100644 index 0000000..d7003fa --- /dev/null +++ b/unit-tests/test-cases/archive-weak/foo.c @@ -0,0 +1,13 @@ + + +void collisionChecker() { } + + +int __attribute__((weak)) foo() +{ + collisionChecker(); + return 1; +} + + + diff --git a/unit-tests/test-cases/archive-weak/main.c b/unit-tests/test-cases/archive-weak/main.c new file mode 100644 index 0000000..d1ce4a9 --- /dev/null +++ b/unit-tests/test-cases/archive-weak/main.c @@ -0,0 +1,42 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +extern int foo(); +extern int bar(); + +// intentionally weak +int __attribute__((weak)) baz() +{ + return 1; +} + +int main() +{ + fprintf(stdout, "hello\n"); + return foo() + bar() + baz(); +} + + + diff --git a/unit-tests/test-cases/auto-arch/Makefile b/unit-tests/test-cases/auto-arch/Makefile new file mode 100644 index 0000000..e5caa64 --- /dev/null +++ b/unit-tests/test-cases/auto-arch/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# The point of this test is a check that ld +# can figure out which architecture is needed +# by looking at the .o files when -arch is not used. +# + +run: all + +all: + ${CC} ${CCFLAGS} hello.c -c -o hello.o -mmacosx-version-min=10.4 + ${LD} -lcrt1.o hello.o -o hello -lSystem 2> fail.log + ${FAIL_IF_BAD_MACHO} hello + file hello | grep ${ARCH} | ${PASS_IFF_STDIN} + +clean: + rm hello.o hello fail.log + + diff --git a/unit-tests/test-cases/auto-arch/hello.c b/unit-tests/test-cases/auto-arch/hello.c new file mode 100644 index 0000000..14d9363 --- /dev/null +++ b/unit-tests/test-cases/auto-arch/hello.c @@ -0,0 +1,29 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +int main() +{ + fprintf(stdout, "hello\n"); +} diff --git a/unit-tests/test-cases/blank-stubs/Makefile b/unit-tests/test-cases/blank-stubs/Makefile new file mode 100644 index 0000000..514fd8b --- /dev/null +++ b/unit-tests/test-cases/blank-stubs/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +ALL_ARCH_OPTIONS = $(patsubst %,-arch %,$(VALID_ARCHS)) + +# +# Test that blank stubs are handled properly +# + +run: all + +all: +# build example fully fat dylib + ${CC} `echo ${ALL_ARCH_OPTIONS}` -dynamiclib foo.c -o libfoo.dylib -install_name libfoo.dylib + lipo libfoo.dylib -remove ${ARCH} -output libfoo.dylib + lipo -create libfoo.dylib -arch_blank ${ARCH} -output libfoo.dylib + ${CC} ${CCFLAGS} main.c libfoo.dylib -o main + ${OTOOL} -L main | grep libfoo | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} main + + +clean: + rm -rf libfoo.dylib main + \ No newline at end of file diff --git a/unit-tests/test-cases/blank-stubs/foo.c b/unit-tests/test-cases/blank-stubs/foo.c new file mode 100644 index 0000000..d0cdf47 --- /dev/null +++ b/unit-tests/test-cases/blank-stubs/foo.c @@ -0,0 +1,4 @@ +int foo (void) +{ + return 1; +} diff --git a/unit-tests/test-cases/blank-stubs/main.c b/unit-tests/test-cases/blank-stubs/main.c new file mode 100644 index 0000000..6c9f6a4 --- /dev/null +++ b/unit-tests/test-cases/blank-stubs/main.c @@ -0,0 +1,5 @@ + +int main (void) +{ + return 0; +} diff --git a/unit-tests/test-cases/dwarf-debug-notes/Makefile b/unit-tests/test-cases/dwarf-debug-notes/Makefile new file mode 100644 index 0000000..8b94b2f --- /dev/null +++ b/unit-tests/test-cases/dwarf-debug-notes/Makefile @@ -0,0 +1,54 @@ +## +# 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@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# produces good "debug notes" stabs from dwarf .o files +# Running nm through stabs-filter.pl produces connonical stabs +# that can be diffed against a checked in know good set of stabs +# + +run: all + +all: hello.o other.o crt1.o + ${CXX} ${CCXXFLAGS} -gdwarf-2 hello.o other.o -o dwarf-hello-${ARCH} -L. + ${FAIL_IF_BAD_MACHO} dwarf-hello-${ARCH} + nm -ap dwarf-hello-${ARCH} | ./stabs-filter.pl > dwarf-hello-${ARCH}.stabs + ${PASS_IFF} diff dwarf-hello-${ARCH}.stabs expected-stabs + +hello.o : hello.cxx + ${CXX} ${CCXXFLAGS} -gdwarf-2 hello.cxx -c -o hello.o -mdynamic-no-pic + +other.o : other.cxx + ${CXX} ${CCXXFLAGS} -gdwarf-2 other.cxx -c -o other.o -mdynamic-no-pic + +# don't want any stabs in crt1.o to effect output, so my private stripped copy +crt1.o : /usr/lib/crt1.o + strip -S /usr/lib/crt1.o -o crt1.o + +clean: + rm -rf dwarf-hello-${ARCH} hello.o other.o crt1.o dwarf-hello-${ARCH}.stabs + + diff --git a/unit-tests/test-cases/dwarf-debug-notes/expected-stabs b/unit-tests/test-cases/dwarf-debug-notes/expected-stabs new file mode 100644 index 0000000..756776e --- /dev/null +++ b/unit-tests/test-cases/dwarf-debug-notes/expected-stabs @@ -0,0 +1,27 @@ +0000 SO CWD +0000 SO hello.cxx +0001 OSO CWD/hello.o +0000 BNSYM +0000 FUN _main +0000 FUN +0000 ENSYM +0000 BNSYM +0000 FUN __Z3fooi +0000 SOL header.h +0000 FUN +0000 ENSYM +0000 SO +0000 SO CWD +0000 SO other.cxx +0001 OSO CWD/other.o +0000 BNSYM +0000 FUN __Z3bari +0000 FUN +0000 ENSYM +0000 GSYM _init +0000 STSYM __ZZ3bariE8bar_init +0000 GSYM _uninit +0000 STSYM _sinit +0000 STSYM _suninit +0000 STSYM __ZZ3bariE10bar_uninit +0000 SO diff --git a/unit-tests/test-cases/dwarf-debug-notes/header.h b/unit-tests/test-cases/dwarf-debug-notes/header.h new file mode 100644 index 0000000..aa960dd --- /dev/null +++ b/unit-tests/test-cases/dwarf-debug-notes/header.h @@ -0,0 +1,8 @@ + + +inline int foo(int x) +{ + return x + 10; +} + +extern int bar(int x); \ No newline at end of file diff --git a/unit-tests/test-cases/dwarf-debug-notes/hello.cxx b/unit-tests/test-cases/dwarf-debug-notes/hello.cxx new file mode 100644 index 0000000..0d508e1 --- /dev/null +++ b/unit-tests/test-cases/dwarf-debug-notes/hello.cxx @@ -0,0 +1,33 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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@ + */ +#include + +#include "header.h" + + +int main() +{ + foo(bar(3)); + fprintf(stdout, "hello\n"); +} \ No newline at end of file diff --git a/unit-tests/test-cases/dwarf-debug-notes/other.cxx b/unit-tests/test-cases/dwarf-debug-notes/other.cxx new file mode 100644 index 0000000..dc4feb4 --- /dev/null +++ b/unit-tests/test-cases/dwarf-debug-notes/other.cxx @@ -0,0 +1,18 @@ + +#include "header.h" + +int uninit; +int init = 1; +static int suninit; +static int sinit=0; + +int bar(int x) +{ + static int bar_uninit; + static int bar_init=3; + bar_uninit = x; + return 20 + suninit + sinit + + bar_init + bar_uninit + foo(x); +} + + diff --git a/unit-tests/test-cases/dwarf-debug-notes/stabs-filter.pl b/unit-tests/test-cases/dwarf-debug-notes/stabs-filter.pl new file mode 100755 index 0000000..706fd12 --- /dev/null +++ b/unit-tests/test-cases/dwarf-debug-notes/stabs-filter.pl @@ -0,0 +1,25 @@ +#!/usr/bin/perl + +use strict; +use Cwd; + +my $dir = getcwd; +#my $xxx = $ARGV[1]; + +while(<>) +{ + # get stabs lines that match "NNNNNNN - xxx" + if(m/^([0-9a-f]+) - ([0-9a-f]+) (.*?)$/) + { + # replace any occurances of cwd path with $CWD + my $line = $3; + if($line =~ m/(.*?)$dir(.*?)$/) + { + $line = $1 . "CWD" . $2; + } + + printf "$line\n"; + } +} + + diff --git a/unit-tests/test-cases/dwarf-ignore/Makefile b/unit-tests/test-cases/dwarf-ignore/Makefile new file mode 100644 index 0000000..773e572 --- /dev/null +++ b/unit-tests/test-cases/dwarf-ignore/Makefile @@ -0,0 +1,41 @@ +## +# 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@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# strips out the dwarf segment by default +# + +run: all + +all: + ${CC} ${CCFLAGS} -gdwarf-2 hello.c -o dwarf-hello-${ARCH} + ${FAIL_IF_BAD_MACHO} dwarf-hello-${ARCH} + size -l dwarf-hello-${ARCH} | grep __DWARF | ${PASS_IFF_EMPTY} + +clean: + rm -rf dwarf-hello-${ARCH} + + diff --git a/unit-tests/test-cases/dwarf-ignore/hello.c b/unit-tests/test-cases/dwarf-ignore/hello.c new file mode 100644 index 0000000..e2f0fe9 --- /dev/null +++ b/unit-tests/test-cases/dwarf-ignore/hello.c @@ -0,0 +1,29 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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@ + */ +#include + +int main() +{ + fprintf(stdout, "hello\n"); +} \ No newline at end of file diff --git a/unit-tests/test-cases/dwarf-strip/Makefile b/unit-tests/test-cases/dwarf-strip/Makefile new file mode 100644 index 0000000..916eff1 --- /dev/null +++ b/unit-tests/test-cases/dwarf-strip/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld -S +# produces no debug notes (stabs) +# + +run: all + +all: + ${CC} ${CCFLAGS} -gdwarf-2 hello.c -Wl,-S -o dwarf-hello-${ARCH} + ${FAIL_IF_BAD_MACHO} dwarf-hello-${ARCH} + nm -ap dwarf-hello-${ARCH} | grep -e "-" | ${PASS_IFF_EMPTY} + +clean: + rm -rf dwarf-hello-${ARCH} + + diff --git a/unit-tests/test-cases/dwarf-strip/hello.c b/unit-tests/test-cases/dwarf-strip/hello.c new file mode 100644 index 0000000..ddca819 --- /dev/null +++ b/unit-tests/test-cases/dwarf-strip/hello.c @@ -0,0 +1,29 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +int main() +{ + fprintf(stdout, "hello\n"); +} \ No newline at end of file diff --git a/unit-tests/test-cases/header-pad/Makefile b/unit-tests/test-cases/header-pad/Makefile new file mode 100644 index 0000000..e4393cd --- /dev/null +++ b/unit-tests/test-cases/header-pad/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a hello-world program with no errors (or crashes) +# + +run: all + +all: + ${CC} ${CCFLAGS} hello.c -o hello-${ARCH} -Wl,-headerpad -Wl,0x3000 + ${PASS_IFF_GOOD_MACHO} hello-${ARCH} + +clean: + rm hello-${ARCH} + + diff --git a/unit-tests/test-cases/header-pad/hello.c b/unit-tests/test-cases/header-pad/hello.c new file mode 100644 index 0000000..14d9363 --- /dev/null +++ b/unit-tests/test-cases/header-pad/hello.c @@ -0,0 +1,29 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +int main() +{ + fprintf(stdout, "hello\n"); +} diff --git a/unit-tests/test-cases/hello-world/Makefile b/unit-tests/test-cases/hello-world/Makefile new file mode 100644 index 0000000..5ce56cc --- /dev/null +++ b/unit-tests/test-cases/hello-world/Makefile @@ -0,0 +1,40 @@ +## +# 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@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a hello-world program with no errors (or crashes) +# + +run: all + +all: + ${CC} ${CCFLAGS} hello.c -o hello-${ARCH} + ${PASS_IFF_GOOD_MACHO} hello-${ARCH} + +clean: + rm hello-${ARCH} + + diff --git a/unit-tests/test-cases/hello-world/hello.c b/unit-tests/test-cases/hello-world/hello.c new file mode 100644 index 0000000..e2f0fe9 --- /dev/null +++ b/unit-tests/test-cases/hello-world/hello.c @@ -0,0 +1,29 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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@ + */ +#include + +int main() +{ + fprintf(stdout, "hello\n"); +} \ No newline at end of file diff --git a/unit-tests/test-cases/literals-coalesce-alignment/Makefile b/unit-tests/test-cases/literals-coalesce-alignment/Makefile new file mode 100644 index 0000000..b27351b --- /dev/null +++ b/unit-tests/test-cases/literals-coalesce-alignment/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to verify that when two cstrings +# are coalesced that the one with greater alignment is used. +# + +run: all + +all: + ${CC} ${ASMFLAGS} cstring-align-0.s -c -o cstring-align-0-${ARCH}.o + ${CC} ${ASMFLAGS} cstring-align-3.s -c -o cstring-align-3-${ARCH}.o + ${LD} -arch ${ARCH} -r cstring-align-0-${ARCH}.o cstring-align-3-${ARCH}.o -o cstring-r-${ARCH}.o + ${OBJECTDUMP} cstring-align-3-${ARCH}.o | grep 'align:' > align-3 + ${OBJECTDUMP} cstring-r-${ARCH}.o | grep 'align:' > align-r + ${PASS_IFF} diff align-3 align-r + +clean: + rm -rf cstring-align-0-${ARCH}.o cstring-align-3-${ARCH}.o cstring-r-${ARCH}.o align-3 align-r + + + diff --git a/unit-tests/test-cases/literals-coalesce-alignment/cstring-align-0.s b/unit-tests/test-cases/literals-coalesce-alignment/cstring-align-0.s new file mode 100644 index 0000000..0dacbad --- /dev/null +++ b/unit-tests/test-cases/literals-coalesce-alignment/cstring-align-0.s @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + + .cstring +L21: .ascii "hello\0" diff --git a/unit-tests/test-cases/literals-coalesce-alignment/cstring-align-3.s b/unit-tests/test-cases/literals-coalesce-alignment/cstring-align-3.s new file mode 100644 index 0000000..d2661dc --- /dev/null +++ b/unit-tests/test-cases/literals-coalesce-alignment/cstring-align-3.s @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + .cstring + .align 3 +L21: .ascii "hello\0" diff --git a/unit-tests/test-cases/literals-coalesce/Makefile b/unit-tests/test-cases/literals-coalesce/Makefile new file mode 100644 index 0000000..80465f8 --- /dev/null +++ b/unit-tests/test-cases/literals-coalesce/Makefile @@ -0,0 +1,43 @@ +## +# 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@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to verify that literals are uniqued. +# After running ld -r all duplicates should be removed. +# + +run: all + +all: + ${CC} ${ASMFLAGS} literals.s -c -o literals-${ARCH}.o + ${OBJECTDUMP} literals-${ARCH}.o | grep 'name:'| uniq -c | grep -v '^ [1|2]' | ${FAIL_IF_STDIN} + ${LD} -arch ${ARCH} -r literals-${ARCH}.o -o literals-r-${ARCH}.o + ${OBJECTDUMP} literals-r-${ARCH}.o | grep 'name:' | uniq -d | ${PASS_IFF_EMPTY} + +clean: + rm -rf literals-${ARCH}.o -o literals-r-${ARCH}.o + + + diff --git a/unit-tests/test-cases/literals-coalesce/literals.s b/unit-tests/test-cases/literals-coalesce/literals.s new file mode 100644 index 0000000..b8d4354 --- /dev/null +++ b/unit-tests/test-cases/literals-coalesce/literals.s @@ -0,0 +1,48 @@ +/* + * 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@ + */ + + .literal8 + +L1: .long 12345678 + .long 87654321 + +L2: .long 12345678 + .long 87654322 + +L3: .long 22345678 + .long 87654321 + +L4: .long 12345678 + .long 87654321 + + .literal4 +L11:.long 12345678 +L12:.long 12345679 +L13:.long 22345678 +L14:.long 12345678 + + .cstring +L21: .ascii "hello\0" +L22: .ascii "hello,there\0" +L23: .ascii "there\0" +L24: .ascii "hello\0" diff --git a/unit-tests/test-cases/multiple-entry-points/Makefile b/unit-tests/test-cases/multiple-entry-points/Makefile new file mode 100644 index 0000000..0a8dee0 --- /dev/null +++ b/unit-tests/test-cases/multiple-entry-points/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to verify a .o file can round-trip +# through ld -r correctly. The ObjectDump utility is used +# dump a "canonical" textual representation of a .o file. +# The before and after .o files are then diff'ed. +# No differences means this test passes +# + +run: all + +all: + ${CC} ${ASMFLAGS} test.s -c -o test.${ARCH}.o + ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o + ${OBJECTDUMP} -no_sort test.${ARCH}.o > test.${ARCH}.o.dump + ${OBJECTDUMP} -no_sort test-r.${ARCH}.o > test-r.${ARCH}.o.dump + ${PASS_IFF} diff test.${ARCH}.o.dump test-r.${ARCH}.o.dump + +clean: + rm -rf test.${ARCH}.o test-r.${ARCH}.o test.${ARCH}.o.dump test-r.${ARCH}.o.dump + + + diff --git a/unit-tests/test-cases/multiple-entry-points/test.s b/unit-tests/test-cases/multiple-entry-points/test.s new file mode 100644 index 0000000..dc3b340 --- /dev/null +++ b/unit-tests/test-cases/multiple-entry-points/test.s @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + + .text + .align 2 + + .globl _foo + .globl _foo2 + .globl _foo3 +_foo: +_foo2: +_foo3: + nop + + + +_bar: + nop + + + .globl _xx + .globl __xx +_xx: +__xx: + nop + + +_ok: + nop + diff --git a/unit-tests/test-cases/no-uuid/Makefile b/unit-tests/test-cases/no-uuid/Makefile new file mode 100644 index 0000000..223c8b8 --- /dev/null +++ b/unit-tests/test-cases/no-uuid/Makefile @@ -0,0 +1,61 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test the we set emit LC_UUID correctly +# + +run: all + +all: + +# Test with dwarf-2 + ${CC} ${CCFLAGS} foo.c -o foo-${ARCH} -gdwarf-2 + ${FAIL_IF_BAD_MACHO} foo-${ARCH} + ${OTOOL} -hlv foo-${ARCH} | grep LC_UUID | ${FAIL_IF_EMPTY} + rm -f foo-${ARCH} + +# Test with stabs + ${CC} ${CCFLAGS} foo.c -o foo-${ARCH} -gfull + ${FAIL_IF_BAD_MACHO} foo-${ARCH} + ${OTOOL} -hlv foo-${ARCH} | grep LC_UUID | ${FAIL_IF_STDIN} + +# Test with two files one with UUID + ${CC} ${CCFLAGS} bar.c -c -gdwarf-2 + ${LD} -arch ${ARCH} bar.o -r -o bar-${ARCH}.o -no_uuid + ${CC} ${CCFLAGS} foo.c -c -gdwarf-2 + ${LD} -arch ${ARCH} foo.o -r -o foo-${ARCH}.o + ${CC} ${CCFLAGS} foo-${ARCH}.o bar-${ARCH}.o -o temp-${ARCH} + ${FAIL_IF_BAD_MACHO} temp-${ARCH} + ${OTOOL} -hlv temp-${ARCH} | grep LC_UUID | ${FAIL_IF_EMPTY} + rm -f foo.o bar.o foo-${ARCH}.o bar-${ARCH}.o temp-${ARCH} + +# Test with with the flag disabling uuid + ${CC} ${CCFLAGS} foo.c -o foo-${ARCH} -Wl,-no_uuid -gdwarf-2 + ${FAIL_IF_BAD_MACHO} foo-${ARCH} + ${OTOOL} -hlv foo-${ARCH} | grep LC_UUID | ${PASS_IFF_EMPTY} + +clean: + rm -rf foo-${ARCH} bar-${ARCH}.dylib temp-${ARCH}.dylib diff --git a/unit-tests/test-cases/no-uuid/bar.c b/unit-tests/test-cases/no-uuid/bar.c new file mode 100644 index 0000000..cbefe0f --- /dev/null +++ b/unit-tests/test-cases/no-uuid/bar.c @@ -0,0 +1,4 @@ +int bar (void) +{ + return 1; +} diff --git a/unit-tests/test-cases/no-uuid/foo.c b/unit-tests/test-cases/no-uuid/foo.c new file mode 100644 index 0000000..57ed6ba --- /dev/null +++ b/unit-tests/test-cases/no-uuid/foo.c @@ -0,0 +1,4 @@ +int main (void) +{ + return 0; +} diff --git a/unit-tests/test-cases/private-non-lazy/Makefile b/unit-tests/test-cases/private-non-lazy/Makefile new file mode 100644 index 0000000..655631c --- /dev/null +++ b/unit-tests/test-cases/private-non-lazy/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to check that a non-lazy-pointer +# in foo.o to a private-extern symbol in bar.o will +# properly survive ld -r +# + +run: all + +all: + ${CC} ${CCFLAGS} -c foo.c -o foo.o + ${CC} ${CCFLAGS} -c bar.c -o bar.o + ${LD} -r foo.o bar.o -o foobar.o -arch ${ARCH} + ${CC} ${CCFLAGS} hello.c foobar.o -o hello + ${FAIL_IF_BAD_MACHO} hello + ${LD} -r foo.o bar.o -o foobar2.o -arch ${ARCH} -keep_private_externs + ${CC} ${CCFLAGS} hello.c foobar2.o -o hello2 + ${PASS_IFF_GOOD_MACHO} hello2 + +clean: + rm foo.o bar.o foobar.o hello foobar2.o hello2 + + diff --git a/unit-tests/test-cases/private-non-lazy/bar.c b/unit-tests/test-cases/private-non-lazy/bar.c new file mode 100644 index 0000000..601dc69 --- /dev/null +++ b/unit-tests/test-cases/private-non-lazy/bar.c @@ -0,0 +1,3 @@ + +int __attribute__((visibility("hidden"))) foo = 0; + diff --git a/unit-tests/test-cases/private-non-lazy/foo.c b/unit-tests/test-cases/private-non-lazy/foo.c new file mode 100644 index 0000000..6816d0b --- /dev/null +++ b/unit-tests/test-cases/private-non-lazy/foo.c @@ -0,0 +1,7 @@ + + +extern int foo; + +int getfoo() { return foo; } + + diff --git a/unit-tests/test-cases/private-non-lazy/hello.c b/unit-tests/test-cases/private-non-lazy/hello.c new file mode 100644 index 0000000..20dccc4 --- /dev/null +++ b/unit-tests/test-cases/private-non-lazy/hello.c @@ -0,0 +1,31 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +extern int getfoo(); + +int main() +{ + return getfoo(); +} diff --git a/unit-tests/test-cases/read-only-relocs/Makefile b/unit-tests/test-cases/read-only-relocs/Makefile new file mode 100644 index 0000000..6cea9e2 --- /dev/null +++ b/unit-tests/test-cases/read-only-relocs/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# will fail to link a dylib compiled with -mdynamic-no-pic +# + + +SHELL = bash # use bash shell so we can redirect just stderr + +run: all + +all: + ${FAIL_IFF} ${CC} ${CCFLAGS} test.c -mdynamic-no-pic -dynamiclib -o test-${ARCH}.dylib 2> fail.log + +clean: + rm test-${ARCH}.dylib fail.log + + diff --git a/unit-tests/test-cases/read-only-relocs/test.c b/unit-tests/test-cases/read-only-relocs/test.c new file mode 100644 index 0000000..f5be7e3 --- /dev/null +++ b/unit-tests/test-cases/read-only-relocs/test.c @@ -0,0 +1,30 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +int a=0; + +int foo(void) +{ + return a; +} diff --git a/unit-tests/test-cases/relocs-asm/Makefile b/unit-tests/test-cases/relocs-asm/Makefile new file mode 100644 index 0000000..6c0eb7b --- /dev/null +++ b/unit-tests/test-cases/relocs-asm/Makefile @@ -0,0 +1,47 @@ +## +# 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@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to verify a .o file can round-trip +# through ld -r correctly. The ObjectDump utility is used +# dump a "canonical" textual representation of a .o file. +# The before and after .o files are then diff'ed. +# No differences means this test passes +# + +run: all + +all: + ${CC} ${ASMFLAGS} relocs-asm.s -c -o relocs-asm.${ARCH}.o + ${LD} -arch ${ARCH} -r -keep_private_externs relocs-asm.${ARCH}.o -o relocs-asm-r.${ARCH}.o + ${OBJECTDUMP} -no_content relocs-asm.${ARCH}.o > relocs-asm.${ARCH}.o.dump + ${OBJECTDUMP} -no_content relocs-asm-r.${ARCH}.o > relocs-asm-r.${ARCH}.o.dump + ${PASS_IFF} diff relocs-asm.${ARCH}.o.dump relocs-asm-r.${ARCH}.o.dump + +clean: + rm -rf relocs-asm.${ARCH}.o relocs-asm-r.${ARCH}.o relocs-asm.${ARCH}.o.dump relocs-asm-r.${ARCH}.o.dump + + + diff --git a/unit-tests/test-cases/relocs-asm/relocs-asm.s b/unit-tests/test-cases/relocs-asm/relocs-asm.s new file mode 100644 index 0000000..5a40e49 --- /dev/null +++ b/unit-tests/test-cases/relocs-asm/relocs-asm.s @@ -0,0 +1,235 @@ +/* + * 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@ + */ + +#if __ppc__ || __ppc64__ + + .text + .align 2 + + .globl _test_loads +_test_loads: + stmw r30,-8(r1) + stwu r1,-48(r1) +Lpicbase: + + ; PIC load of a + addis r2,r10,ha16(_a-Lpicbase) + lwz r2,lo16(_a-Lpicbase)(r2) + + ; PIC load of c + addis r2,r10,ha16(_c-Lpicbase) + lwz r2,lo16(_c-Lpicbase)(r2) + + ; absolute load of a + lis r2,ha16(_a) + lwz r2,lo16(_a)(r2) + + ; absolute load of c + lis r2,ha16(_c) + lwz r2,lo16(_c)(r2) + + ; absolute load of external + lis r2,ha16(_ax) + lwz r2,lo16(_ax)(r2) + + ; absolute lea of external + lis r2,hi16(_ax) + ori r2,r2,lo16(_ax) + + + ; PIC load of a + addend + addis r2,r10,ha16(_a+0x19000-Lpicbase) + lwz r2,lo16(_a+0x19000-Lpicbase)(r2) + + ; absolute load of a + addend + lis r2,ha16(_a+0x19000) + lwz r2,lo16(_a+0x19000)(r2) + + ; absolute load of external + addend + lis r2,ha16(_ax+0x19000) + lwz r2,lo16(_ax+0x19000)(r2) + + ; absolute lea of external + addend + lis r2,hi16(_ax+0x19000) + ori r2,r2,lo16(_ax+0x19000) + + + ; PIC load of a + addend + addis r2,r10,ha16(_a+0x09000-Lpicbase) + lwz r2,lo16(_a+0x09000-Lpicbase)(r2) + + ; absolute load of a + addend + lis r2,ha16(_a+0x09000) + lwz r2,lo16(_a+0x09000)(r2) + + ; absolute load of external + addend + lis r2,ha16(_ax+0x09000) + lwz r2,lo16(_ax+0x09000)(r2) + + ; absolute lea of external + addend + lis r2,hi16(_ax+0x09000) + ori r2,r2,lo16(_ax+0x09000) + + blr + + +_test_calls: + ; call internal + bl _test_branches + + ; call internal + addend + bl _test_branches+0x19000 + + ; call external + bl _external + + ; call external + addend + bl _external+0x19000 + + +_test_branches: + ; call internal + bne _test_calls + + ; call internal + addend + bne _test_calls+16 + + ; call external + bne _external + + ; call external + addend + bne _external+16 +#endif + + + +#if __i386__ + .text + .align 2 + + .globl _test_loads +_test_loads: + pushl %ebp +Lpicbase: + + # PIC load of a + movl _a-Lpicbase(%ebx), %eax + + # absolute load of a + movl _a, %eax + + # absolute load of external + movl _ax, %eax + + # absolute lea of external + leal _ax, %eax + + + # PIC load of a + addend + movl _a-Lpicbase+0x19000(%ebx), %eax + + # absolute load of a + addend + movl _a+0x19000(%ebx), %eax + + # absolute load of external + addend + movl _ax+0x19000(%ebx), %eax + + # absolute lea of external + addend + leal _ax+0x1900, %eax + + ret + + +_test_calls: + # call internal + call _test_branches + + # call internal + addend + call _test_branches+0x19000 + + # call external + call _external + + # call external + addend + call _external+0x19000 + + +_test_branches: + # call internal + jne _test_calls + + # call internal + addend + jne _test_calls+16 + + # call external + jne _external + + # call external + addend + jne _external+16 +#endif + + + + # test that pointer-diff relocs are preserved + .text +_test_diffs: + .align 2 +Llocal2: + .long 0 + .long Llocal2-_test_branches +#if __ppc64__ + .quad Llocal2-_test_branches +#endif + + + .data +_a: + .long 0 + +_b: +#if __ppc__ || __i386__ + .long _test_calls + .long _test_calls+16 + .long _external + .long _external+16 +#elif __ppc64__ + .quad _test_calls + .quad _test_calls+16 + .quad _external + .quad _external+16 +#endif + + # test that reloc sizes are the same +Llocal3: + .long 0 + +Llocal4: + .long 0 + + .long Llocal4-Llocal3 + +Lfiller: + .space 0x9000 +_c: + .long 0 + diff --git a/unit-tests/test-cases/relocs-c/Makefile b/unit-tests/test-cases/relocs-c/Makefile new file mode 100644 index 0000000..b400705 --- /dev/null +++ b/unit-tests/test-cases/relocs-c/Makefile @@ -0,0 +1,49 @@ +## +# Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# The point of this test is to verify a .o file can round-trip +# through ld -r correctly. The ObjectDump utility is used +# dump a "canonical" textual representation of a .o file. +# The before and after .o files are then diff'ed. +# No differences means this test passes +# + +run: all + +all: + ${CC} ${CCFLAGS} test.c -c -o test.${ARCH}.o + ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o + ${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump + grep "plus" test.${ARCH}.o.dump | ${FAIL_IF_STDIN} + ${OBJECTDUMP} -no_content test-r.${ARCH}.o > test-r.${ARCH}.o.dump + grep "plus" test-r.${ARCH}.o.dump | ${FAIL_IF_STDIN} + ${PASS_IFF} diff test.${ARCH}.o.dump test-r.${ARCH}.o.dump + +clean: + rm -rf test.${ARCH}.o test-r.${ARCH}.o test.${ARCH}.o.dump test-r.${ARCH}.o.dump + + diff --git a/unit-tests/test-cases/relocs-c/test.c b/unit-tests/test-cases/relocs-c/test.c new file mode 100644 index 0000000..b877760 --- /dev/null +++ b/unit-tests/test-cases/relocs-c/test.c @@ -0,0 +1,76 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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@ + */ + +static int foo; + +int __attribute__((visibility("hidden"))) foofoo; + +static int uninit_static; +static int init_static = 1; + int __attribute__((visibility("hidden"))) uninit_hidden; + int __attribute__((visibility("hidden"))) init_hidden = 1; + int uninit_global; + int init_global = 1; +extern int extern_global; +extern int __attribute__((visibility("hidden"))) extern_hidden; + +static int uninit_static_array[4]; +static int init_static_array[4] = {1,2,3,4}; + int __attribute__((visibility("hidden"))) uninit_hidden_array[4]; + int __attribute__((visibility("hidden"))) init_hidden_array[4] = {1,2,3,4}; + int uninit_global_array[4]; + int init_global_array[4] = {1,2,3,4}; +extern int extern_global_array[4]; + +int test1() { return uninit_static; } +int test2() { return init_static; } +int test3() { return uninit_hidden; } +int test4() { return init_hidden; } +int test5() { return uninit_global; } +int test6() { return init_global; } +int test7() { return extern_global; } +int test8() { return extern_hidden; } + +int test_array1() { return uninit_static_array[2]; } +int test_array2() { return init_static_array[2]; } +int test_array3() { return uninit_hidden_array[2]; } +int test_array4() { return init_hidden_array[2]; } +int test_array5() { return uninit_global_array[2]; } +int test_array6() { return init_global_array[2]; } +int test_array7() { return extern_global_array[2]; } + +static int foo2; +int test9() { return foo2; } + + +int* p_init_global = &init_global; +void* p_test1 = (void*)&test1; +unsigned char pad = 2; +unsigned char pad2 = 3; // this padding throws off alignment on compiler generated anonymous non-lazy pointers... + +int func() __attribute__((visibility("hidden"))); +int func() { return foo; } + +int func2() { return func() + 1; } + diff --git a/unit-tests/test-cases/relocs-literals/Makefile b/unit-tests/test-cases/relocs-literals/Makefile new file mode 100644 index 0000000..6d45efd --- /dev/null +++ b/unit-tests/test-cases/relocs-literals/Makefile @@ -0,0 +1,47 @@ +## +# 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@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# The point of this test is to verify a .o file can round-trip +# through ld -r correctly. The ObjectDump utility is used +# dump a "canonical" textual representation of a .o file. +# The before and after .o files are then diff'ed. +# No differences means this test passes +# + +run: all + +all: + ${CC} ${CCFLAGS} -Os -mdynamic-no-pic test.c -c -o test.${ARCH}.o + ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o + ${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump + ${OBJECTDUMP} -no_content test-r.${ARCH}.o > test-r.${ARCH}.o.dump + ${PASS_IFF} diff test.${ARCH}.o.dump test-r.${ARCH}.o.dump + +clean: + rm -rf test.${ARCH}.o test-r.${ARCH}.o test.${ARCH}.o.dump test-r.${ARCH}.o.dump + + diff --git a/unit-tests/test-cases/relocs-literals/test.c b/unit-tests/test-cases/relocs-literals/test.c new file mode 100644 index 0000000..31e87c2 --- /dev/null +++ b/unit-tests/test-cases/relocs-literals/test.c @@ -0,0 +1,47 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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@ + */ + +const char* foo = "foo"; +const char* const bar = "bar"; + +const char charArray1[] = "charArray1"; +static const char charArray2[] = "charArray2"; + + +const char* getString() { return "string"; } +const char* getString2() { return charArray2; } +const char* getString3() { return charArray1; } +const char* getString4() { return foo; } + + +float f1 = 3.0; +double d1 = 3.0; +long double ld1 = 3.0; + + + +float getSingle() { return 1.0; } +double getDouble() { return 2.0; } +long double getLongDouble() { return 3.0; } + diff --git a/unit-tests/test-cases/relocs-objc/Makefile b/unit-tests/test-cases/relocs-objc/Makefile new file mode 100644 index 0000000..9b55f25 --- /dev/null +++ b/unit-tests/test-cases/relocs-objc/Makefile @@ -0,0 +1,47 @@ +## +# 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@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# The point of this test is to verify a .o file can round-trip +# through ld -r correctly. The ObjectDump utility is used +# dump a "canonical" textual representation of a .o file. +# The before and after .o files are then diff'ed. +# No differences means this test passes +# + +run: all + +all: + ${CC} ${CCFLAGS} test.m -c -o test.${ARCH}.o + ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o + ${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump + ${OBJECTDUMP} -no_content test-r.${ARCH}.o > test-r.${ARCH}.o.dump + ${PASS_IFF} diff test.${ARCH}.o.dump test-r.${ARCH}.o.dump + +clean: + rm -rf test.${ARCH}.o test-r.${ARCH}.o test.${ARCH}.o.dump test-r.${ARCH}.o.dump + + diff --git a/unit-tests/test-cases/relocs-objc/test.m b/unit-tests/test-cases/relocs-objc/test.m new file mode 100644 index 0000000..a691e4a --- /dev/null +++ b/unit-tests/test-cases/relocs-objc/test.m @@ -0,0 +1,50 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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@ + */ +#include + + +@interface Foo : NSObject +{ + int ivar; +} +- (id) init; +- (void) foo; +@end + + +@implementation Foo + +- (id) init +{ + self = [super init]; + return self; +} + +- (void) foo +{ + [self class]; +} + +@end + diff --git a/unit-tests/test-cases/stabs-coalesce/Makefile b/unit-tests/test-cases/stabs-coalesce/Makefile new file mode 100644 index 0000000..c3414e0 --- /dev/null +++ b/unit-tests/test-cases/stabs-coalesce/Makefile @@ -0,0 +1,52 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# removes the stabs associated with a copy of a coalesced +# function that was removed. +# Running nm through stabs-filter.pl produces connonical stabs +# that can be diffed against a checked in know good set of stabs +# + +run: all + +all: hello.o other.o + ${CXX} ${CCXXFLAGS} -gused hello.o other.o -o stabs-hello-${ARCH} + ${FAIL_IF_BAD_MACHO} stabs-hello-${ARCH} + nm -ap stabs-hello-${ARCH} | grep FUN | grep _Z3fooi | wc -l > stabs-hello-foo-count + echo " 1" > one + ${PASS_IFF} diff stabs-hello-foo-count one + +hello.o : hello.cxx + ${CXX} ${CCXXFLAGS} -gused hello.cxx -c -o hello.o + +other.o : other.cxx + ${CXX} ${CCXXFLAGS} -gused other.cxx -c -o other.o + +clean: + rm -rf stabs-hello-${ARCH} hello.o other.o stabs-hello-${ARCH}.stabs stabs-hello-foo-count one + + diff --git a/unit-tests/test-cases/stabs-coalesce/header.h b/unit-tests/test-cases/stabs-coalesce/header.h new file mode 100644 index 0000000..378308f --- /dev/null +++ b/unit-tests/test-cases/stabs-coalesce/header.h @@ -0,0 +1,31 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +inline int foo(int x) +{ + return x + 10; +} + +extern int bar(int x); diff --git a/unit-tests/test-cases/stabs-coalesce/hello.cxx b/unit-tests/test-cases/stabs-coalesce/hello.cxx new file mode 100644 index 0000000..33bf273 --- /dev/null +++ b/unit-tests/test-cases/stabs-coalesce/hello.cxx @@ -0,0 +1,33 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +#include "header.h" + + +int main() +{ + foo(bar(3)); + fprintf(stdout, "hello\n"); +} \ No newline at end of file diff --git a/unit-tests/test-cases/stabs-coalesce/other.cxx b/unit-tests/test-cases/stabs-coalesce/other.cxx new file mode 100644 index 0000000..ee97d7d --- /dev/null +++ b/unit-tests/test-cases/stabs-coalesce/other.cxx @@ -0,0 +1,41 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include "header.h" + +int uninit; +int init = 1; +static int suninit; +static int sinit=0; + +int bar(int x) +{ + static int bar_uninit; + static int bar_init=3; + bar_uninit = x; + return 20 + suninit + sinit + + bar_init + bar_uninit + foo(x); +} + + diff --git a/unit-tests/test-cases/static-executable/Makefile b/unit-tests/test-cases/static-executable/Makefile new file mode 100644 index 0000000..cee5f44 --- /dev/null +++ b/unit-tests/test-cases/static-executable/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a static executable (requires non-public archives) +# + +run: all + +all: + ${CC} ${CCFLAGS} test.c -static -o test-${ARCH} -L/usr/local/lib/system -lc_static -lm_static + ${PASS_IFF_GOOD_MACHO} test-${ARCH} + +clean: + rm test-${ARCH} + + diff --git a/unit-tests/test-cases/static-executable/test.c b/unit-tests/test-cases/static-executable/test.c new file mode 100644 index 0000000..ab472fb --- /dev/null +++ b/unit-tests/test-cases/static-executable/test.c @@ -0,0 +1,28 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +int main() +{ + return 0; +} diff --git a/unit-tests/test-cases/weak_import/Makefile b/unit-tests/test-cases/weak_import/Makefile new file mode 100644 index 0000000..69ff397 --- /dev/null +++ b/unit-tests/test-cases/weak_import/Makefile @@ -0,0 +1,56 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test the weak_import attribute works +# + + +run: all + +all: + ${CC} ${CCFLAGS} -dynamiclib -single_module foo.c -o libfoo-${ARCH}.dylib + ${CC} ${CCFLAGS} -mmacosx-version-min=10.4 main.c -o main-${ARCH} libfoo-${ARCH}.dylib + nm -m main-${ARCH} | grep _func1 | grep -v weak >/dev/null + nm -m main-${ARCH} | grep _func2 | grep weak >/dev/null + nm -m main-${ARCH} | grep _func3 | grep -v weak >/dev/null + nm -m main-${ARCH} | grep _func4 | grep weak >/dev/null + nm -m main-${ARCH} | grep _data1 | grep -v weak >/dev/null + nm -m main-${ARCH} | grep _data2 | grep weak >/dev/null + nm -m main-${ARCH} | grep _data3 | grep -v weak >/dev/null + nm -m main-${ARCH} | grep _data4 | grep weak >/dev/null + ${CC} ${CCFLAGS} -mmacosx-version-min=10.4 main.c -dynamiclib -o main-${ARCH}.dylib libfoo-${ARCH}.dylib + nm -m main-${ARCH}.dylib | grep _func1 | grep -v weak >/dev/null + nm -m main-${ARCH}.dylib | grep _func2 | grep weak >/dev/null + nm -m main-${ARCH}.dylib | grep _func3 | grep -v weak >/dev/null + nm -m main-${ARCH}.dylib | grep _func4 | grep weak >/dev/null + nm -m main-${ARCH}.dylib | grep _data1 | grep -v weak >/dev/null + nm -m main-${ARCH}.dylib | grep _data2 | grep weak >/dev/null + nm -m main-${ARCH}.dylib | grep _data3 | grep -v weak >/dev/null + nm -m main-${ARCH}.dylib | grep _data4 | grep weak >/dev/null + ${PASS_IFF_GOOD_MACHO} main-${ARCH}.dylib + +clean: + rm -rf libfoo-${ARCH}.dylib main-${ARCH} main-${ARCH}.dylib diff --git a/unit-tests/test-cases/weak_import/foo.c b/unit-tests/test-cases/weak_import/foo.c new file mode 100644 index 0000000..900b052 --- /dev/null +++ b/unit-tests/test-cases/weak_import/foo.c @@ -0,0 +1,17 @@ + + +#include "foo.h" + +void func1() {} +void func2() {} +void func3() {} +void func4() {} + + +int data1 = 0; +int data2 = 0; // weak_import initialized +int data3; +int data4; // weak_import uninitialized +int data5 = 0; +int data6 = 0; // weak_import + diff --git a/unit-tests/test-cases/weak_import/foo.h b/unit-tests/test-cases/weak_import/foo.h new file mode 100644 index 0000000..f455515 --- /dev/null +++ b/unit-tests/test-cases/weak_import/foo.h @@ -0,0 +1,16 @@ + + +extern void func1(); +extern void func2() __attribute__((weak_import)); +extern void func3(); +extern void func4() __attribute__((weak_import)); + +extern int data1; +extern int data2 __attribute__((weak_import)); +extern int data3; +extern int data4 __attribute__((weak_import)); +extern int data5; +extern int data6 __attribute__((weak_import)); + + + diff --git a/unit-tests/test-cases/weak_import/main.c b/unit-tests/test-cases/weak_import/main.c new file mode 100644 index 0000000..3266aed --- /dev/null +++ b/unit-tests/test-cases/weak_import/main.c @@ -0,0 +1,20 @@ + +#include "foo.h" + + +int* pdata5 = &data5; +int* pdata6 = &data6; + + +int main (void) +{ + // make non-lazy reference to func3 and func4 + if ( &func3 == &func4 ) { + // make lazy reference to func3 and func4 + func1(); + func2(); + } + + return data1 + data2 + data3 + data4; +} + diff --git a/unit-tests/test-cases/zero-fill/Makefile b/unit-tests/test-cases/zero-fill/Makefile new file mode 100644 index 0000000..a5f2f12 --- /dev/null +++ b/unit-tests/test-cases/zero-fill/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a program with a large zero-fill section +# + +run: all + +all: + ${CC} ${CCFLAGS} test.c -o test-${ARCH} + ${PASS_IFF_GOOD_MACHO} test-${ARCH} + +clean: + rm test-${ARCH} + + diff --git a/unit-tests/test-cases/zero-fill/test.c b/unit-tests/test-cases/zero-fill/test.c new file mode 100644 index 0000000..cfdc08c --- /dev/null +++ b/unit-tests/test-cases/zero-fill/test.c @@ -0,0 +1,51 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +// if we used one big array, the linker would page align it +// but we want to test a non-page align big chunk of zero-fill data +int bigarray1[256]; +int bigarray2[256]; +int bigarray3[256]; +int bigarray4[256]; +int bigarray5[256]; +int bigarray6[256]; +static int staticbigarray1[256]; +static int staticbigarray2[256]; +static int staticbigarray3[256]; +static int staticbigarray4[256]; +static int staticbigarray5[256]; +static int staticbigarray6[256]; + +int main() +{ + staticbigarray1[10] = 4; + staticbigarray2[10] = 4; + staticbigarray3[10] = 4; + staticbigarray4[10] = 4; + staticbigarray5[10] = 4; + staticbigarray6[10] = 4; + return 0; +} +