From 69a49097222b02da1734b2c7a46b6ebb29f32fbc Mon Sep 17 00:00:00 2001 From: Apple Date: Wed, 6 Sep 2006 20:50:50 +0000 Subject: [PATCH] ld64-59.2.tar.gz --- ChangeLog | 794 ++++++--------- doc/man/man1/rebase.1 | 39 + ld64.xcodeproj/project.pbxproj | 236 ++++- src/Architectures.hpp | 12 +- src/MachOFileAbstraction.hpp | 9 +- src/MachOReaderArchive.hpp | 3 +- src/MachOReaderDylib.hpp | 82 +- src/MachOReaderRelocatable.hpp | 721 +++++++++++--- src/MachOWriterExecutable.hpp | 811 ++++++++++++++-- src/ObjectDump.cpp | 13 +- src/ObjectFile.h | 15 +- src/Options.cpp | 268 +++++- src/Options.h | 13 +- src/SectCreate.cpp | 3 +- src/ld.cpp | 577 +++++++---- src/machochecker.cpp | 248 ++++- src/rebase.cpp | 907 ++++++++++++++++++ unit-tests/include/common.makefile | 7 +- unit-tests/run-all-unit-tests | 2 +- unit-tests/test-cases/bundle_loader/Makefile | 43 + unit-tests/test-cases/bundle_loader/bar.c | 31 + unit-tests/test-cases/bundle_loader/bundle.c | 31 + unit-tests/test-cases/bundle_loader/main.c | 30 + unit-tests/test-cases/dead_strip/Makefile | 52 + unit-tests/test-cases/dead_strip/deadwood.c | 11 + unit-tests/test-cases/dead_strip/main.c | 32 + unit-tests/test-cases/dead_strip/main.exp | 1 + .../test-cases/dwarf-debug-notes-r/Makefile | 61 ++ .../test-cases/dwarf-debug-notes-r/bar.cxx | 4 + .../dwarf-debug-notes-r/expected-stabs | 24 + .../test-cases/dwarf-debug-notes-r/foo.cxx | 4 + .../test-cases/dwarf-debug-notes-r/main.cxx | 4 + .../dwarf-debug-notes-r/stabs-filter.pl | 25 + .../dwarf-debug-notes/expected-stabs | 4 +- unit-tests/test-cases/filelist/Makefile | 48 + unit-tests/test-cases/filelist/hello.c | 29 + .../test-cases/late-link-error/Makefile | 43 + .../test-cases/late-link-error/link_error.s | 18 + .../test-cases/multiple-entry-points/test.s | 3 + .../test-cases/objc-references/Makefile | 48 + unit-tests/test-cases/objc-references/test.m | 52 + unit-tests/test-cases/rebase-basic/Makefile | 46 + unit-tests/test-cases/rebase-basic/bar.m | 13 + unit-tests/test-cases/rebase-basic/foo.c | 14 + unit-tests/test-cases/relocs-asm/relocs-asm.s | 99 +- unit-tests/test-cases/relocs-c/Makefile | 4 +- .../test-cases/tentative-to-real/Makefile | 44 + .../test-cases/tentative-to-real/test.c | 3 + 48 files changed, 4536 insertions(+), 1045 deletions(-) create mode 100644 doc/man/man1/rebase.1 create mode 100644 src/rebase.cpp create mode 100644 unit-tests/test-cases/bundle_loader/Makefile create mode 100644 unit-tests/test-cases/bundle_loader/bar.c create mode 100644 unit-tests/test-cases/bundle_loader/bundle.c create mode 100644 unit-tests/test-cases/bundle_loader/main.c create mode 100644 unit-tests/test-cases/dead_strip/Makefile create mode 100644 unit-tests/test-cases/dead_strip/deadwood.c create mode 100644 unit-tests/test-cases/dead_strip/main.c create mode 100644 unit-tests/test-cases/dead_strip/main.exp create mode 100644 unit-tests/test-cases/dwarf-debug-notes-r/Makefile create mode 100644 unit-tests/test-cases/dwarf-debug-notes-r/bar.cxx create mode 100644 unit-tests/test-cases/dwarf-debug-notes-r/expected-stabs create mode 100644 unit-tests/test-cases/dwarf-debug-notes-r/foo.cxx create mode 100644 unit-tests/test-cases/dwarf-debug-notes-r/main.cxx create mode 100755 unit-tests/test-cases/dwarf-debug-notes-r/stabs-filter.pl create mode 100644 unit-tests/test-cases/filelist/Makefile create mode 100644 unit-tests/test-cases/filelist/hello.c create mode 100644 unit-tests/test-cases/late-link-error/Makefile create mode 100644 unit-tests/test-cases/late-link-error/link_error.s create mode 100644 unit-tests/test-cases/objc-references/Makefile create mode 100644 unit-tests/test-cases/objc-references/test.m create mode 100644 unit-tests/test-cases/rebase-basic/Makefile create mode 100644 unit-tests/test-cases/rebase-basic/bar.m create mode 100644 unit-tests/test-cases/rebase-basic/foo.c create mode 100644 unit-tests/test-cases/tentative-to-real/Makefile create mode 100644 unit-tests/test-cases/tentative-to-real/test.c diff --git a/ChangeLog b/ChangeLog index d028e47..26a83ce 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,691 +1,443 @@ ------ Tagged ld64-47 - +----- Tagged ld64-59 ------ Tagged ld64-46 - -2006-03-10 Nick Kledzik +2006-06-22 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 + ld64 lost DWARF debug notes + MachOReaderRelocatable.hpp: add fHasUUID so kDebugInfoStabsUUID can be set later + unit-tests/test-cases/dwarf-debug-notes-r: added test case -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 +2006-06-21 Nick Kledzik + python 64-bit address miscalculation + src/MachOReaderRelocatable.hpp: change getTargetOffset() to sign extend the 32-bit value to 64-bits ------ 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-06-21 Nick Kledzik -2006-02-24 Nick Kledzik + ld64 seems to offset things incorrectly when using -r + src/MachOWriterExecutable.hpp: in -r mode, virtual sections don't increment address - * src/Options.cpp: 9A110: ld64 can't deal with section names >16 chars -2006-02-23 Nick Kledzik +----- Tagged ld64-58 - * 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-06-16 Nick Kledzik -2006-02-23 Nick Kledzik + src/rebase.cpp: fix page alignment problem + src/rebase.cpp: fix endianess problem with local non-lazy pointers - 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-06-15 Nick Kledzik -2006-02-23 Nick Kledzik + src/rebase.cpp: fix to build in CurryWeed + ld64.xcodeproj/project.pbxproj: fix to build properly in CurryWeed - 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-06-15 Nick Kledzik -2006-02-22 Nick Kledzik + Support .objc_class_name_* symbols + src/ObjectFile.h: Add kSymbolTableInAsAbsolute + src/MachOReaderRelocatable.hpp: synthesize references to required objc classes + src/MachOWriterExecutable.hpp: write objc_class_name as absolute symbol + unit-tests/test-cases/objc-references: added - * src/Options.cpp: chnage printf on unknown arg to a throw +2006-06-15 Nick Kledzik ------ Tagged ld64-39 + SECTION_ATTRIBUTES unset in ppc64 mach-o header + src/MachOWriterExecutable.hpp: add section attribute for sections with code -2006-02-20 Nick Kledzik +2006-06-15 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 + ld64 bogus duplicate symbol name linking GNU libobjc + src/MachOReaderRelocatable.hpp: only special case Apple's objc runtime objc classes -2006-02-20 Nick Kledzik +2006-06-15 Nick Kledzik - * unit-tests/test-cases/stabs-coalesce: added new test case - * src/ld.cpp.hpp: in collectStabs removed unused stabs + x86_64: ".align" directive not honored + src/MachOReaderRelocatable.hpp: change code alignment to not depend on atom size ------ Tagged ld64-38 +2006-06-14 Nick Kledzik -2006-02-17 Nick Kledzik + jump table into middle of weak symbol causes error + src/MachOReaderRelocatable.hpp: create direct references to the interior of weak symbols + src/MachOWriterExecutable.hpp: don't error on absolute references to interior of weak symbols - * src/MachOWriterExecutable.hpp: set correct n_sect field of stabs +2006-06-13 Nick Kledzik -2006-02-15 Nick Kledzik + src/Options.cpp: allow -image_base as an alias for -seg1addr - * src/MachOReaderArchive.hpp: with -all_load skip over both kinds of SYMDEFs - * unit-tests/test-cases/archive-basic/Makefile: add -all_load test case +2006-06-13 Nick Kledzik ------ Tagged ld64-37 + implement -d + src/Options.h: add fMakeTentativeDefinitionsReal + src/Options.cpp: set fMakeTentativeDefinitionsReal if -d option is found + src/MachOWriterExecutable.hpp: turn tentative into real definition if makeTentativeDefinitionsReal + unit-tests/test-cases/btentative-to-real: added test case -2006-02-13 Eric Christopher +2006-06-13 Nick Kledzik - * src/MachOWriterExecutable.hpp (assignFileOffsets): Simplify. Add comments. - Adjust whitespace. + implement -bundle_loader + src/Options.h: add fBundleLoader bit to DynamicLibraryOptions + src/Options.cpp: handle -bundle_loader + src/ld.cpp: pass fBundleLoader bit to MachOReaderDylib + src/MachOReaderDylib.hpp: support reading MH_EXECUTE files if fBundleLoader is set + src/MachOWriterExecutable.hpp: set bundle loader ordinal as EXECUTABLE_ORDINAL + unit-tests/test-cases/bundle_loader: added test case -2006-02-13 Nick Kledzik +2006-06-12 Nick Kledzik - * src/MachOWriterExecutable.hpp: in Writer::fixUpReferenceRelocatable() fix kPCRel32 for external case + -syslibroot can cause "can't find ordinal for imported" error + src/MachOReaderDylib.hpp: in Reader::reExports() compare install path in addition to load path -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-06-10 Nick Kledzik -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 + Need rebasing tool + src/rebase.cpp: added + unit-tests/test-cases/rebase-basic: added + doc/man/man1/rebase.1: added + ld64.xcodeproj/project.pbxproj: added rebase target. changed all targets to build with dwarf + -2006-02-02 Nick Kledzik +2006-06-10 Nick Kledzik - * src/MachOReaderRelocatable.hpp: properly coalesce 8-byte literals - * src/MachOWriterExecutable.hpp: support ppc64::kPointerDiff32 + src/machochecker.cpp: add some ppc reloc sanity checking ------ Tagged ld64-32 +----- Tagged ld64-57 -2006-02-02 Nick Kledzik +2006-06-06 Nick Kledzik - * src/MachOReaderRelocatable.hpp: support anonymous zero fill atoms + ld64 is not adding a final '/' char on the initial directory-name SO stab debug map entry + ld.cpp: Change Linker::synthesizeStabs() to assure directory SO always has a trailing slash + unit-tests/test-cases/dwarf-debug-notes/expected-stabs: update with trailing / + +2006-06-06 Nick Kledzik -2006-02-02 Nick Kledzik + -sectcreate of a 0-byte section fails + MachOWriterExecutable.cpp: Don't error out on zero length segments + MachOWriterExecutable.cpp: For ppc64 reloc base address is the first writable segment iff + there is a writable segment >4GB from base address - * 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-06-04 Eric Christopher -2006-02-01 Nick Kledzik + Radar 4560240 + Radar 3964999 + * src/ld.cpp (createReader): Fixed error message. + (resolve): Ditto. + (resolveFrom): Ditto. + (checkUndefines): Ditto. - * src/MachOReaderRelocatable.hpp: Support -macosx_version_min 10.5 +----- Tagged ld64-56 -2006-02-01 Nick Kledzik +2006-05-23 Nick Kledzik - * src/MachOReaderRelocatable.hpp: don't try to parse debug_line dwarf if no symboled atoms + No debug notes for ObjC methods when linking with ld64 + ld.cpp: don't limit debug notes to functions starting with underscore ------ Tagged ld64-31 +2006-05-22 Nick Kledzik -2006-02-01 Eric Christopher + ld64 spends much time in mach_o::relocatable::Reader::findAtomByName + * src/MachOReaderRelocatable.hpp: add makeReferenceToSymbol() so that x86_64 does not need to do by-name lookups - * 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-05-22 Nick Kledzik + remove inferring warning + * ld.cpp: Remove "inferring" warning. If a link failed and now arch was specifed add which arch was + inferred to error message -2006-01-31 Nick Kledzik +2006-05-19 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 + ld64 does not honor -arch_multiple + * ld.cpp: If fOptions.printArchPrefix(), add architecture name to error message -2006-01-31 Nick Kledzik +2006-05-19 Nick Kledzik - * ld64.xcodeproj/project.pbxproj : Make buildable on Leopard - * src/MachOFileAbstraction.hpp: make buildable without latest cctools headers + Support S_16BYTE_LITERALS section types + * src/MachOReaderRelocatable.hpp: support S_16BYTE_LITERALS + * src/MachOWriterExecutable.hpp: support S_16BYTE_LITERALS -2006-01-31 Nick Kledzik +2006-05-19 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 + "warning can't parse dwarf compilation unit info" warnings building debug + * src/MachOReaderRelocatable.hpp: fix bugs in dwarf line table parsing -2006-01-30 Eric Christopher +----- Tagged ld64-55 - * src/ExecutableFile.h: Indent. +2006-05-18 Nick Kledzik -2006-01-30 Nick Kledzik + Default the pagezero size to 4GB for x86-64 + * src/Options.cpp: Chnage default the pagezero size to 4GB for x86-64 - * src/MachOReaderRelocatable.hpp: performance improvements - * src/ld.cpp: now that stubs are synthesized in write, don't need to special case anymore +2006-05-18 Nick Kledzik -2006-01-30 Nick Kledzik + x86_64 CarbonCore fails to link with "atom not found in symbolIndex" + * src/MachOWriterExecutable.hpp: in buildObjectFileFixups() don't call addObjectRelocs() on kNoFixUp references - * src/MachOReaderRelocatable.hpp: fix parsing of pcc relocs - * unit-tests/test-cases/relocs-asm/relocs-asm.s: add test case for above +2006-05-18 Nick Kledzik -2006-01-29 Nick Kledzik + ld64: .section defaults to read-only + * src/MachOReaderRelocatable.hpp: default unknown segments to r/w - * 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-05-18 Nick Kledzik -2006-01-29 Nick Kledzik + -fvisibility=hidden causes crashes for x86_64 + * src/MachOWriterExecutable.hpp: properly handle RIP relative tentative definitions - * src/Options.cpp: verify -allow_stack_execute is only used on main executables +2006-05-12 Nick Kledzik -2006-01-29 Nick Kledzik + * src/Architectures.hpp: add x86::kAbsolute32 + * src/MachOReaderRelocatable.hpp: generate x86::kAbsolute32 for mdynamic-no-pic instructions + * src/MachOWriterExecutable.hpp: process x86::kAbsolute32 reference kind - * src/MachOReaderRelocatable.hpp: sync with latest dwarf reader from Geoff - * src/debugline.c: sync with latest dwarf reader from Geoff +----- Tagged ld64-54 -2006-01-27 Eric Christopher +2006-05-11 Nick Kledzik - * src/ld.cpp (Linker::syntesizeStabs): Correct spelling. Update all uses. + CF-393 failes to link for x86_64 + * src/MachOWriterExecutable.cpp: fix sign extension for Rel32 relocs in Writer::fixUpReferenceRelocatable -2006-01-27 Eric Christopher +2006-05-11 Nick Kledzik - * 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. + warning arch x86_64 not found using i386 + * src/ld.cpp: remove hack to allow x86_64 to link against i386 dylibs -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-05-10 Nick Kledzik -2006-01-27 Eric Christopher + x86_64: .objc_class_name symbol names scrambled + * src/MachOReaderRelocatable.hpp: properly compute alignment of __OBJC __class sections - * 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 +2006-05-08 Nick Kledzik - * src/MachOWriterExecutable.hpp: handle NULL strings in SO debug notes + Support -dead_strip + * src/Options.h/cpp: implement -why_load and -why_live. Enable -dead_strip. + * src/MachOReaderArchive.hpp: implement -why_load + * src/MachOReaderRelocatable.hpp: suppress GCC_except_table* symbols in final output + * src/ld.cpp: implement dead code stripping + * unit-tests/test-cases/dead_strip: added -2006-01-26 Nick Kledzik +----- Tagged ld64-53 - * src/MachOWriterExecutable.hpp: fix header padding calculation and thread state +2006-05-05 Nick Kledzik -2006-01-26 Nick Kledzik + * src/Options.cpp: make 10.4 be minimum OS version for newer architectures - Rewrite all stabs processing. - Move sythesize of debug notes into ld.cpp +2006-05-05 Nick Kledzik -2006-01-26 Nick Kledzik + N_SO symbols in 64-bit builds have a zero address for n.n_value + * src/ld.cpp: for SO stabs, associate first and last atom in the SO range + * src/MachOWriterExecutable.hpp: use atom associated with SO stab to set ins n_value - * src/MachOWriterExecutable.hpp: fix ppc and ppc64 stub relocs +2006-05-05 Nick Kledzik -2006-01-25 Nick Kledzik + * MachOWriterExecutable.hpp: fix end FUN stab to have length of function - * ld64.xcodeproj/project.pbxproj: special case building in Curry -2006-01-25 Nick Kledzik +2006-05-02 Nick Kledzik - * src/MachOWriterExecutable.hpp: fix bugs in stub/lazy-pointer synthesis + 64-bit main executables should have 4GB zero page by default + * src/Opptions.cpp: change default pagezero_size to 4GB for ppc64 + 64 bit: apps with -mdynamic-no-pic seg fault when page zero > 4GB + * src/MachOWriterExecutable.cpp: rework pagezero for ppc64 so that if any mdynamic-no-pic code + is found, the code is kept in the low 2GB, and a new segment is create to map away up to 4GB. -2006-01-24 Eric Christopher +2006-05-02 Nick Kledzik - * 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. + * src/Opptions.cpp: remove warning about -stack_addr not specified. Add warning if 32-bit stack + overlaps shared region -2006-01-24 Nick Kledzik +----- Tagged ld64-52.1 - * src/MachOReaderRelocatable.hpp: better C++ eh parsing +2006-05-01 Nick Kledzik -2006-01-23 Eric Christopher + * src/MachOReaderRelocatable.cpp: rework handleAnonymousNonLazyPointers() to handle anl's in the middle + the __data section too. - * 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. +----- Tagged ld64-52 -2006-01-23 Nick Kledzik +2006-04-28 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 + 64-bit: 9A152 TextEdit crashes in dlopen on bring-up + * src/MachOReaderRelocatable.cpp: rework anonymous non-lazy-pointer detection -2006-01-23 Nick Kledzik +2006-04-28 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 + 64 Bit: Development build of ppc64 TextEdit gets confused about static variables + * src/MachOReaderRelocatable.cpp: mark non-lazy-pointer atoms as scopeTranslationUnit if targetting a static symbol -2006-01-17 Nick Kledzik - * src/MachOReaderRelocatable.hpp: tweaks to synthesizing debug notes +2006-04-27 Nick Kledzik -2006-01-16 Nick Kledzik + dyld crashes ungracefully on x86_64 when there is an internal exception + * src/MachOWriterExecutable.cpp: allow non-zero PCRELGOT addends (used by C++ eh frames) - * 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-04-21 Nick Kledzik -2006-01-11 Nick Kledzik + * src/Options.cpp: fix default address for ppc64 custom stack + * src/MachOWriterExecutable.cpp: fix set up of ppc64 custom stack - * src/MachOReaderRelocatable.hpp: support Tiger crt1.o build with old ld64 - * src/ObjectDump.hpp: Support -arch option -2006-01-10 Nick Kledzik +2006-04-14 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 + * src/Options.cpp: fix -sub_library processing to work it dylib is specifed with leaf name -2006-01-10 Nick Kledzik +----- Tagged ld64-51.1 - * src/MachOReaderRelocatable.hpp: warn if dwarf can't be parsed - * src/MachOReaderArchive.hpp: modTime for OSO stabs from archives is .a modTime +2006-04-13 Nick Kledzik -2006-01-09 Nick Kledzik + 64-bit: 9A152 TextEdit crashes in dlopen on bring-up + * src/MachOReaderRelocatable.hpp: when detecting anonymous non-lazy-pointers disqualify data + that points to static or global symbols + * src/ld.cpp: print version of ld64 in error messages - * track modification time of .o files so that sythesized OSO stab will have it -2006-01-09 Nick Kledzik +----- Tagged ld64-51 - * src/MachOFileAbstraction.hpp: add macho_uuid_command - * src/MachOWriterExecutable.cpp: add UUID load command to generated files +2006-04-11 Nick Kledzik -2006-01-09 Nick Kledzik + exported symbols not properly stripped + * src/MachOReaderRelocatable.hpp: enable AnonymousAtom::setScope() - * 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-03-31 Nick Kledzik -2006-01-05 Nick Kledzik + ld64 fails when linking debug ppc64 HIToolbox + * src/MachOReaderRelocatable.hpp: handle anonymous non-lazy pointers encoded with local relocations + * src/MachOWriterExecutable.hpp: in -r mode, only generated INDIRECT_SYMBOL_LOCAL for non-lazy targets that - * src/MachOReaderRelocatable.hpp: support new relocations -2006-01-05 Nick Kledzik +2006-03-31 Nick Kledzik - * src/MachOReaderDylib.hpp: support MH_DYLIB_STUB - * src/MachOReaderRelocatable.hpp: Add Geoff's comp unit extractor + ld64 should remove generated file if link errors out + * src/MachOWriterExecutable.hpp: catch exceptions in Writer::write(), delete output file, and rethrow -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 +----- Tagged ld64-50 -2006-01-03 Eric Christopher +2006-03-29 Nick Kledzik - * 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. + * src/MachOWriterExecutable.hpp: fix x86_64 addends when -multi_module forces an external relocation -2006-01-02 Nick Kledzik +2006-03-29 Nick Kledzik - * Refactor: move Atom::writeContent() to Writer + * src/MachOReaderRelocatable.hpp: synthesize .objc_class_name symbols + * src/MachOFileAbstraction.hpp: use strncpy for sect/seg names to zero fill trailing space -2005-12-23 Nick Kledzik +2006-03-28 Nick Kledzik - * Reworked, simplify, and document test harness - * unit-tests/README: Added + * src/MachOReaderRelocatable.hpp: fix spurious warning about dwarf line info -2005-12-23 Nick Kledzik +----- Tagged ld64-49.1 - * src/MachOReaderRelocatable.hpp: fixes for Objective-C - * unit-tests/test-cases/relocs-objc: Added +2006-03-25 Nick Kledzik -2005-12-22 Nick Kledzik + * MachOWriterExecutable.hpp : don't complain about ppc64 dyld being based > 4GB - * src/MachOReaderRelocatable.hpp: fix check that next reloc is pair - * src/MachOReaderRelocatable.hpp: Add code to synthesize essential stabs from dwarf +----- Tagged ld64-49 -2005-12-21 Nick Kledzik +2006-03-24 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 + * src/MachOWriterExecutable.hpp: dyld is allowed to have synthesized non-lazy pointers + ld64 is after processing bad GSYM stabs + * src/MachOReaderRelocatable.hpp: if a GSYM is found that does not match any data symbol, suppress it -2005-12-15 Eric Christopher +2006-03-23 Nick Kledzik - * 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/MachOWriterExecutable.hpp: in Writer::fixUpReferenceFinal() fix when x86::kPointer is for an + external relocation - * 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. +2006-03-23 Nick Kledzik -2005-12-15 Nick Kledzik + * src/Options.cpp: change macosx-min-version to default to a per-architecture setting + add warning if -pagezero_size is not page aligned + * src/MachOWriterExecutable.hpp: properly handle external relocations for ppc64 with 4GB pagezero + * src/machochecker.cpp: sanity check relocation records - * src/MachOReaderRelocatable.hpp: Add comments about dwarf +----- Tagged ld64-48 -2005-12-14 Nick Kledzik +2006-03-21 Nick Kledzik - * src/ELFFileAbstraction.hpp: Added - * src/ELFReaderRelocatable.hpp: Added - * Lot of fixes for new architecture + 64bit: passing function pointer to another function passes the wrong function address + * src/MachOReaderRelocatable.hpp: when processing a non-lazy pointer to a static function, don't accidentally + match it to a STAB symbol. -2005-12-13 Nick Kledzik +2006-03-21 Nick Kledzik - * src/MachOReaderRelocatable.hpp: check for S_ATTR_DEBUG and ignore those sections - * unit-tests/test-cases/dwarf-ignore: added + .eh symbols make up 13% of libstdc++'s stripped binary size + * src/ObjectFile.h: add ReaderOptions.fForFinalLinkedImage + * src/Options.cpp: setup ReaderOptions.fForFinalLinkedImage + * src/MachOReaderRelocatable.hpp: mark .eh symbols kSymbolTableNotIn when building final linked image -2005-12-12 Nick Kledzik +2006-03-21 Nick Kledzik - * Added test harness and three initial tests: - relocs-asm, relocs-c, and hello-world + ld64 does not parse optional second argument to -filelist + * unit-tests/test-cases/filelist: added + * src/Options.cpp: in Options::loadFileList() handle comma option -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. +----- Tagged ld64-47 -2005-12-12 Nick Kledzik - * src/ObjectDump.cpp: Add command line options: -no_content, -stabs, -no_sort +----- Tagged ld64-46 -2005-12-11 Eric Christopher +2006-03-10 Nick Kledzik - * src/Options.cpp: Reformat. - * src/Options.h: Ditto. + 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 -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. +2006-03-09 Nick Kledzik -2005-12-06 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 - * src/Options.cpp src/Options.h: Add design comments +2006-03-09 Nick Kledzik -2005-12-05 Eric Christopher + crash in stub with 2GB pagezero + * src/MachOWriterExecutable.hpp: StubAtom can't be no-pic if a large zero-page is used - * src/ld.cpp (Linker::createWriter): Uncomment ppc64 and - i386 linkers. +2006-03-06 Nick Kledzik -2005-12-05 Eric Christopher + * src/Options.cpp: addSectionAlignment, warn if -sectalign alignment is not a power of two - * ChangeLog: New file. +----- Tagged ld64-45 -2005-12-02 Nick Kledzik +2006-03-06 Nick Kledzik - * src/ObjectFile.h: Add design comments + ld64 failed: rel32 out of range when linking a dylib + * src/MachOWriterExecutable.cpp: in Writer::fixUpReferenceFinal add (int32_t) cast -2005-11-28 Nick Kledzik +2006-03-06 Nick Kledzik - * Refactor Atom to use getDefinitionKind() + LP64/9A122: ld64: hang when trying to link DiscRecording framework + * src/Options.cpp: addSectionAlignment, warn on zero. Use log2() for alignment conversion -2005-11-21 Nick Kledzik +2006-03-06 Nick Kledzik - * src/MachOWriterExecutable.hpp: don't generate section for commons in -r mode + x86_THREAD_STATE64_COUNT will change, ld64 must adapt + * src/MachOWriterExecutable.hpp: update ThreadsLoadCommandsAtom for new thread status layout -2005-11-18 Nick Kledzik +----- Tagged ld64-44 - * x86 tweaks +2006-03-04 Nick Kledzik -2005-11-18 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. - * src/ObjectDump.cpp: make work with command line arguments +2006-03-02 Nick Kledzik -2005-11-18 Nick Kledzik + * src/ld.cpp: only re-map page aligned sub-parts of a fat file. A conformat mmap() requires alignment. - * Massive rework to remove preprocessor conditionals and use templates +----- Tagged ld64-43 -2005-11-14 Nick Kledzik +2006-03-03 Nick Kledzik - * Created new Subversion repository for ld64 from cvs tag ld64-27.2 + RIP-relative offsets aren't handled properly when the instruction has immediate operands + * src/Architectures.hpp: add x86_64::kPCRel32_* + * src/MachOReaderRelocatable.hpp: generate x86_64::kPCRel32_* + * src/MachOWriterExecutable.hpp: process x86_64::kPCRel32_* diff --git a/doc/man/man1/rebase.1 b/doc/man/man1/rebase.1 new file mode 100644 index 0000000..6743a96 --- /dev/null +++ b/doc/man/man1/rebase.1 @@ -0,0 +1,39 @@ +.Dd June 6, 2006 +.Dt rebase 1 +.Os Darwin +.Sh NAME +.Nm rebase +.Nd "Changes base address of dylibs and bundles" +.Sh SYNOPSIS +.Nm +.Op Fl low_address Ar addr +.Op Fl high_address Ar addr +.Op Fl arch Ar arch +.Op Fl v +.Ar file(s) +.Sh DESCRIPTION +The base address of an image (dylib or bundle) is the preferred address for it to be loaded. By +default all images are built with a base address of zero. At runtime, if the +preferred memory range is already occupied, dyld will "slide" the image to a new address range. +There is a small cost to the slide, as dyld must do some fix ups. +The rebase tool takes a list of images and adjust their base address to be non-overlapping. If no +low or high address is specified, the a suitable address range is choosen for the architecture. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl low_address Ar addr +Force the base address for the first image to be +.Ar addr +(specified in hex). Each subsequent file gets the next available base address. +.It Fl high_address Ar addr +Force the base address for the last image to be such that when that image is loaded it occupies +memory up to +.Ar addr +(specified in hex). Each preceeding file gets the previous available base address. +.It Fl arch Ar arch +Only rebase the specified architecture. Other architectures in a universal image are left as is. +.It Fl v +Verbose. Print information about rebasing done. +.El +.Sh SEE ALSO +.Xr ld 1 diff --git a/ld64.xcodeproj/project.pbxproj b/ld64.xcodeproj/project.pbxproj index 1ce18dd..3475042 100644 --- a/ld64.xcodeproj/project.pbxproj +++ b/ld64.xcodeproj/project.pbxproj @@ -13,17 +13,27 @@ buildPhases = ( F96D5367094A2754008E9EE8 /* ShellScript */, ); - buildSettings = { - PRODUCT_NAME = "unit-tests"; - }; dependencies = ( F96D536A094A275D008E9EE8 /* PBXTargetDependency */, F96D536C094A275F008E9EE8 /* PBXTargetDependency */, + F96904890A4333AC00B77D2A /* PBXTargetDependency */, F9EA73970974999B008B4F1D /* PBXTargetDependency */, ); name = "unit-tests"; productName = "unit-tests"; }; + F9B1A2670A3A567B00DA8FAB /* all */ = { + isa = PBXAggregateTarget; + buildConfigurationList = F9B1A26C0A3A568700DA8FAB /* Build configuration list for PBXAggregateTarget "all" */; + buildPhases = ( + ); + dependencies = ( + F9B1A2690A3A568200DA8FAB /* PBXTargetDependency */, + F9B1A26B0A3A568400DA8FAB /* PBXTargetDependency */, + ); + name = all; + productName = all; + }; /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ @@ -31,10 +41,12 @@ 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 */; }; + F9B1A2640A3A563E00DA8FAB /* rebase.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = F9B1A2580A3A448800DA8FAB /* rebase.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 */; }; + F9EC78060A2F8674002A3E39 /* rebase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9EC78050A2F8674002A3E39 /* rebase.cpp */; }; /* End PBXBuildFile section */ /* Begin PBXBuildRule section */ @@ -56,24 +68,14 @@ }; /* 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 */ + F96904880A4333AC00B77D2A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9023C3006D5A227001BBF46 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9EC77ED0A2F85F6002A3E39 /* rebase */; + remoteInfo = rebase; + }; F96D5369094A275D008E9EE8 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = F9023C3006D5A227001BBF46 /* Project object */; @@ -88,6 +90,20 @@ remoteGlobalIDString = F971EED206D5ACF60041D381; remoteInfo = ObjectDump; }; + F9B1A2680A3A568200DA8FAB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9023C3006D5A227001BBF46 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9023C3806D5A23E001BBF46; + remoteInfo = ld; + }; + F9B1A26A0A3A568400DA8FAB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9023C3006D5A227001BBF46 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9EC77ED0A2F85F6002A3E39; + remoteInfo = rebase; + }; F9EA73960974999B008B4F1D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = F9023C3006D5A227001BBF46 /* Project object */; @@ -108,6 +124,16 @@ ); runOnlyForDeploymentPostprocessing = 1; }; + F9B1A25E0A3A44CB00DA8FAB /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man1; + dstSubfolderSpec = 0; + files = ( + F9B1A2640A3A563E00DA8FAB /* rebase.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -128,12 +154,15 @@ 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 = ""; }; + F9B1A2580A3A448800DA8FAB /* rebase.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = rebase.1; path = doc/man/man1/rebase.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 = ""; }; + F9EC77EE0A2F85F6002A3E39 /* rebase */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = rebase; sourceTree = BUILT_PRODUCTS_DIR; }; + F9EC78050A2F8674002A3E39 /* rebase.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = rebase.cpp; path = src/rebase.cpp; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -158,6 +187,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + F9EC77EC0A2F85F6002A3E39 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -183,7 +219,9 @@ F9EA7583097882F3008B4F1D /* debugline.h */, F9EA72D4097454FF008B4F1D /* machochecker.cpp */, F971EED706D5AD240041D381 /* ObjectDump.cpp */, + F9EC78050A2F8674002A3E39 /* rebase.cpp */, F97F5028070D0BB200B9FCD7 /* ld64.1 */, + F9B1A2580A3A448800DA8FAB /* rebase.1 */, F9023C3A06D5A23E001BBF46 /* Products */, ); sourceTree = ""; @@ -194,6 +232,7 @@ F9023C3906D5A23E001BBF46 /* ld64 */, F971EED306D5ACF60041D381 /* ObjectDump */, F9EA72CB097454A6008B4F1D /* machocheck */, + F9EC77EE0A2F85F6002A3E39 /* rebase */, ); name = Products; sourceTree = ""; @@ -213,9 +252,6 @@ F9E8D4BE07FCAF2A00FD5801 /* PBXBuildRule */, F9E8D4BD07FCAF2000FD5801 /* PBXBuildRule */, ); - buildSettings = { - PRODUCT_NAME = ld64; - }; dependencies = ( ); name = ld; @@ -232,9 +268,6 @@ ); buildRules = ( ); - buildSettings = { - PRODUCT_NAME = ObjectDump; - }; dependencies = ( ); name = ObjectDump; @@ -251,13 +284,6 @@ ); buildRules = ( ); - buildSettings = { - GCC_GENERATE_DEBUGGING_SYMBOLS = NO; - GCC_MODEL_TUNING = G5; - INSTALL_PATH = "$(HOME)/bin"; - PREBINDING = NO; - PRODUCT_NAME = machocheck; - }; dependencies = ( ); name = machocheck; @@ -265,24 +291,37 @@ productReference = F9EA72CB097454A6008B4F1D /* machocheck */; productType = "com.apple.product-type.tool"; }; + F9EC77ED0A2F85F6002A3E39 /* rebase */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9EC77F00A2F8616002A3E39 /* Build configuration list for PBXNativeTarget "rebase" */; + buildPhases = ( + F9EC77EB0A2F85F6002A3E39 /* Sources */, + F9EC77EC0A2F85F6002A3E39 /* Frameworks */, + F9B1A25E0A3A44CB00DA8FAB /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = rebase; + productName = rebase; + productReference = F9EC77EE0A2F85F6002A3E39 /* rebase */; + 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 = ( + F9B1A2670A3A567B00DA8FAB /* all */, F9023C3806D5A23E001BBF46 /* ld */, + F9EC77ED0A2F85F6002A3E39 /* rebase */, F971EED206D5ACF60041D381 /* ObjectDump */, F9EA72CA097454A6008B4F1D /* machocheck */, F96D5368094A2754008E9EE8 /* unit-tests */, @@ -335,9 +374,22 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + F9EC77EB0A2F85F6002A3E39 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9EC78060A2F8674002A3E39 /* rebase.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + F96904890A4333AC00B77D2A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9EC77ED0A2F85F6002A3E39 /* rebase */; + targetProxy = F96904880A4333AC00B77D2A /* PBXContainerItemProxy */; + }; F96D536A094A275D008E9EE8 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = F9023C3806D5A23E001BBF46 /* ld */; @@ -348,6 +400,16 @@ target = F971EED206D5ACF60041D381 /* ObjectDump */; targetProxy = F96D536B094A275F008E9EE8 /* PBXContainerItemProxy */; }; + F9B1A2690A3A568200DA8FAB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9023C3806D5A23E001BBF46 /* ld */; + targetProxy = F9B1A2680A3A568200DA8FAB /* PBXContainerItemProxy */; + }; + F9B1A26B0A3A568400DA8FAB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9EC77ED0A2F85F6002A3E39 /* rebase */; + targetProxy = F9B1A26A0A3A568400DA8FAB /* PBXContainerItemProxy */; + }; F9EA73970974999B008B4F1D /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = F9EA72CA097454A6008B4F1D /* machocheck */; @@ -361,8 +423,9 @@ buildSettings = { COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + DEBUG_INFORMATION_FORMAT = dwarf; GCC_DYNAMIC_NO_PIC = YES; - GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; GCC_TREAT_WARNINGS_AS_ERRORS = YES; @@ -392,7 +455,6 @@ INSTALL_PATH = /usr/bin; OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)"; OTHER_LDFLAGS = ""; - PREBINDING = NO; PRODUCT_NAME = ld64; SECTORDER_FLAGS = ""; VERSIONING_SYSTEM = "apple-generic"; @@ -405,14 +467,13 @@ buildSettings = { COPY_PHASE_STRIP = YES; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_DYNAMIC_NO_PIC = YES; - GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; 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; @@ -440,9 +501,9 @@ INSTALL_PATH = /usr/bin; OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)"; OTHER_LDFLAGS = ""; - PREBINDING = NO; PRODUCT_NAME = ld64; SECTORDER_FLAGS = ""; + VALID_ARCHS = "i386 ppc"; VERSIONING_SYSTEM = "apple-generic"; WARNING_CFLAGS = "-Wall"; }; @@ -452,7 +513,8 @@ isa = XCBuildConfiguration; buildSettings = { COPY_PHASE_STRIP = NO; - GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; INSTALL_PATH = "$(HOME)/bin"; @@ -473,15 +535,15 @@ isa = XCBuildConfiguration; buildSettings = { COPY_PHASE_STRIP = YES; - GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = 0; + GCC_OPTIMIZATION_LEVEL = s; INSTALL_PATH = "$(HOME)/bin"; OTHER_LDFLAGS = ""; OTHER_REZFLAGS = ""; PREBINDING = NO; PRODUCT_NAME = ObjectDump; - SECTORDER_FLAGS = ""; WARNING_CFLAGS = ( "-Wmost", "-Wno-four-char-constants", @@ -524,12 +586,36 @@ }; name = Release; }; + F9B1A26D0A3A568700DA8FAB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + PRODUCT_NAME = all; + }; + name = Debug; + }; + F9B1A26E0A3A568700DA8FAB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + PRODUCT_NAME = all; + ZERO_LINK = NO; + }; + name = Release; + }; F9EA72D0097454D5008B4F1D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; INSTALL_PATH = "$(HOME)/bin"; PREBINDING = NO; PRODUCT_NAME = machocheck; @@ -540,7 +626,8 @@ isa = XCBuildConfiguration; buildSettings = { COPY_PHASE_STRIP = YES; - GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_MODEL_TUNING = G5; INSTALL_PATH = "$(HOME)/bin"; PREBINDING = NO; @@ -548,6 +635,37 @@ }; name = Release; }; + F9EC77F10A2F8616002A3E39 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = "$(HOME)/bin"; + PREBINDING = NO; + PRODUCT_NAME = rebase; + }; + name = Debug; + }; + F9EC77F20A2F8616002A3E39 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + GCC_PREPROCESSOR_DEFINITIONS = "$(GCC_PREPROCESSOR_DEFINITIONS_$(RC_RELEASE))"; + GCC_PREPROCESSOR_DEFINITIONS_CurryWeed = __OPEN_SOURCE__; + INSTALL_PATH = /usr/bin; + PREBINDING = NO; + PRODUCT_NAME = rebase; + VALID_ARCHS = "i386 ppc"; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -587,6 +705,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + F9B1A26C0A3A568700DA8FAB /* Build configuration list for PBXAggregateTarget "all" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9B1A26D0A3A568700DA8FAB /* Debug */, + F9B1A26E0A3A568700DA8FAB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; F9EA72CF097454D5008B4F1D /* Build configuration list for PBXNativeTarget "machocheck" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -596,6 +723,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + F9EC77F00A2F8616002A3E39 /* Build configuration list for PBXNativeTarget "rebase" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9EC77F10A2F8616002A3E39 /* Debug */, + F9EC77F20A2F8616002A3E39 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = F9023C3006D5A227001BBF46 /* Project object */; diff --git a/src/Architectures.hpp b/src/Architectures.hpp index 71c10c4..e735f9e 100644 --- a/src/Architectures.hpp +++ b/src/Architectures.hpp @@ -56,9 +56,19 @@ struct x86 typedef Pointer32 P; enum ReferenceKinds { kNoFixUp, kFollowOn, kPointer, kPointerWeakImport, kPointerDiff, - kPCRel32, kPCRel32WeakImport }; + kPCRel32, kPCRel32WeakImport, kAbsolute32 }; }; +struct x86_64 +{ + typedef Pointer64 P; + + enum ReferenceKinds { kNoFixUp, kFollowOn, kPointer, kPointerWeakImport, kPointerDiff, kPointerDiff32, + kPCRel32, kPCRel32_1, kPCRel32_2, kPCRel32_4, + kBranchPCRel32, kBranchPCRel32WeakImport, + kPCRel32GOTLoad, kPCRel32GOTLoadWeakImport, + kPCRel32GOT, kPCRel32GOTWeakImport }; +}; diff --git a/src/MachOFileAbstraction.hpp b/src/MachOFileAbstraction.hpp index 6110e80..5d46cbf 100644 --- a/src/MachOFileAbstraction.hpp +++ b/src/MachOFileAbstraction.hpp @@ -39,6 +39,9 @@ struct uuid_command { }; #endif +#ifndef S_16BYTE_LITERALS + #define S_16BYTE_LITERALS 0xE +#endif #include "FileAbstraction.hpp" #include "Architectures.hpp" @@ -130,7 +133,7 @@ public: 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); } + void set_segname(const char* value) INLINE { strncpy(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); } @@ -179,10 +182,10 @@ 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); } + void set_sectname(const char* value) INLINE { strncpy(section.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); } + void set_segname(const char* value) INLINE { strncpy(section.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); } diff --git a/src/MachOReaderArchive.hpp b/src/MachOReaderArchive.hpp index d40ca70..c5c120b 100644 --- a/src/MachOReaderArchive.hpp +++ b/src/MachOReaderArchive.hpp @@ -385,10 +385,11 @@ std::vector* Reader::getJustInTimeAtomsFor(const cha if ( result != NULL ) { const Entry* member = (Entry*)&fFileContent[E::get32(result->ran_off)]; if ( fInstantiatedEntries.count(member) == 0 ) { + if ( fOptions.fWhyLoad ) + printf("%s forced load of %s(%s)\n", name, this->getPath(), member->getName()); // 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()); } } diff --git a/src/MachOReaderDylib.hpp b/src/MachOReaderDylib.hpp index e9ff931..9cadfbb 100644 --- a/src/MachOReaderDylib.hpp +++ b/src/MachOReaderDylib.hpp @@ -85,6 +85,7 @@ public: 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 dontDeadStrip() const { return false; } virtual bool isZeroFill() const { return false; } virtual uint64_t getSize() const { return 0; } virtual std::vector& getReferences() const { return fgEmptyReferenceList; } @@ -131,10 +132,11 @@ 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() {} + static bool validFile(const uint8_t* fileContent, bool executableOrDylib); + static Reader* make(const uint8_t* fileContent, uint64_t fileLength, const char* path, + bool executableOrDylib, const ObjectFile::ReaderOptions& options) + { return new Reader(fileContent, fileLength, path, executableOrDylib, options); } + virtual ~Reader() {} virtual const char* getPath() { return fPath; } virtual time_t getModificationTime() { return 0; } @@ -168,7 +170,8 @@ private: struct PathAndFlag { const char* path; bool reExport; }; - Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, const ObjectFile::ReaderOptions& options); + Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, + bool executableOrDylib, const ObjectFile::ReaderOptions& options); const char* fPath; const char* fParentUmbrella; @@ -191,11 +194,11 @@ bool Reader::fgLogHashtable = false; template -Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, const ObjectFile::ReaderOptions& options) +Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, bool executableOrDylib, const ObjectFile::ReaderOptions& options) : fParentUmbrella(NULL), fDylibInstallPath(NULL), fDylibTimeStamp(0), fDylibtCurrentVersion(0), fDylibCompatibilityVersion(0) { // sanity check - if ( ! validFile(fileContent) ) + if ( ! validFile(fileContent, executableOrDylib) ) throw "not a valid mach-o object file"; fPath = strdup(path); @@ -301,7 +304,7 @@ Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* p } // validate minimal load commands - if ( fDylibInstallPath == NULL ) + if ( (fDylibInstallPath == NULL) && (header->filetype() != MH_EXECUTE) ) throw "dylib missing LC_ID_DYLIB load command"; if ( symbolTable == NULL ) throw "dylib missing LC_SYMTAB load command"; @@ -404,8 +407,9 @@ 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 char* childInstallPath = child->getInstallPath(); for (typename std::vector::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { - if ( it->reExport && (strcmp(it->path, child->getPath()) == 0) ) + if ( it->reExport && ((strcmp(it->path, child->getPath()) == 0) || ((childInstallPath!=NULL) && (strcmp(it->path, childInstallPath)==0))) ) return true; } @@ -422,44 +426,80 @@ bool Reader::reExports(ObjectFile::Reader* child) } template <> -bool Reader::validFile(const uint8_t* fileContent) +bool Reader::validFile(const uint8_t* fileContent, bool executableOrDylib) { 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; + switch ( header->filetype() ) { + case MH_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_EXECUTE: + return executableOrDylib; + default: + return false; + } } template <> -bool Reader::validFile(const uint8_t* fileContent) +bool Reader::validFile(const uint8_t* fileContent, bool executableOrDylib) { 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; + switch ( header->filetype() ) { + case MH_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_EXECUTE: + return executableOrDylib; + default: + return false; + } } template <> -bool Reader::validFile(const uint8_t* fileContent) +bool Reader::validFile(const uint8_t* fileContent, bool executableOrDylib) { 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; + switch ( header->filetype() ) { + case MH_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_EXECUTE: + return executableOrDylib; + default: + return false; + } } +template <> +bool Reader::validFile(const uint8_t* fileContent, bool executableOrDylib) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC_64 ) + return false; + if ( header->cputype() != CPU_TYPE_X86_64 ) + return false; + switch ( header->filetype() ) { + case MH_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_EXECUTE: + return executableOrDylib; + default: + return false; + } +} diff --git a/src/MachOReaderRelocatable.hpp b/src/MachOReaderRelocatable.hpp index 508be47..7458d59 100644 --- a/src/MachOReaderRelocatable.hpp +++ b/src/MachOReaderRelocatable.hpp @@ -31,6 +31,7 @@ #include #include #include +#include #ifndef S_ATTR_DEBUG #define S_ATTR_DEBUG 0x02000000 #endif @@ -99,7 +100,7 @@ public: 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 uint64_t getTargetOffset() const { return (int64_t)((int32_t)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(); } @@ -127,10 +128,13 @@ Reference::Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset : 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(); + // make reference a by-name unless: + // - the reference type is only used with direct references + // - the target is translation unit scoped + if ( (kind != A::kNoFixUp) && (kind != A::kFollowOn) + && (toTarget.atom->getScope() != ObjectFile::Atom::scopeTranslationUnit) ) { //fprintf(stderr, "Reference(): changing to by-name %p %s, target scope=%d\n", toTarget.atom, fToTargetName, toTarget.atom->getScope()); + fToTargetName = toTarget.atom->getName(); fToTarget.atom = NULL; } ((class BaseAtom*)at.atom)->addReference(this); @@ -181,15 +185,10 @@ private: template Segment::Segment(const macho_section* sect) - : fSection(sect), fWritable(false), fExecutable(false) + : fSection(sect), fWritable(true), 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 ) { + if ( strcmp(fSection->segname(), "__TEXT") == 0 ) { + fWritable = false; fExecutable = true; } else if ( strcmp(fSection->segname(), "__IMPORT") == 0 ) { @@ -245,8 +244,8 @@ public: 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 SymbolTableInclusion getSymbolTableInclusion() const { return fSymbolTableInclusion; } + virtual bool dontDeadStrip() const { return ((fSymbol->n_desc() & (N_NO_DEAD_STRIP|REFERENCED_DYNAMICALLY)) != 0); } 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); } @@ -287,6 +286,7 @@ protected: ReferenceVector fReferences; std::vector fLineInfo; ObjectFile::Atom::Scope fScope; + SymbolTableInclusion fSymbolTableInclusion; uint8_t fAlignment; }; @@ -306,9 +306,7 @@ SymbolAtom::SymbolAtom(Reader& owner, const macho_nlist

* symbol, const // 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); } @@ -347,14 +345,42 @@ SymbolAtom::SymbolAtom(Reader& owner, const macho_nlist

* symbol, const case S_8BYTE_LITERALS: setSize(8); break; + case S_16BYTE_LITERALS: + setSize(16); + break; case S_CSTRING_LITERALS: setSize(strlen((char*)(fOwner.fHeader) + section->offset() + fAddress - section->addr()) + 1); + break; case S_REGULAR: case S_ZEROFILL: case S_COALESCED: // size calculate later after next atom is found break; } + + // compute whether this atom needs to be in symbol table + if ( (fSymbol->n_desc() & REFERENCED_DYNAMICALLY) != 0) { + fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableInAndNeverStrip; + } + else if ( fOwner.fOptions.fForFinalLinkedImage + && ((section->flags() & SECTION_TYPE) == S_COALESCED) + && ((section->flags() & S_ATTR_NO_TOC) == S_ATTR_NO_TOC) + && ((section->flags() & S_ATTR_STRIP_STATIC_SYMS) == S_ATTR_STRIP_STATIC_SYMS) + && (strcmp(section->sectname(), "__eh_frame") == 0) ) { + // .eh symbols exist so the linker can associate them with functions + // removing them from final linked images is a big space savings rdar://problem/4180168 + fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableNotIn; + } + else if ( fOwner.fOptions.fForFinalLinkedImage + && ((section->flags() & SECTION_TYPE) == S_REGULAR) + && (strncmp(section->sectname(), "__gcc_except_tab", 16) == 0) + && (strncmp(this->getName(), "GCC_except_table", 16) == 0) ) { + // GCC_except_table* symbols don't need to exist in final linked image + fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableNotIn; + } + else { + fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn; + } } @@ -363,14 +389,23 @@ 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); + if ( fSection->flags() & S_ATTR_SOME_INSTRUCTIONS ) { + // For code, the aligment is based just on the section alignment and code address + if ( fAddress == 0 ) + fAlignment = fSection->align(); + else + fAlignment = std::min((uint8_t)__builtin_ctz(fAddress), (uint8_t)fSection->align()); + } + else { + // For data, 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); + } } @@ -476,6 +511,7 @@ public: virtual bool isZeroFill() const { return true; } virtual SymbolTableInclusion getSymbolTableInclusion() const { return ((fSymbol->n_desc() & REFERENCED_DYNAMICALLY) != 0) ? ObjectFile::Atom::kSymbolTableInAndNeverStrip : ObjectFile::Atom::kSymbolTableIn; } + virtual bool dontDeadStrip() const { return ((fSymbol->n_desc() & (N_NO_DEAD_STRIP|REFERENCED_DYNAMICALLY)) != 0); } virtual uint64_t getSize() const { return fSymbol->n_value(); } virtual std::vector& getReferences() const { return fgNoReferences; } virtual bool mustRemainInSection() const { return true; } @@ -488,8 +524,8 @@ public: 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 addReference(ObjectFile::Reference* ref) { throw "ld64: can't add references"; } + virtual void addLineInfo(const ObjectFile::LineInfo& info) { throw "ld64: can't add line info to tentative definition"; } virtual void alignAtLeast(uint8_t align) { } protected: @@ -568,7 +604,8 @@ public: 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 ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return fSymbolTableInclusion; } + virtual bool dontDeadStrip() const { return fDontDeadStrip; } virtual bool isZeroFill() const; virtual uint64_t getSize() const { return fSize; } virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } @@ -580,10 +617,10 @@ public: 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 setScope(ObjectFile::Atom::Scope newScope) { fScope = 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 addLineInfo(const ObjectFile::LineInfo& info) { fprintf(stderr, "ld64: can't add line info to anonymous symbol %s from %s\n", this->getDisplayName(), this->getFile()->getPath()); } virtual void alignAtLeast(uint8_t align) { } BaseAtom* redirectTo() { return fRedirect; } bool isWeakImportStub() { return fWeakImportStub; } @@ -609,14 +646,18 @@ protected: Segment* fSegment; ReferenceVector fReferences; BaseAtom* fRedirect; + bool fDontDeadStrip; bool fWeakImportStub; bool fReallyNonLazyPointer; // HACK until compiler stops emitting anonymous non-lazy pointers + ObjectFile::Atom::SymbolTableInclusion fSymbolTableInclusion; + ObjectFile::Atom::Scope fScope; }; 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) + : fOwner(owner), fSynthesizedName(NULL), fSection(section), fAddress(addr), fSize(size), fSegment(NULL), fDontDeadStrip(true), + fWeakImportStub(false), fReallyNonLazyPointer(false), fSymbolTableInclusion(ObjectFile::Atom::kSymbolTableNotIn), + fScope(ObjectFile::Atom::scopeTranslationUnit) { fSegment = new Segment(fSection); fRedirect = this; @@ -628,32 +669,62 @@ AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* sectio } 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 + if ( (strcmp(section->sectname(), "__class") == 0) && (strcmp(section->segname(), "__OBJC") == 0) && owner.fAppleObjc ) { + // special case ObjC classes to synthesize .objc_class_name_* symbols, for Apple runtime only + uint32_t classNameAddr = P::getP(*(pint_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr + 2*sizeof(pint_t) - section->addr())); + const char* str = (char*)(owner.fHeader) + section->offset() + classNameAddr - section->addr(); + asprintf((char**)&fSynthesizedName, ".objc_class_name_%s", str); + if ( fOwner.fOptions.fForFinalLinkedImage ) + fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn; + else + fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableInAsAbsolute; + fScope = ObjectFile::Atom::scopeGlobal; + } + else if ( strcmp(fSection->sectname(), "__cstring") == 0 ) { + // handle .o files created by old ld64 -r that are missing cstring section type + const char* str = (char*)(owner.fHeader) + section->offset() + addr - section->addr(); + asprintf((char**)&fSynthesizedName, "cstring=%s", str); + } + break; case S_CSTRING_LITERALS: { const char* str = (char*)(owner.fHeader) + section->offset() + addr - section->addr(); asprintf((char**)&fSynthesizedName, "cstring=%s", str); + fScope = ObjectFile::Atom::scopeLinkageUnit; + fDontDeadStrip = false; } 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); + fScope = ObjectFile::Atom::scopeLinkageUnit; + fDontDeadStrip = false; } 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); + fScope = ObjectFile::Atom::scopeLinkageUnit; + fDontDeadStrip = false; + } + break; + case S_16BYTE_LITERALS: + { + uint64_t value1 = E::get64(*(uint64_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr - section->addr())); + uint64_t value2 = E::get64(*(uint64_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr + 8 - section->addr())); + asprintf((char**)&fSynthesizedName, "16-byte-literal=0x%016llX,%016llX", value1, value2); + fScope = ObjectFile::Atom::scopeLinkageUnit; + fDontDeadStrip = false; } 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()); + uint32_t literalNameAddr = P::getP(*(pint_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr - section->addr())); + const char* str = (char*)(owner.fHeader) + section->offset() + literalNameAddr - section->addr(); + asprintf((char**)&fSynthesizedName, "literal-pointer@%s@%s@%s", section->segname(), section->sectname(), str); + fScope = ObjectFile::Atom::scopeLinkageUnit; } break; case S_MOD_INIT_FUNC_POINTERS: @@ -680,22 +751,27 @@ AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* sectio if ( staticAtom != NULL ) fRedirect = staticAtom; } + fScope = ObjectFile::Atom::scopeLinkageUnit; } break; case S_LAZY_SYMBOL_POINTERS: case S_NON_LAZY_SYMBOL_POINTERS: { + fDontDeadStrip = false; + fScope = ObjectFile::Atom::scopeLinkageUnit; 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))); + // All atoms not created yet, so we need to scan symbol table 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) ) { + if ( ((sym->n_type() & N_TYPE) == N_SECT) + && ((sym->n_type() & N_STAB) == 0) + && (sym->n_value() == nonLazyPtrValue) ) { const char* name = &fOwner.fStrings[sym->n_strx()]; char* str = new char[strlen(name)+16]; strcpy(str, name); @@ -703,10 +779,11 @@ AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* sectio fSynthesizedName = str; // add direct reference to target later, because its atom may not be constructed yet fOwner.fLocalNonLazys.push_back(this); + fScope = ObjectFile::Atom::scopeTranslationUnit; return; } } - throwf("malformed .o file: non-lazy-pointer with value 0x%08X missing symbol", nonLazyPtrValue); + throwf("malformed .o file: non-lazy-pointer at address 0x%08X with value 0x%0llX missing symbol", addr, (uint64_t)nonLazyPtrValue); } const macho_nlist

* targetSymbol = &fOwner.fSymbols[symbolIndex]; const char* name = &fOwner.fStrings[targetSymbol->n_strx()]; @@ -718,10 +795,17 @@ AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* sectio 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); + if ( (targetSymbol->n_type() & N_EXT) == 0 ) { + // target is translation unit scoped, so add direct reference to target + //fOwner.makeReference(A::kPointer, addr, targetSymbol->n_value()); + new Reference(A::kPointer, AtomAndOffset(this), fOwner.findAtomAndOffset(targetSymbol->n_value())); + } + else { + if ( fOwner.isWeakImportSymbol(targetSymbol) ) + new Reference(A::kPointerWeakImport, AtomAndOffset(this), name, 0); + else + new Reference(A::kPointer, AtomAndOffset(this), name, 0); + } } break; default: @@ -752,31 +836,24 @@ 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; - } + return ObjectFile::Atom::scopeTranslationUnit; + else + return fScope; } template ObjectFile::Atom::DefinitionKind AnonymousAtom::getDefinitionKind() const { if ( fReallyNonLazyPointer ) - return ObjectFile::Atom::kWeakDefinition; + return ObjectFile::Atom::kRegularDefinition; // 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_16BYTE_LITERALS: case S_NON_LAZY_SYMBOL_POINTERS: + case S_LITERAL_POINTERS: return ObjectFile::Atom::kWeakDefinition; default: return ObjectFile::Atom::kRegularDefinition; @@ -793,8 +870,6 @@ bool AnonymousAtom::isZeroFill() const 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); @@ -814,6 +889,8 @@ uint8_t AnonymousAtom::getAlignment() const return 2; case S_8BYTE_LITERALS: return 3; + case S_16BYTE_LITERALS: + return 4; case S_NON_LAZY_SYMBOL_POINTERS: return (uint8_t)log2(sizeof(pint_t)); default: @@ -906,7 +983,9 @@ private: 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); + Reference* makeReferenceToSymbol(Kinds kind, uint32_t atAddr, const macho_nlist

* toSymbol, uint32_t toOffset); void validSectionType(uint8_t type); + void handleAnonymousNonLazyPointers(const macho_section

* sect); BaseAtom* findAtomByName(const char*); @@ -923,6 +1002,7 @@ private: std::map fAddrToAtom; std::vector*> fLocalNonLazys; ObjectFile::Reader::DebugInfoKind fDebugInfo; + bool fHasUUID; const macho_section

* fDwarfDebugInfoSect; const macho_section

* fDwarfDebugAbbrevSect; const macho_section

* fDwarfDebugLineSect; @@ -930,15 +1010,56 @@ private: const char* fDwarfTranslationUnitFile; std::map fDwarfIndexToFile; std::vector fStabs; + bool fAppleObjc; }; +// usually do nothing +template void Reader::handleAnonymousNonLazyPointers(const macho_section

* sect) { } + +// HACK for ppc64, need to split of anonymous non-lazy-pointers because they must be 8-byte aligned to work with ld instruction +template <> void +Reader::handleAnonymousNonLazyPointers(const macho_section

* dataSect) { + if ( (dataSect->size() >= sizeof(pint_t)) + && (dataSect->align() >= log2(sizeof(pint_t))) + && (strcmp(dataSect->sectname(), "__data") == 0) + && (strcmp(dataSect->segname(), "__DATA") == 0) ) { + std::set lo14targets; + 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 ( strncmp(sect->sectname(), "__text", 6) == 0 ) { + 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 ) { + const macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)r; + if ( sreloc->r_type() == PPC_RELOC_LO14_SECTDIFF ) { + lo14targets.insert(sreloc->r_value()); + } + } + } + } + } + // walk backwards so that newly created anonymous atoms do not mask misalignmented + for (std::set::reverse_iterator it=lo14targets.rbegin(); it != lo14targets.rend(); it++) { + uint32_t targetOfLO14 = *it; + AtomAndOffset found = this->findAtomAndOffset(targetOfLO14); + if ( (found.offset & 0x7) != 0 ) { + AnonymousAtom* newAtom = new AnonymousAtom(*this, dataSect, targetOfLO14, sizeof(pint_t)); + newAtom->fReallyNonLazyPointer = true; + fAtoms.push_back(newAtom); + fAddrToAtom[targetOfLO14] = newAtom; + } + } + } +} 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) + fDebugInfo(kDebugInfoNone), fHasUUID(false), fDwarfDebugInfoSect(NULL), fDwarfDebugAbbrevSect(NULL), + fDwarfTranslationUnitDir(NULL), fDwarfTranslationUnitFile(NULL), fAppleObjc(false) { // sanity check if ( ! validFile(fileContent) ) @@ -949,6 +1070,8 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, 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; + uint32_t undefinedStartIndex = 0; + uint32_t undefinedEndIndex = 0; for (uint32_t i = 0; i < cmd_count; ++i) { switch (cmd->cmd()) { case LC_SYMTAB: @@ -963,11 +1086,12 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, { const macho_dysymtab_command

* dsymtab = (struct macho_dysymtab_command

*)cmd; fIndirectTable = (uint32_t*)((char*)fHeader + dsymtab->indirectsymoff()); + undefinedStartIndex = dsymtab->iundefsym(); + undefinedEndIndex = undefinedStartIndex + dsymtab->nundefsym(); } break; case LC_UUID: - if (getDebugInfoKind() != kDebugInfoDwarf) - fDebugInfo = kDebugInfoStabsUUID; + fHasUUID = true; break; default: @@ -1004,6 +1128,7 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, case S_COALESCED: case S_4BYTE_LITERALS: case S_8BYTE_LITERALS: + case S_16BYTE_LITERALS: case S_CSTRING_LITERALS: { BaseAtom* newAtom = new SymbolAtom(*this, &sym, section); @@ -1037,6 +1162,9 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, else if ( (type == N_UNDF) && (sym.n_value() != 0) ) { fAtoms.push_back(new TentativeAtom(*this, &sym)); } + else if ( (type == N_ABS) && (strncmp(&fStrings[sym.n_strx()], ".objc_class_name_", 16) == 0) ) { + fAppleObjc = true; + } } } @@ -1073,6 +1201,17 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, case S_8BYTE_LITERALS: atomSize = 8; break; + case S_16BYTE_LITERALS: + atomSize = 16; + break; + case S_REGULAR: + // special case ObjC classes to synthesize .objc_class_name_* symbols + if ( (strcmp(sect->sectname(), "__class") == 0) && (strcmp(sect->segname(), "__OBJC") == 0) && fAppleObjc ) { + // gcc sometimes over aligns class structure + uint32_t align = 1 << sect->align(); + atomSize = ((12 * sizeof(pint_t)) + align-1) & (-align); + } + break; } if ( atomSize != 0 ) { for(uint32_t sectOffset=0; sectOffset < sect->size(); sectOffset += atomSize) { @@ -1143,46 +1282,12 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, 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 + // HACK until compiler stops generated anonymous non-lazy pointers rdar://problem/4513414 + handleAnonymousNonLazyPointers(sect); + // if there is not an atom already at the start of this section, add an anonymous one 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; @@ -1241,6 +1346,49 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, } } + // check of object file that defines no classes, but uses classes + if ( !fAppleObjc ) { + for (uint32_t i=undefinedStartIndex; i < undefinedEndIndex; ++i) { + const macho_nlist

& sym = fSymbols[i]; + if ( (sym.n_type() & N_STAB) == 0 ) { + if ( ((sym.n_type() & N_TYPE) == N_UNDF) && (strncmp(&fStrings[sym.n_strx()], ".objc_class_name_", 16) == 0) ) { + fAppleObjc = true; + break; + } + } + } + } + + // add objective-c references + if ( fAppleObjc ) { + 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 ( (strcmp(sect->sectname(), "__class") == 0) && (strcmp(sect->segname(), "__OBJC") == 0) ) { + // gcc sometimes over aligns class structure + uint32_t align = 1 << sect->align(); + uint32_t classSize = ((12 * sizeof(pint_t)) + align-1) & (-align); + for (uint32_t offset = 0; offset < sect->size(); offset += classSize) { + // add by-name reference to super class + uint32_t superClassNameAddr = P::getP(*(pint_t*)(((uint8_t*)fHeader) + sect->offset() + offset + sizeof(pint_t))); + const char* superStr = (char*)(fHeader) + sect->offset() + superClassNameAddr - sect->addr(); + const char* superClassName; + asprintf((char**)&superClassName, ".objc_class_name_%s", superStr); + makeByNameReference(A::kNoFixUp, sect->addr()+offset+sizeof(pint_t), superClassName, 0); + } + } + else if ( (strcmp(sect->sectname(), "__cls_refs") == 0) && (strcmp(sect->segname(), "__OBJC") == 0) ) { + for (uint32_t offset = 0; offset < sect->size(); offset += sizeof(pint_t)) { + // scan through __cls_refs and add by-name reference for each required class + uint32_t classNameAddr = P::getP(*(pint_t*)(((uint8_t*)fHeader) + sect->offset() + offset)); + const char* classStr = (char*)(fHeader) + sect->offset() + classNameAddr - sect->addr(); + const char* className; + asprintf((char**)&className, ".objc_class_name_%s", classStr); + makeByNameReference(A::kNoFixUp, sect->addr()+offset, className, 0); + } + } + } + } + // 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; @@ -1273,12 +1421,15 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, // 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; + // compiler sometimes emits emtpty dwarf sections when there is no debug info, skip those + if ( (fDwarfDebugInfoSect != NULL) && (fDwarfDebugInfoSect->size() != 0) ) { + 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; + } } } @@ -1297,9 +1448,13 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, uint32_t curAtomOffset = 0; uint32_t curAtomAddress = 0; uint32_t curAtomSize = 0; - while ( line_next (lines, &result, line_stop_line) ) { + while ( line_next (lines, &result, line_stop_pc) ) { // for performance, see if in next pc is in current atom - if ( (curAtom != NULL) && (result.pc <= curAtomAddress+curAtomSize) && (curAtomAddress <= result.pc) ) { + if ( (curAtom != NULL) && (curAtomAddress <= result.pc) && (result.pc < (curAtomAddress+curAtomSize)) ) { + curAtomOffset = result.pc - curAtomAddress; + } + // or pc at end of current atom + else if ( result.end_of_sequence && (curAtom != NULL) && (result.pc == (curAtomAddress+curAtomSize)) ) { curAtomOffset = result.pc - curAtomAddress; } else { @@ -1309,7 +1464,7 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, if ( curAtom == NULL ) break; // file has line info but no functions curAtomOffset = ao.offset; - curAtomAddress = result.pc; + curAtomAddress = result.pc - ao.offset; curAtomSize = curAtom->getSize(); } const char* filename; @@ -1325,8 +1480,12 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, 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); + //fprintf(stderr, "addr=0x%08llX, line=%lld, file=%s, atom=%s, atom.size=0x%X, end=%d\n", + // result.pc, result.line, filename, curAtom->getDisplayName(), curAtomSize, result.end_of_sequence); ((BaseAtom*)curAtom)->addLineInfo(info); + if ( result.end_of_sequence ) { + curAtom = NULL; + } } line_free(lines); } @@ -1346,10 +1505,11 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, enum { start, inBeginEnd, inFun } state = start; for (uint32_t symbolIndex = 0; symbolIndex < fSymbolCount; ++symbolIndex ) { const macho_nlist

* sym = &fSymbols[symbolIndex]; + bool useStab = true; 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; + fDebugInfo = (fHasUUID ? kDebugInfoStabsUUID : kDebugInfoStabs); Stab stab; stab.atom = NULL; stab.type = type; @@ -1403,6 +1563,7 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, } if ( stab.atom == NULL ) { fprintf(stderr, "can't find atom for N_GSYM stabs %s in %s\n", symString, path); + useStab = false; } break; case N_FUN: @@ -1506,7 +1667,8 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, break; } // add to list of stabs for this .o file - fStabs.push_back(stab); + if ( useStab ) + fStabs.push_back(stab); } } } @@ -1525,6 +1687,18 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, #endif } +template <> +void Reader::validSectionType(uint8_t type) +{ + switch ( type ) { + case S_SYMBOL_STUBS: + throw "symbol_stub sections not valid in x86_64 object files"; + case S_LAZY_SYMBOL_POINTERS: + throw "lazy pointer sections not valid in x86_64 object files"; + case S_NON_LAZY_SYMBOL_POINTERS: + throw "non lazy pointer sections not valid in x86_64 object files"; + } +} template void Reader::validSectionType(uint8_t type) @@ -1600,10 +1774,51 @@ Reference* Reader::makeReferenceToEH(const char* ehName, pint_t ehAtomAddr 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) ; + return makeReference(A::kNoFixUp, funcAddr, ehAtomAddress); } +template <> +Reference* Reader::makeByNameReference(Kinds kind, uint32_t atAddr, const char* toName, uint32_t toOffset) +{ + // x86_64 uses external relocations everywhere, so external relocations do not imply by-name references + // instead check scope of target + BaseAtom* target = findAtomByName(toName); + if ( (target != NULL) && (target->getScope() == ObjectFile::Atom::scopeTranslationUnit) ) + return new Reference(kind, findAtomAndOffset(atAddr), AtomAndOffset(target, toOffset)); + else + return new Reference(kind, findAtomAndOffset(atAddr), toName, toOffset); +} + +template <> +Reference* Reader::makeReferenceToSymbol(Kinds kind, uint32_t atAddr, const macho_nlist

* toSymbol, uint32_t toOffset) +{ + // x86_64 uses external relocations everywhere, so external relocations do not imply by-name references + // instead check scope of target + if ( ((toSymbol->n_type() & N_TYPE) == N_SECT) && ((toSymbol->n_type() & N_EXT) == 0) ) + return new Reference(kind, findAtomAndOffset(atAddr), findAtomAndOffset(toSymbol->n_value(), toSymbol->n_value()+toOffset)); + else + return new Reference(kind, findAtomAndOffset(atAddr), &fStrings[toSymbol->n_strx()], 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 + // for x86_64 the __eh_frame section contains the addends, so need to use relocs to find target + uint32_t ehAtomDeltaSectionOffset = ehAtomAddress + 8 - ehSect->addr(); // offset 8 in eh info is delta to function + const macho_relocation_info

* relocs = (macho_relocation_info

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

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

* reloc = relocs; reloc < relocsEnd; ++reloc) { + if ( (reloc->r_address() == ehAtomDeltaSectionOffset) && (reloc->r_type() == X86_64_RELOC_UNSIGNED) ) { + uint32_t funcAddr = fSymbols[reloc->r_symbolnum()].n_value(); + return makeReference(x86_64::kNoFixUp, funcAddr, ehAtomAddress); + } + } + fprintf(stderr, "ld64: warning, can't find matching function for eh symbol %s\n", ehName); + return NULL; +} template AtomAndOffset Reader::findAtomAndOffset(uint32_t addr) @@ -1955,6 +2170,18 @@ bool Reader::validFile(const uint8_t* fileContent) 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_X86_64 ) + return false; + if ( header->filetype() != MH_OBJECT ) + return false; + return true; +} template @@ -2087,7 +2314,7 @@ bool Reader::addRelocReference_powerpc(const macho_section* se Reference* ref = makeReference(A::kAbsLow14, srcAddr, dstAddr); BaseAtom* target = ((BaseAtom*)&(ref->getTarget())); if ( target != NULL ) - target->alignAtLeast(2); + target->alignAtLeast(3); } } break; @@ -2236,7 +2463,7 @@ bool Reader::addRelocReference_powerpc(const macho_section* se 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); + target->alignAtLeast(3); } break; case PPC_RELOC_HA16_SECTDIFF: @@ -2350,6 +2577,9 @@ bool Reader::addRelocReference(const macho_section* sect, const mac kind = x86::kPCRel32; pointerValue += srcAddr + sizeof(uint32_t); } + else if ( strcmp(sect->segname(), "__TEXT") == 0 ) { + kind = x86::kAbsolute32; + } else { kind = x86::kPointer; } @@ -2411,7 +2641,10 @@ bool Reader::addRelocReference(const macho_section* sect, const mac makeReferenceWithToBase(x86::kPCRel32, srcAddr, betterDstAddr, dstAddr); } else { - makeReferenceWithToBase(x86::kPointer, srcAddr, betterDstAddr, dstAddr); + if ( strcmp(sect->segname(), "__TEXT") == 0 ) + makeReferenceWithToBase(x86::kAbsolute32, srcAddr, betterDstAddr, dstAddr); + else + makeReferenceWithToBase(x86::kPointer, srcAddr, betterDstAddr, dstAddr); } break; case GENERIC_RELOC_SECTDIFF: @@ -2437,12 +2670,177 @@ bool Reader::addRelocReference(const macho_section* sect, const mac return result; } +template <> +bool Reader::addRelocReference(const macho_section* sect, const macho_relocation_info* reloc) +{ + uint64_t srcAddr; + uint64_t dstAddr = 0; + uint64_t addend; + uint32_t* fixUpPtr; + x86_64::ReferenceKinds kind; + bool result = false; + const macho_nlist

* targetSymbol = NULL; + const char* targetName = NULL; + srcAddr = sect->addr() + reloc->r_address(); + fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address()); + //fprintf(stderr, "addReloc type=%d\n", reloc->r_type()); + if ( reloc->r_extern() ) { + targetSymbol = &fSymbols[reloc->r_symbolnum()]; + targetName = &fStrings[targetSymbol->n_strx()]; + } + switch ( reloc->r_type() ) { + case X86_64_RELOC_UNSIGNED: + if ( reloc->r_pcrel() ) + throw "pcrel and X86_64_RELOC_UNSIGNED not supported"; + if ( reloc->r_length() != 3 ) + throw "length < 3 and X86_64_RELOC_UNSIGNED not supported"; + dstAddr = E::get64(*((uint64_t*)fixUpPtr)); + if ( reloc->r_extern() ) + makeReferenceToSymbol(x86_64::kPointer, srcAddr, targetSymbol, dstAddr); + else + makeReference(x86_64::kPointer, srcAddr, dstAddr); + break; + case X86_64_RELOC_SIGNED: + if ( ! reloc->r_pcrel() ) + throw "not pcrel and X86_64_RELOC_SIGNED not supported"; + if ( reloc->r_length() != 2 ) + throw "length != 2 and X86_64_RELOC_SIGNED not supported"; + kind = x86_64::kPCRel32; + dstAddr = (int64_t)((int32_t)(E::get32(*fixUpPtr))); + if ( dstAddr == (uint64_t)(-1) ) { + dstAddr = 0; + kind = x86_64::kPCRel32_1; + } + else if ( dstAddr == (uint64_t)(-2) ) { + dstAddr = 0; + kind = x86_64::kPCRel32_2; + } + else if ( dstAddr == (uint64_t)(-4) ) { + dstAddr = 0; + kind = x86_64::kPCRel32_4; + } + if ( reloc->r_extern() ) + makeReferenceToSymbol(kind, srcAddr, targetSymbol, dstAddr); + else { + makeReference(kind, srcAddr, srcAddr+4+dstAddr); + } + break; + case X86_64_RELOC_BRANCH: + if ( ! reloc->r_pcrel() ) + throw "not pcrel and X86_64_RELOC_BRANCH not supported"; + if ( reloc->r_length() != 2 ) + throw "length != 2 and X86_64_RELOC_BRANCH not supported"; + dstAddr = (int64_t)((int32_t)(E::get32(*fixUpPtr))); + if ( reloc->r_extern() ) { + if ( isWeakImportSymbol(targetSymbol) ) + makeReferenceToSymbol(x86_64::kBranchPCRel32WeakImport, srcAddr, targetSymbol, dstAddr); + else + makeReferenceToSymbol(x86_64::kBranchPCRel32, srcAddr, targetSymbol, dstAddr); + } + else { + makeReference(x86_64::kBranchPCRel32, srcAddr, srcAddr+4+dstAddr); + } + break; + case X86_64_RELOC_GOT: + if ( ! reloc->r_extern() ) + throw "not extern and X86_64_RELOC_GOT not supported"; + if ( ! reloc->r_pcrel() ) + throw "not pcrel and X86_64_RELOC_GOT not supported"; + if ( reloc->r_length() != 2 ) + throw "length != 2 and X86_64_RELOC_GOT not supported"; + addend = (int64_t)((int32_t)(E::get32(*fixUpPtr))); + if ( isWeakImportSymbol(targetSymbol) ) + makeReferenceToSymbol(x86_64::kPCRel32GOTWeakImport, srcAddr, targetSymbol, addend); + else + makeReferenceToSymbol(x86_64::kPCRel32GOT, srcAddr, targetSymbol, addend); + break; + case X86_64_RELOC_GOT_LOAD: + if ( ! reloc->r_extern() ) + throw "not extern and X86_64_RELOC_GOT_LOAD not supported"; + if ( ! reloc->r_pcrel() ) + throw "not pcrel and X86_64_RELOC_GOT_LOAD not supported"; + if ( reloc->r_length() != 2 ) + throw "length != 2 and X86_64_RELOC_GOT_LOAD not supported"; + addend = (int64_t)((int32_t)(E::get32(*fixUpPtr))); + if ( isWeakImportSymbol(targetSymbol) ) + makeReferenceToSymbol(x86_64::kPCRel32GOTLoadWeakImport, srcAddr, targetSymbol, addend); + else + makeReferenceToSymbol(x86_64::kPCRel32GOTLoad, srcAddr, targetSymbol, addend); + break; + case X86_64_RELOC_SUBTRACTOR: + if ( reloc->r_pcrel() ) + throw "X86_64_RELOC_SUBTRACTOR cannot be pc-relative"; + if ( reloc->r_length() < 2 ) + throw "X86_64_RELOC_SUBTRACTOR must have r_length of 2 or 3"; + if ( !reloc->r_extern() ) + throw "X86_64_RELOC_SUBTRACTOR must have r_extern=1"; + const macho_relocation_info* nextReloc = &reloc[1]; + if ( nextReloc->r_type() != X86_64_RELOC_UNSIGNED ) + throw "X86_64_RELOC_SUBTRACTOR must be followed by X86_64_RELOC_UNSIGNED"; + result = true; + if ( nextReloc->r_pcrel() ) + throw "X86_64_RELOC_UNSIGNED following a X86_64_RELOC_SUBTRACTOR cannot be pc-relative"; + if ( nextReloc->r_length() != reloc->r_length() ) + throw "X86_64_RELOC_UNSIGNED following a X86_64_RELOC_SUBTRACTOR must have same r_length"; + Reference* ref; + bool negativeAddend; + if ( reloc->r_length() == 2 ) { + kind = x86_64::kPointerDiff32; + dstAddr = E::get32(*fixUpPtr); // addend is in content + negativeAddend = ((dstAddr & 0x80000000) != 0); + } + else { + kind = x86_64::kPointerDiff; + dstAddr = E::get64(*((uint64_t*)fixUpPtr)); // addend is in content + negativeAddend = ((dstAddr & 0x8000000000000000ULL) != 0); + } + ObjectFile::Atom* inAtom = this->findAtomAndOffset(srcAddr).atom; + // create reference with "to" target + if ( nextReloc->r_extern() ) { + const macho_nlist

* targetSymbol = &fSymbols[nextReloc->r_symbolnum()]; + const char* targetName = &fStrings[targetSymbol->n_strx()]; + ref = makeReferenceToSymbol(kind, srcAddr, targetSymbol, 0); + // if "to" is in this atom, change by-name to a direct reference + if ( strcmp(targetName, inAtom->getName()) == 0 ) + ref->setTarget(*inAtom, 0); + } + else { + ref = makeReference(kind, srcAddr, dstAddr); + } + // add in "from" target + if ( reloc->r_extern() ) { + const macho_nlist

* targetFromSymbol = &fSymbols[reloc->r_symbolnum()]; + const char* fromTargetName = &fStrings[targetFromSymbol->n_strx()]; + if ( (targetFromSymbol->n_type() & N_EXT) == 0 ) { + // from target is translation unit scoped, so use a direct reference + ref->setFromTarget(*(findAtomAndOffset(targetSymbol->n_value()).atom)); + } + else if ( strcmp(fromTargetName, inAtom->getName()) == 0 ) { + // if "from" is in this atom, change by-name to a direct reference + ref->setFromTarget(*inAtom); + } + else { + // some non-static other atom + ref->setFromTargetName(fromTargetName); + } + } + // addend goes in from side iff negative + if ( negativeAddend ) + ref->setFromTargetOffset(-dstAddr); + else + ref->setToTargetOffset(dstAddr); + break; + default: + fprintf(stderr, "unknown relocation type %d\n", reloc->r_type()); + } + return result; +} template <> const char* Reference::getDescription() const { - static char temp[1024]; + static char temp[2048]; switch( fKind ) { case x86::kNoFixUp: sprintf(temp, "reference to "); @@ -2473,6 +2871,9 @@ const char* Reference::getDescription() const case x86::kPCRel32: sprintf(temp, "offset 0x%04X, rel32 reference to ", fFixUpOffsetInSrc); break; + case x86::kAbsolute32: + sprintf(temp, "offset 0x%04X, absolute32 reference to ", fFixUpOffsetInSrc); + break; } // always quote by-name references if ( fToTargetName != NULL ) { @@ -2496,7 +2897,7 @@ const char* Reference::getDescription() const template <> const char* Reference::getDescription() const { - static char temp[1024]; + static char temp[2048]; switch( fKind ) { case ppc::kNoFixUp: sprintf(temp, "reference to "); @@ -2573,7 +2974,7 @@ const char* Reference::getDescription() const template <> const char* Reference::getDescription() const { - static char temp[1024]; + static char temp[2048]; switch( fKind ) { case ppc64::kNoFixUp: sprintf(temp, "reference to "); @@ -2649,12 +3050,90 @@ const char* Reference::getDescription() const strcat(temp, "NULL target"); } if ( fToTarget.offset != 0 ) - sprintf(&temp[strlen(temp)], " plus 0x%08X", fToTarget.offset); + sprintf(&temp[strlen(temp)], " plus 0x%llX", this->getTargetOffset()); return temp; } +template <> +const char* Reference::getDescription() const +{ + static char temp[2048]; + switch( fKind ) { + case x86_64::kNoFixUp: + sprintf(temp, "reference to "); + break; + case x86_64::kFollowOn: + sprintf(temp, "followed by "); + break; + case x86_64::kPointerWeakImport: + sprintf(temp, "offset 0x%04llX, weak import pointer to ", fFixUpOffsetInSrc); + break; + case x86_64::kPointer: + sprintf(temp, "offset 0x%04llX, pointer to ", fFixUpOffsetInSrc); + break; + case x86_64::kPointerDiff32: + case x86_64::kPointerDiff: + { + // by-name references have quoted names + const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; + const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; + const char* size = (fKind == x86_64::kPointerDiff32) ? "32-bit" : "64-bit"; + sprintf(temp, "offset 0x%04llX, %s pointer difference: (&%s%s%s + 0x%08X) - (&%s%s%s + 0x%08X)", + fFixUpOffsetInSrc, size, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + return temp; + } + break; + case x86_64::kPCRel32: + sprintf(temp, "offset 0x%04llX, rel32 reference to ", fFixUpOffsetInSrc); + break; + case x86_64::kPCRel32_1: + sprintf(temp, "offset 0x%04llX, rel32-1 reference to ", fFixUpOffsetInSrc); + break; + case x86_64::kPCRel32_2: + sprintf(temp, "offset 0x%04llX, rel32-2 reference to ", fFixUpOffsetInSrc); + break; + case x86_64::kPCRel32_4: + sprintf(temp, "offset 0x%04llX, rel32-4 reference to ", fFixUpOffsetInSrc); + break; + case x86_64::kBranchPCRel32: + sprintf(temp, "offset 0x%04llX, branch rel32 reference to ", fFixUpOffsetInSrc); + break; + case x86_64::kBranchPCRel32WeakImport: + sprintf(temp, "offset 0x%04llX, branch rel32 reference to weak imported ", fFixUpOffsetInSrc); + break; + case x86_64::kPCRel32GOT: + sprintf(temp, "offset 0x%04llX, rel32 reference to GOT entry for ", fFixUpOffsetInSrc); + break; + case x86_64::kPCRel32GOTWeakImport: + sprintf(temp, "offset 0x%04llX, rel32 reference to GOT entry for weak imported ", fFixUpOffsetInSrc); + break; + case x86_64::kPCRel32GOTLoad: + sprintf(temp, "offset 0x%04llX, rel32 reference to GOT entry for ", fFixUpOffsetInSrc); + break; + case x86_64::kPCRel32GOTLoadWeakImport: + sprintf(temp, "offset 0x%04llX, rel32 reference to GOT entry for weak imported ", 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%llX", this->getTargetOffset()); + + return temp; +} }; // namespace relocatable diff --git a/src/MachOWriterExecutable.hpp b/src/MachOWriterExecutable.hpp index 30aa24b..70798a6 100644 --- a/src/MachOWriterExecutable.hpp +++ b/src/MachOWriterExecutable.hpp @@ -189,6 +189,7 @@ private: void adjustLinkEditSections(); void buildObjectFileFixups(); void buildExecutableFixups(); + uint64_t relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const; bool referenceRequiresRuntimeFixUp(const ObjectFile::Reference* ref, bool slideable) const; void fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const; void fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const; @@ -207,7 +208,9 @@ private: 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); + bool illegalRelocInFinalLinkedImage(const ObjectFile::Reference&, bool slideable); + bool mightNeedPadSegment(); + void scanForAbsoluteReferences(); struct DirectLibrary { @@ -253,6 +256,7 @@ private: class UUIDLoadCommandAtom* fUUIDAtom; std::vector fWriterSynthesizedAtoms; std::vector fSegmentInfos; + class SegmentInfo* fPadSegmentInfo; class ObjectFile::Atom* fEntryPoint; class ObjectFile::Atom* fDyldHelper; std::vector fDirectLibraries; @@ -266,6 +270,7 @@ private: class SymbolTableLinkEditAtom* fSymbolTableAtom; class IndirectTableLinkEditAtom* fIndirectTableAtom; class StringsLinkEditAtom* fStringsAtom; + class PageZeroAtom* fPageZeroAtom; macho_nlist

* fSymbolTable; std::vector > fSectionRelocs; std::vector > fInternalRelocs; @@ -290,7 +295,9 @@ private: bool fHasWeakExports; bool fReferencesWeakImports; bool fSeenFollowOnReferences; + bool fWritableSegmentPastFirst4GB; std::map fWeakImportMap; + SegmentInfo* fFirstWritableSegment; }; @@ -311,6 +318,7 @@ public: static Segment fgStackSegment; static Segment fgImportSegment; static Segment fgDataSegment; + private: const char* fName; const bool fReadable; @@ -332,7 +340,7 @@ 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(); } + WriterAtom(Writer& writer, Segment& segment) : fWriter(writer), fSegment(segment) { } virtual ObjectFile::Reader* getFile() const { return &fWriter; } virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; } @@ -341,6 +349,7 @@ public: virtual Scope getScope() const { return ObjectFile::Atom::scopeTranslationUnit; } virtual DefinitionKind getDefinitionKind() const { return kRegularDefinition; } virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } + virtual bool dontDeadStrip() const { return true; } virtual bool isZeroFill() const { return false; } virtual std::vector& getReferences() const { return fgEmptyReferenceList; } virtual bool mustRemainInSection() const { return true; } @@ -371,17 +380,21 @@ template class PageZeroAtom : public WriterAtom { public: - PageZeroAtom(Writer& writer) : WriterAtom(writer, Segment::fgPageZeroSegment) {} + PageZeroAtom(Writer& writer) : WriterAtom(writer, Segment::fgPageZeroSegment), + fSize(fWriter.fOptions.zeroPageSize()) {} virtual const char* getDisplayName() const { return "page zero content"; } virtual bool isZeroFill() const { return true; } - virtual uint64_t getSize() const { return fWriter.fOptions.zeroPageSize(); } + virtual uint64_t getSize() const { return fSize; } virtual const char* getSectionName() const { return "._zeropage"; } virtual uint8_t getAlignment() const { return 12; } + void setSize(uint64_t size) { fSize = size; } private: using WriterAtom::fWriter; typedef typename A::P P; + uint64_t fSize; }; + template class DsoHandleAtom : public WriterAtom { @@ -457,7 +470,6 @@ public: void computeSize(); void setup(); unsigned int commandCount() { return fCommandCount; } - void assignFileOffsets(); private: using WriterAtom::fWriter; typedef typename A::P P; @@ -465,6 +477,7 @@ private: uint32_t fSize; }; + template class SymbolTableLoadCommandsAtom : public LoadCommandAtom { @@ -858,6 +871,26 @@ private: std::vector fReferences; }; +template +class StubHelperAtom : public WriterAtom +{ +public: + StubHelperAtom(Writer& writer, ObjectFile::Atom& target, ObjectFile::Atom& lazyPointer); + virtual const char* getName() const { return fName; } + virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual uint8_t getAlignment() const { return 2; } + virtual uint64_t getSize() const; + virtual const char* getSectionName() const { return "__stub_helper"; } + virtual std::vector& 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); + using WriterAtom::fWriter; + const char* fName; + ObjectFile::Atom& fTarget; + std::vector fReferences; +}; template class LazyPointerAtom : public WriterAtom @@ -956,9 +989,9 @@ struct ExportSorter 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), + fLoadCommandsSegment(NULL), fPadSegmentInfo(NULL), fPageZeroAtom(NULL), fLargestAtomSize(1), fEmitVirtualSections(false), fHasWeakExports(false), fReferencesWeakImports(false), - fSeenFollowOnReferences(false) + fSeenFollowOnReferences(false), fWritableSegmentPastFirst4GB(false), fFirstWritableSegment(NULL) { int permissions = 0777; if ( fOptions.outputKind() == Options::kObjectFile ) @@ -975,7 +1008,7 @@ Writer::Writer(const char* path, Options& options, std::vector(*this)); + fWriterSynthesizedAtoms.push_back(fPageZeroAtom = new PageZeroAtom(*this)); if ( fOptions.outputKind() == Options::kDynamicExecutable ) fWriterSynthesizedAtoms.push_back(new DsoHandleAtom(*this)); fWriterSynthesizedAtoms.push_back(new MachHeaderAtom(*this)); @@ -1045,7 +1078,10 @@ Writer::Writer(const char* path, Options& options, std::vector::Writer(const char* path, Options& options, std::vectorgetInstallPath(); if ( seenDylibInfo.options.fInstallPathOverride != NULL ) seenDylibInstallPath = dylibInfo.options.fInstallPathOverride; @@ -1148,6 +1184,11 @@ Writer::~Writer() } +// for ppc64, -mdynamic-no-pic only works in low 2GB, so we might need to split the zeropage into two segments +template <>bool Writer::mightNeedPadSegment() { return (fOptions.zeroPageSize() >= 0x80000000ULL); } +template bool Writer::mightNeedPadSegment() { return false; } + + template ObjectFile::Atom* Writer::getUndefinedProxyAtom(const char* name) { @@ -1189,31 +1230,41 @@ uint64_t Writer::write(std::vector& atoms, fEntryPoint = entryPointAtom; fDyldHelper = dyldHelperAtom; - // Set for create UUID - if (createUUID) - fUUIDAtom->emit(); + try { + // Set for create UUID + if (createUUID) + fUUIDAtom->emit(); - // create inter-library stubs - synthesizeStubs(); + // check for mdynamic-no-pic codegen which force code into low 4GB + scanForAbsoluteReferences(); - // create SegmentInfo and SectionInfo objects and assign all atoms to a section - partitionIntoSections(); + // create inter-library stubs + synthesizeStubs(); - // segment load command can now be sized and padding can be set - adjustLoadCommandsAndPadding(); + // create SegmentInfo and SectionInfo objects and assign all atoms to a section + partitionIntoSections(); - // assign each section a file offset - assignFileOffsets(); + // segment load command can now be sized and padding can be set + adjustLoadCommandsAndPadding(); - // if need to add branch islands, reassign file offsets - if ( addBranchIslands() ) + // assign each section a file offset assignFileOffsets(); - // build symbol table and relocations - buildLinkEdit(); + // if need to add branch islands, reassign file offsets + if ( addBranchIslands() ) + assignFileOffsets(); - // write everything - return writeAtoms(); + // build symbol table and relocations + buildLinkEdit(); + + // write everything + return writeAtoms(); + } catch (...) { + // clean up if any errors + close(fFileDescriptor); + (void)unlink(fFilePath); + throw; + } } template @@ -1239,12 +1290,17 @@ 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); + if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAsAbsolute ) { + entry->set_n_type(N_EXT | N_ABS); } - + else { + entry->set_n_type(N_EXT | N_SECT); + if ( (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit) && (fOptions.outputKind() == Options::kObjectFile) ) { + if ( fOptions.keepPrivateExterns() ) + entry->set_n_type(N_EXT | N_SECT | N_PEXT); + } + } + // set n_sect (section number of implementation ) uint8_t sectionIndex = atom->getSection()->getIndex(); entry->set_n_sect(sectionIndex); @@ -1416,7 +1472,7 @@ void Writer::collectExportedAndImportedAndLocalAtoms() fImportedAtoms.push_back(atom); break; case ObjectFile::Atom::kTentativeDefinition: - if ( fOptions.outputKind() == Options::kObjectFile ) { + if ( (fOptions.outputKind() == Options::kObjectFile) && !fOptions.makeTentativeDefinitionsReal() ) { fImportedAtoms.push_back(atom); break; } @@ -1445,7 +1501,7 @@ uint64_t Writer::valueForStab(const ObjectFile::Reader::Stab& stab) { switch ( stab.type ) { case N_FUN: - if ( stab.other == 0 ) { + if ( (stab.string == NULL) || (strlen(stab.string) == 0) ) { // end of function N_FUN has size return stab.atom->getSize(); } @@ -1470,7 +1526,20 @@ uint64_t Writer::valueForStab(const ObjectFile::Reader::Stab& stab) case N_ENSYM: return stab.atom->getSize(); case N_SO: - return 0; + if ( stab.atom == NULL ) { + return 0; + } + else { + if ( (stab.string == NULL) || (strlen(stab.string) == 0) ) { + // end of translation unit N_SO has address of end of last atom + return getAtomLoadAddress(stab.atom) + stab.atom->getSize(); + } + else { + // start of translation unit N_SO has address of end of first atom + return getAtomLoadAddress(stab.atom); + } + } + break; default: return stab.value; } @@ -1505,7 +1574,10 @@ uint32_t Writer::stringOffsetForStab(const ObjectFile::Reader::Stab& stab) template uint8_t Writer::sectionIndexForStab(const ObjectFile::Reader::Stab& stab) { - if ( stab.atom != NULL ) + // in FUN stabs, n_sect field is 0 for start FUN and 1 for end FUN + if ( stab.type == N_FUN ) + return stab.other; + else if ( stab.atom != NULL ) return stab.atom->getSection()->getIndex(); else return stab.other; @@ -1571,6 +1643,104 @@ void Writer::buildFixups() } } +template <> +uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) +{ + ObjectFile::Atom& target = ref->getTarget(); + bool external = (target.getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn); + uint32_t symbolIndex = external ? this->symbolIndex(target) : target.getSection()->getIndex(); + uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset(); + macho_relocation_info

reloc1; + macho_relocation_info

reloc2; + x86_64::ReferenceKinds kind = (x86_64::ReferenceKinds)ref->getKind(); + + switch ( kind ) { + case x86_64::kNoFixUp: + case x86_64::kFollowOn: + return 0; + + case x86_64::kPointer: + case x86_64::kPointerWeakImport: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolIndex); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(3); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_UNSIGNED); + fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + return 1; + + case x86_64::kPointerDiff32: + case x86_64::kPointerDiff: + { + ObjectFile::Atom& fromTarget = ref->getFromTarget(); + bool fromExternal = (fromTarget.getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn); + uint32_t fromSymbolIndex = fromExternal ? this->symbolIndex(fromTarget) : fromTarget.getSection()->getIndex(); + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolIndex); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(kind==x86_64::kPointerDiff32 ? 2 : 3); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_UNSIGNED); + reloc2.set_r_address(address); + reloc2.set_r_symbolnum(fromSymbolIndex); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(kind==x86_64::kPointerDiff32 ? 2 : 3); + reloc2.set_r_extern(fromExternal); + reloc2.set_r_type(X86_64_RELOC_SUBTRACTOR); + fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + fSectionRelocs.insert(fSectionRelocs.begin(), reloc2); + return 2; + } + + case x86_64::kBranchPCRel32: + case x86_64::kBranchPCRel32WeakImport: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolIndex); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_BRANCH); + fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + return 1; + + case x86_64::kPCRel32: + case x86_64::kPCRel32_1: + case x86_64::kPCRel32_2: + case x86_64::kPCRel32_4: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolIndex); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_SIGNED); + fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + return 1; + + case x86_64::kPCRel32GOT: + case x86_64::kPCRel32GOTWeakImport: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolIndex); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_GOT); + fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + return 1; + + case x86_64::kPCRel32GOTLoad: + case x86_64::kPCRel32GOTLoadWeakImport: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolIndex); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_GOT_LOAD); + fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + return 1; + } + return 0; +} template <> uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) @@ -1606,6 +1776,7 @@ uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Refere case x86::kPointer: case x86::kPointerWeakImport: + case x86::kAbsolute32: if ( !isExtern && (ref->getTargetOffset() != 0) ) { // use scattered reloc is target offset is non-zero sreloc1->set_r_scattered(true); @@ -2031,9 +2202,8 @@ void Writer::buildObjectFileFixups() //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() ) + // only use INDIRECT_SYMBOL_LOCAL in non-lazy-pointers for atoms that won't be in symbol table + if ( curSection->fAllNonLazyPointers && (ref->getTarget().getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn) ) undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL; else undefinedSymbolIndex = this->symbolIndex(ref->getTarget()); @@ -2067,7 +2237,7 @@ void Writer::buildObjectFileFixups() relocIndex += this->addObjectRelocs(atom, ref); } } - else { + else if ( ref->getKind() != A::kNoFixUp ) { relocIndex += this->addObjectRelocs(atom, ref); } } @@ -2091,9 +2261,9 @@ void Writer::buildObjectFileFixups() } template <> -bool Writer::illegalRelocInFinalLinkedImage(uint8_t kind, bool slideable) +bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref, bool slideable) { - switch ( kind ) { + switch ( ref.getKind() ) { case ppc::kAbsLow16: case ppc::kAbsLow14: case ppc::kAbsHigh16: @@ -2106,9 +2276,9 @@ bool Writer::illegalRelocInFinalLinkedImage(uint8_t kind, bool slideable) template <> -bool Writer::illegalRelocInFinalLinkedImage(uint8_t kind, bool slideable) +bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref, bool slideable) { - switch ( kind ) { + switch ( ref.getKind() ) { case ppc::kAbsLow16: case ppc::kAbsLow14: case ppc::kAbsHigh16: @@ -2120,11 +2290,31 @@ bool Writer::illegalRelocInFinalLinkedImage(uint8_t kind, bool slideable) } template <> -bool Writer::illegalRelocInFinalLinkedImage(uint8_t kind, bool slideable) -{ +bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref, bool slideable) +{ + if ( ref.getKind() == x86::kAbsolute32 ) { + switch ( ref.getTarget().getDefinitionKind() ) { + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + // illegal in dylibs/bundles, until we support TEXT relocs + return slideable; + case ObjectFile::Atom::kWeakDefinition: + // illegal if an exported weak symbol, until we support TEXT relocs + return this->shouldExport(ref.getTarget()); + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + // illegal until we support TEXT relocs + return true; + } + } return false; } +template <> +bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref, bool slideable) +{ + return false; +} template @@ -2161,6 +2351,64 @@ typename Writer::RelocKind Writer::relocationNeededInFinalLinkedImage(cons return kRelocNone; } +template +uint64_t Writer::relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const +{ + // for 32-bit architectures, the r_address field in relocs + // for final linked images is the offset from the base address + uint64_t result = address - fOptions.baseAddress(); + if ( result > 0x7FFFFFFF ) { + throwf("image too large: address can't fit in 31-bit r_address field in %s from %s", + atom->getDisplayName(), atom->getFile()->getPath()); + } + return result; +} + +template <> +uint64_t Writer::relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const +{ + // for x86_64, the r_address field in relocs for final linked images + // is the offset from the start address of the first writable segment + uint64_t result = address - fFirstWritableSegment->fBaseAddress; + if ( result > 0xFFFFFFFF ) { + throwf("image too large: address can't fit in 32-bit r_address field in %s from %s", + atom->getDisplayName(), atom->getFile()->getPath()); + } + return result; +} + +template <> +uint64_t Writer::relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const +{ + // for ppc64, the Mac OS X 10.4 dyld assumes r_address is always the offset from the base address. + // the 10.5 dyld, iterprets the r_address as: + // 1) an offset from the base address, iff there are no writable segments with a address > 4GB from base address, otherwise + // 2) an offset from the base address of the first writable segment + // For dyld, r_address is always the offset from the base address + uint64_t result; + bool badFor10_4 = false; + if ( fWritableSegmentPastFirst4GB ) { + if ( fOptions.macosxVersionMin() < Options::k10_5 ) + badFor10_4 = true; + result = address - fFirstWritableSegment->fBaseAddress; + if ( result > 0xFFFFFFFF ) { + throwf("image too large: address can't fit in 32-bit r_address field in %s from %s", + atom->getDisplayName(), atom->getFile()->getPath()); + } + } + else { + result = address - fOptions.baseAddress(); + if ( (fOptions.macosxVersionMin() < Options::k10_5) && (result > 0x7FFFFFFF) ) + badFor10_4 = true; + } + if ( badFor10_4 ) { + throwf("image or pagezero_size too large for Mac OS X 10.4: address can't fit in 31-bit r_address field for %s from %s", + atom->getDisplayName(), atom->getFile()->getPath()); + } + return result; +} + + template void Writer::buildExecutableFixups() { @@ -2212,7 +2460,7 @@ void Writer::buildExecutableFixups() 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_address(relocAddressInFinalLinkedImage(atom->getAddress(), atom)); pblaReloc.set_r_symbolnum(sectionNum); pblaReloc.set_r_pcrel(false); pblaReloc.set_r_length(); @@ -2223,7 +2471,8 @@ void Writer::buildExecutableFixups() } 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()); + 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: @@ -2237,7 +2486,7 @@ void Writer::buildExecutableFixups() // 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_address(this->relocAddressInFinalLinkedImage(atom->getAddress() + ref->getFixUpOffset(), atom)); internalReloc.set_r_symbolnum(sectionNum); internalReloc.set_r_pcrel(false); internalReloc.set_r_length(); @@ -2249,7 +2498,7 @@ void Writer::buildExecutableFixups() case kRelocExternal: { macho_relocation_info

externalReloc; - externalReloc.set_r_address(atom->getAddress()+ref->getFixUpOffset()-fOptions.baseAddress()); + externalReloc.set_r_address(this->relocAddressInFinalLinkedImage(atom->getAddress() + ref->getFixUpOffset(), atom)); externalReloc.set_r_symbolnum(this->symbolIndex(ref->getTarget())); externalReloc.set_r_pcrel(false); externalReloc.set_r_length(); @@ -2260,7 +2509,7 @@ void Writer::buildExecutableFixups() break; } } - else if ( this->illegalRelocInFinalLinkedImage(ref->getKind(), slideable) ) { + else if ( this->illegalRelocInFinalLinkedImage(*ref, slideable) ) { throwf("absolute addressing (perhaps -mdynamic-no-pic) used in %s from %s not allowed in slidable image", atom->getDisplayName(), atom->getFile()->getPath()); } } @@ -2307,6 +2556,13 @@ void Writer::writeNoOps(uint32_t from, uint32_t to) ::pwrite(fFileDescriptor, &x86Nop, 1, 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() @@ -2380,7 +2636,8 @@ uint64_t Writer::writeAtoms() 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()); + //fprintf(stderr, "writing 0x%08X -> 0x%08X (addr=0x%llX, size=0x%llX), atom %s from %s\n", + // offset, end, atom->getAddress(), atom->getSize(), atom->getDisplayName(), atom->getFile()->getPath()); // write out ::pwrite(fFileDescriptor, buffer, atom->getSize(), offset); } @@ -2389,6 +2646,7 @@ uint64_t Writer::writeAtoms() } } delete [] buffer; + close(fFileDescriptor); return end; } @@ -2397,6 +2655,8 @@ template <> void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const { uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; + const int64_t bl_twoGigLimit = 0x7FFFFFFF; + int64_t displacement; switch ( (x86::ReferenceKinds)(ref->getKind()) ) { case x86::kNoFixUp: case x86::kFollowOn: @@ -2405,12 +2665,12 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const Ob case x86::kPointerWeakImport: case x86::kPointer: { - if ( ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kRegularDefinition ) { + if ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal ) { // external realocation ==> pointer contains addend LittleEndian::set32(*fixUp, ref->getTargetOffset()); } else { - // internal relocation => pointer contains target address + // 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()); } @@ -2422,7 +2682,7 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const Ob break; case x86::kPCRel32WeakImport: case x86::kPCRel32: - int64_t displacement = 0; + displacement = 0; switch ( ref->getTarget().getDefinitionKind() ) { case ObjectFile::Atom::kRegularDefinition: case ObjectFile::Atom::kWeakDefinition: @@ -2435,13 +2695,27 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const Ob 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; + case x86::kAbsolute32: + switch ( ref->getTarget().getDefinitionKind() ) { + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + case ObjectFile::Atom::kTentativeDefinition: + // pointer contains target address + LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); + break; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + // external realocation ==> pointer contains addend + LittleEndian::set32(*fixUp, ref->getTargetOffset()); + break; + } + break; } } @@ -2451,12 +2725,14 @@ void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, co uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; bool isExternal = ( (ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kRegularDefinition) && shouldExport(ref->getTarget()) ); - switch (ref->getKind()) { + switch ( (x86::ReferenceKinds)(ref->getKind()) ) { case x86::kNoFixUp: case x86::kFollowOn: // do nothing break; case x86::kPointer: + case x86::kPointerWeakImport: + case x86::kAbsolute32: { if ( isExternal ) { // external realocation ==> pointer contains addend @@ -2480,6 +2756,7 @@ void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, co (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); break; case x86::kPCRel32: + case x86::kPCRel32WeakImport: int64_t displacement = 0; if ( isExternal ) displacement = ref->getTargetOffset() - (inAtom->getAddress() + ref->getFixUpOffset() + 4); @@ -2495,6 +2772,160 @@ void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, co } } +template <> +void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + const int64_t twoGigLimit = 0x7FFFFFFF; + uint64_t* fixUp = (uint64_t*)&buffer[ref->getFixUpOffset()]; + int64_t displacement = 0; + switch ( (x86_64::ReferenceKinds)(ref->getKind()) ) { + case x86_64::kNoFixUp: + case x86_64::kFollowOn: + // do nothing + break; + case x86_64::kPointerWeakImport: + case x86_64::kPointer: + { + //fprintf(stderr, "fixUpReferenceFinal: %s reference to %s\n", this->getDisplayName(), target.getDisplayName()); + if ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal ) { + // external realocation ==> pointer contains addend + LittleEndian::set64(*fixUp, ref->getTargetOffset()); + } + else { + // internal relocation + // pointer contains target address + //printf("Atom::fixUpReferenceFinal) target.name=%s, target.address=0x%08llX\n", target.getDisplayName(), target.getAddress()); + LittleEndian::set64(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); + } + } + break; + case x86_64::kPointerDiff32: + displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); + if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) + throw "32-bit pointer difference out of range"; + LittleEndian::set32(*((uint32_t*)fixUp), (uint32_t)displacement); + break; + case x86_64::kPointerDiff: + LittleEndian::set64(*fixUp, + (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); + break; + case x86_64::kBranchPCRel32WeakImport: + case x86_64::kBranchPCRel32: + case x86_64::kPCRel32: + case x86_64::kPCRel32_1: + case x86_64::kPCRel32_2: + case x86_64::kPCRel32_4: + case x86_64::kPCRel32GOT: + case x86_64::kPCRel32GOTWeakImport: + case x86_64::kPCRel32GOTLoad: + case x86_64::kPCRel32GOTLoadWeakImport: + switch ( ref->getTarget().getDefinitionKind() ) { + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + case ObjectFile::Atom::kTentativeDefinition: + displacement = (ref->getTarget().getAddress() + (int32_t)ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); + break; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + throw "codegen problem, can't use rel32 to external symbol"; + break; + } + switch ( ref->getKind() ) { + case x86_64::kPCRel32_1: + displacement -= 1; + break; + case x86_64::kPCRel32_2: + displacement -= 2; + break; + case x86_64::kPCRel32_4: + displacement -= 4; + break; + } + if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) { + //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); + throw "rel32 out of range"; + } + LittleEndian::set32(*((uint32_t*)fixUp), (int32_t)displacement); + break; + } +} + +template <> +void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + const int64_t twoGigLimit = 0x7FFFFFFF; + bool external = (ref->getTarget().getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn); + uint64_t* fixUp = (uint64_t*)&buffer[ref->getFixUpOffset()]; + int64_t displacement = 0; + int32_t temp32; + switch ( (x86_64::ReferenceKinds)(ref->getKind()) ) { + case x86_64::kNoFixUp: + case x86_64::kFollowOn: + // do nothing + break; + case x86_64::kPointer: + case x86_64::kPointerWeakImport: + { + if ( external ) { + // external realocation ==> pointer contains addend + LittleEndian::set64(*fixUp, ref->getTargetOffset()); + } + else { + // internal relocation ==> pointer contains target address + LittleEndian::set64(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); + } + } + break; + case x86_64::kPointerDiff32: + // addend in content + LittleEndian::set32(*((uint32_t*)fixUp), ref->getTargetOffset() - ref->getFromTargetOffset() ); + break; + case x86_64::kPointerDiff: + // addend in content + LittleEndian::set64(*fixUp, ref->getTargetOffset() - ref->getFromTargetOffset() ); + break; + case x86_64::kBranchPCRel32: + case x86_64::kBranchPCRel32WeakImport: + case x86_64::kPCRel32: + case x86_64::kPCRel32_1: + case x86_64::kPCRel32_2: + case x86_64::kPCRel32_4: + // turn unsigned 64-bit target offset in signed 32-bit offset, since that is what source originally had + temp32 = ref->getTargetOffset(); + if ( external ) { + // extern relocation contains addend + displacement = temp32; + } + else { + // internal relocations contain delta to target address + displacement = (ref->getTarget().getAddress() + temp32) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); + } + switch ( ref->getKind() ) { + case x86_64::kPCRel32_1: + displacement -= 1; + break; + case x86_64::kPCRel32_2: + displacement -= 2; + break; + case x86_64::kPCRel32_4: + displacement -= 4; + break; + } + if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) { + //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); + throw "rel32 out of range"; + } + LittleEndian::set32(*((uint32_t*)fixUp), (int32_t)displacement); + break; + case x86_64::kPCRel32GOT: + case x86_64::kPCRel32GOTLoad: + case x86_64::kPCRel32GOTWeakImport: + case x86_64::kPCRel32GOTLoadWeakImport: + // contains addend (usually zero) + LittleEndian::set32(*((uint32_t*)fixUp), (uint32_t)(ref->getTargetOffset())); + break; + } +} template <> void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const @@ -2560,6 +2991,13 @@ void Writer::fixUpReference_powerpc(const ObjectFile::Reference* ref, const O throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; P::setP(*fixUpPointer, fDyldHelper->getAddress()); } + else if ( !finalLinkedImage && ((SectionInfo*)inAtom->getSection())->fAllNonLazyPointers ) { + // indirect symbol table has INDIRECT_SYMBOL_LOCAL, so we must put address in content + if ( ref->getTarget().getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn ) + P::setP(*fixUpPointer, targetAddr); + else + P::setP(*fixUpPointer, 0); + } else if ( relocateableExternal ) { // external realocation ==> pointer contains addend P::setP(*fixUpPointer, ref->getTargetOffset()); @@ -2724,6 +3162,11 @@ bool Writer::stubableReferenceKind(uint8_t kind) return (kind == x86::kPCRel32 || kind == x86::kPCRel32WeakImport); } +template <> +bool Writer::stubableReferenceKind(uint8_t kind) +{ + return (kind == x86_64::kBranchPCRel32 || kind == x86_64::kBranchPCRel32WeakImport); +} template <> bool Writer::weakImportReferenceKind(uint8_t kind) @@ -2743,6 +3186,18 @@ bool Writer::weakImportReferenceKind(uint8_t kind) return (kind == x86::kPCRel32WeakImport || kind == x86::kPointerWeakImport); } +template <> +bool Writer::weakImportReferenceKind(uint8_t kind) +{ + switch ( kind ) { + case x86_64::kPointerWeakImport: + case x86_64::kBranchPCRel32WeakImport: + case x86_64::kPCRel32GOTWeakImport: + case x86_64::kPCRel32GOTLoadWeakImport: + return true; + } + return false; +} template <> @@ -2763,6 +3218,52 @@ bool Writer::GOTReferenceKind(uint8_t kind) return false; } +template <> +bool Writer::GOTReferenceKind(uint8_t kind) +{ + switch ( kind ) { + case x86_64::kPCRel32GOT: + case x86_64::kPCRel32GOTWeakImport: + case x86_64::kPCRel32GOTLoad: + case x86_64::kPCRel32GOTLoadWeakImport: + return true; + } + return false; +} + +template +void Writer::scanForAbsoluteReferences() +{ + // do nothing +} + +// for ppc64 look for any -mdynamic-no-pic codegen +template <> +void Writer::scanForAbsoluteReferences() +{ + // only do this for main executable + if ( mightNeedPadSegment() && (fPageZeroAtom != NULL) ) { + 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; + switch (ref->getKind()) { + case ppc64::kAbsLow16: + case ppc64::kAbsLow14: + case ppc64::kAbsHigh16: + case ppc64::kAbsHigh16AddLow: + //fprintf(stderr, "found -mdyanmic-no-pic codegen in %s in %s\n", atom->getDisplayName(), atom->getFile()->getPath()); + // shrink page-zero and add pad segment to compensate + fPadSegmentInfo = new SegmentInfo(); + strcpy(fPadSegmentInfo->fName, "__4BGFILL"); + fPageZeroAtom->setSize(0x1000); + return; + } + } + } + } +} template @@ -2770,10 +3271,10 @@ void Writer::synthesizeStubs() { switch ( fOptions.outputKind() ) { case Options::kStaticExecutable: - case Options::kDyld: case Options::kObjectFile: // these output kinds never have stubs return; + case Options::kDyld: case Options::kDynamicLibrary: case Options::kDynamicBundle: case Options::kDynamicExecutable: @@ -2841,7 +3342,7 @@ void Writer::synthesizeStubs() nlp = pos->second; } // alter reference to use non lazy pointer instead - ref->setTarget(*nlp, 0); + ref->setTarget(*nlp, ref->getTargetOffset()); } } } @@ -2922,7 +3423,9 @@ void Writer::synthesizeStubs() ObjectFile::Atom* atom = *it; ObjectFile::Section* nextSection = atom->getSection(); if ( nextSection != curSection ) { - if ( (prevAtom != NULL) && (strcmp(prevAtom->getSectionName(), "__dyld") == 0) ) { + if ( (prevAtom != NULL) + && ((strcmp(prevAtom->getSectionName(), "__dyld") == 0) + || ((fOptions.outputKind() == Options::kDyld) && (strcmp(prevAtom->getSectionName(), "__data") == 0))) ) { // found end of __dyld section, insert lazy pointers here fAllAtoms->insert(it, fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end()); inserted = true; @@ -2966,7 +3469,8 @@ void Writer::partitionIntoSections() 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)) ); + currentSectionInfo->fVirtualSection = ( (currentSectionInfo->fSectionName[0] == '.') || + (oneSegmentCommand && (atom->getDefinitionKind()==ObjectFile::Atom::kTentativeDefinition)) && !fOptions.makeTentativeDefinitionsReal() ); if ( !currentSectionInfo->fVirtualSection || fEmitVirtualSections ) currentSectionInfo->setIndex(sectionIndex++); currentSegmentInfo->fSections.push_back(currentSectionInfo); @@ -3088,6 +3592,12 @@ bool Writer::addBranchIslands() return false; } +template <> +bool Writer::addBranchIslands() +{ + // x86 branches can reach entire 4G size of largest image + return false; +} template <> inline uint8_t Writer::branch24Reference() @@ -3343,7 +3853,10 @@ void Writer::assignFileOffsets() // adjust file offset to match address if ( prevSection != NULL ) { - fileOffset = (address - prevSection->getBaseAddress()) + prevSection->fFileOffset; + if ( finalLinkedImage || !prevSection->fVirtualSection ) + fileOffset = (address - prevSection->getBaseAddress()) + prevSection->fFileOffset; + else + fileOffset = ( (fileOffset+alignment-1) & (-alignment) ); } // update section info @@ -3357,7 +3870,8 @@ void Writer::assignFileOffsets() throwf("zero-fill section %s not at end of segment", curSection->fSectionName); // update running pointers - address += curSection->fSize; + if ( finalLinkedImage || !curSection->fVirtualSection ) + address += curSection->fSize; fileOffset += curSection->fSize; // update segment info @@ -3382,11 +3896,10 @@ void Writer::assignFileOffsets() 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]; @@ -3404,7 +3917,7 @@ void Writer::assignFileOffsets() 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 { + else if ( (segment1->fSize != 0) && (segment2->fSize != 0) ) { throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)", segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize); } @@ -3412,6 +3925,18 @@ void Writer::assignFileOffsets() } } } + + // set up fFirstWritableSegment and fWritableSegmentPastFirst4GB + for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { + SegmentInfo* curSegment = *segit; + if ( (curSegment->fInitProtection & VM_PROT_WRITE) != 0 ) { + if ( fFirstWritableSegment == NULL ) + fFirstWritableSegment = curSegment; + if ( (curSegment->fBaseAddress + curSegment->fSize - fOptions.baseAddress()) >= 0x100000000LL ) + fWritableSegmentPastFirst4GB = true; + } + } + } template @@ -3426,6 +3951,21 @@ void Writer::adjustLinkEditSections() const unsigned int sectionCount = lastSeg->fSections.size(); uint64_t fileOffset = lastSeg->fSections[firstLinkEditSectionIndex]->fFileOffset; uint64_t address = lastSeg->fSections[firstLinkEditSectionIndex]->getBaseAddress(); + if ( fPadSegmentInfo != NULL ) { + // insert __4GBFILL segment into segments vector before LINKEDIT + for(std::vector::iterator it = fSegmentInfos.begin(); it != fSegmentInfos.end(); ++it) { + if ( *it == lastSeg ) { + fSegmentInfos.insert(it, fPadSegmentInfo); + break; + } + } + // adjust __4GBFILL segment to span from end of last segment to zeroPageSize + fPadSegmentInfo->fSize = fOptions.zeroPageSize() - address; + fPadSegmentInfo->fBaseAddress = address; + // adjust LINKEDIT to start at zeroPageSize + address = fOptions.zeroPageSize(); + lastSeg->fBaseAddress = fOptions.zeroPageSize(); + } for (unsigned int i=firstLinkEditSectionIndex; i < sectionCount; ++i) { std::vector& atoms = lastSeg->fSections[i]->fAtoms; const unsigned int atomCount = atoms.size(); @@ -3442,6 +3982,7 @@ void Writer::adjustLinkEditSections() if ( size > fLargestAtomSize ) fLargestAtomSize = size; } + //fprintf(stderr, "setting: lastSeg->fSections[%d]->fSize = 0x%08llX\n", i, sectionOffset); lastSeg->fSections[i]->fSize = sectionOffset; fileOffset += sectionOffset; address += sectionOffset; @@ -3636,6 +4177,13 @@ void MachHeaderAtom::setHeaderInfo(macho_header& header) const header.set_cpusubtype(CPU_SUBTYPE_I386_ALL); } +template <> +void MachHeaderAtom::setHeaderInfo(macho_header& header) const +{ + header.set_magic(MH_MAGIC_64); + header.set_cputype(CPU_TYPE_X86_64); + header.set_cpusubtype(CPU_SUBTYPE_X86_64_ALL); +} template CustomStackAtom::CustomStackAtom(Writer& writer) @@ -3666,6 +4214,11 @@ bool CustomStackAtom::stackGrowsDown() return true; } +template <> +bool CustomStackAtom::stackGrowsDown() +{ + return true; +} template void SegmentLoadCommandsAtom::computeSize() @@ -3684,6 +4237,10 @@ void SegmentLoadCommandsAtom::computeSize() } fSize = size; fCommandCount = segCount; + if ( fWriter.fPadSegmentInfo != NULL ) { + ++fCommandCount; + fSize += sizeof(macho_segment_command

); + } } template <> @@ -3704,6 +4261,11 @@ 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 @@ -3766,7 +4328,7 @@ void SegmentLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const sect->set_reserved1(sectInfo->fIndirectSymbolOffset); } else if ( sectInfo->fAllStubs ) { - sect->set_flags(S_SYMBOL_STUBS); + sect->set_flags(S_SYMBOL_STUBS | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS); sect->set_reserved1(sectInfo->fIndirectSymbolOffset); sect->set_reserved2(sectInfo->fSize / sectInfo->fAtoms.size()); } @@ -3802,9 +4364,15 @@ void SegmentLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const else if ( (strcmp(sectInfo->fSectionName, "__literal8") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { sect->set_flags(S_8BYTE_LITERALS); } + else if ( (strcmp(sectInfo->fSectionName, "__literal16") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { + sect->set_flags(S_16BYTE_LITERALS); + } else if ( (strcmp(sectInfo->fSectionName, "__message_refs") == 0) && (strcmp(sectInfo->fSegmentName, "__OBJC") == 0) ) { sect->set_flags(S_LITERAL_POINTERS); } + else if ( (strncmp(sectInfo->fSectionName, "__text", 6) == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { + sect->set_flags(S_REGULAR | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS); + } } } p = &p[sizeof(macho_segment_command

) + sectionsEmitted*sizeof(macho_section

)]; @@ -4070,6 +4638,11 @@ uint64_t ThreadsLoadCommandsAtom::getSize() const return this->alignedSize(16 + 16*4); // base size + i386_THREAD_STATE_COUNT * 4 } +template <> +uint64_t ThreadsLoadCommandsAtom::getSize() const +{ + return this->alignedSize(16 + x86_THREAD_STATE64_COUNT * 4); +} template <> void ThreadsLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const @@ -4101,7 +4674,7 @@ void ThreadsLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const 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 + cmd->set_thread_register(3, fWriter.fOptions.customStackAddr()); // r1 } template <> @@ -4121,6 +4694,21 @@ void ThreadsLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const } +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(x86_THREAD_STATE64); + cmd->set_count(x86_THREAD_STATE64_COUNT); + cmd->set_thread_register(16, start); // rip + if ( fWriter.fOptions.hasCustomStack() ) + cmd->set_thread_register(7, fWriter.fOptions.customStackAddr()); // uesp +} template @@ -4343,7 +4931,7 @@ bool StubAtom::pic() const // This usually only happens when a large zero-page is requested switch ( fWriter.fOptions.outputKind() ) { case Options::kDynamicExecutable: - return (fWriter.fOptions.zeroPageSize() > 4096); + return (fWriter.fPageZeroAtom->getSize() > 4096); case Options::kDynamicLibrary: case Options::kDynamicBundle: return true; @@ -4406,6 +4994,15 @@ StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target) writer.fAllSynthesizedStubs.push_back(this); } +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); + fReferences.push_back(new WriterReference(2, x86_64::kPCRel32, lp)); +} template const char* StubAtom::stubName(const char* name) @@ -4433,6 +5030,11 @@ uint64_t StubAtom::getSize() const return 5; } +template <> +uint64_t StubAtom::getSize() const +{ + return 6; +} template <> uint8_t StubAtom::getAlignment() const @@ -4493,6 +5095,23 @@ void StubAtom::copyRawContent(uint8_t buffer[]) const buffer[4] = 0xF4; } +template <> +void StubAtom::copyRawContent(uint8_t buffer[]) const +{ + buffer[0] = 0xFF; // jmp *foo$lazy_pointer(%rip) + buffer[1] = 0x25; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x00; +} + +// x86_64 stubs are 7 bytes and need no alignment +template <> +uint8_t StubAtom::getAlignment() const +{ + return 0; +} template <> const char* StubAtom::getSectionName() const @@ -4514,6 +5133,60 @@ const char* StubAtom::getSectionName() const +template <> +StubHelperAtom::StubHelperAtom(Writer& writer, ObjectFile::Atom& target, ObjectFile::Atom& lazyPointer) + : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target) +{ + writer.fAllSynthesizedStubHelpers.push_back(this); + + fReferences.push_back(new WriterReference(3, x86_64::kPCRel32, &lazyPointer)); + fReferences.push_back(new WriterReference(8, x86_64::kPCRel32, writer.fDyldHelper)); + if ( writer.fDyldHelper == NULL ) + throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; +} + +template <> +uint64_t StubHelperAtom::getSize() const +{ + return 12; +} + +template <> +void StubHelperAtom::copyRawContent(uint8_t buffer[]) const +{ + buffer[0] = 0x4C; // lea foo$lazy_ptr(%rip),%r11 + buffer[1] = 0x8D; + buffer[2] = 0x1D; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x00; + buffer[6] = 0x00; + buffer[7] = 0xE9; // jmp dyld_stub_binding_helper + buffer[8] = 0x00; + buffer[9] = 0x00; + buffer[10] = 0x00; + buffer[11] = 0x00; +} + +template +const char* StubHelperAtom::stubName(const char* name) +{ + char* buf; + asprintf(&buf, "%s$stubHelper", name); + return buf; +} + + +// specialize lazy pointer for x86_64 to initially pointer to stub helper +template <> +LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Atom& target) + : WriterAtom(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target) +{ + writer.fAllSynthesizedLazyPointers.push_back(this); + + StubHelperAtom* helper = new StubHelperAtom(writer, target, *this); + fReferences.push_back(new WriterReference(0, x86_64::kPointer, helper)); +} template diff --git a/src/ObjectDump.cpp b/src/ObjectDump.cpp index 7c51e0a..4150fda 100644 --- a/src/ObjectDump.cpp +++ b/src/ObjectDump.cpp @@ -289,15 +289,16 @@ static ObjectFile::Reader* createReader(const char* path, const ObjectFile::Read 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); + uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, 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; + for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { + fprintf(stderr, "archs[i].cputype = %X\n", archs[i].cputype); + if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)sPreferredArch ) { + p = p + OSSwapBigToHostInt32(archs[i].offset); mh = (struct mach_header*)p; } } @@ -308,6 +309,8 @@ static ObjectFile::Reader* createReader(const char* path, const ObjectFile::Read 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); } @@ -336,6 +339,8 @@ int main(int argc, const char* argv[]) sPreferredArch = CPU_TYPE_POWERPC; else if ( strcmp(arch, "i386") == 0 ) sPreferredArch = CPU_TYPE_I386; + else if ( strcmp(arch, "x86_64") == 0 ) + sPreferredArch = CPU_TYPE_X86_64; else throwf("unknown architecture %s", arch); } diff --git a/src/ObjectFile.h b/src/ObjectFile.h index 0be4910..98e59f4 100644 --- a/src/ObjectFile.h +++ b/src/ObjectFile.h @@ -59,13 +59,16 @@ struct LineInfo class ReaderOptions { public: - ReaderOptions() : fFullyLoadArchives(false), fLoadObjcClassesInArchives(false), fFlatNamespace(false), - fDebugInfoStripping(kDebugInfoFull), fTraceDylibs(false), fTraceIndirectDylibs(false), fTraceArchives(false), fTraceOutputFile(NULL) {} + ReaderOptions() : fFullyLoadArchives(false), fLoadObjcClassesInArchives(false), fFlatNamespace(false), + fForFinalLinkedImage(false), fWhyLoad(false), fDebugInfoStripping(kDebugInfoFull), + fTraceDylibs(false), fTraceIndirectDylibs(false), fTraceArchives(false), fTraceOutputFile(NULL) {} enum DebugInfoStripping { kDebugInfoNone, kDebugInfoMinimal, kDebugInfoFull }; bool fFullyLoadArchives; bool fLoadObjcClassesInArchives; bool fFlatNamespace; + bool fForFinalLinkedImage; + bool fWhyLoad; DebugInfoStripping fDebugInfoStripping; bool fTraceDylibs; bool fTraceIndirectDylibs; @@ -190,7 +193,7 @@ class Atom public: enum Scope { scopeTranslationUnit, scopeLinkageUnit, scopeGlobal }; enum DefinitionKind { kRegularDefinition, kWeakDefinition, kTentativeDefinition, kExternalDefinition, kExternalWeakDefinition }; - enum SymbolTableInclusion { kSymbolTableNotIn, kSymbolTableIn, kSymbolTableInAndNeverStrip }; + enum SymbolTableInclusion { kSymbolTableNotIn, kSymbolTableIn, kSymbolTableInAndNeverStrip, kSymbolTableInAsAbsolute }; virtual Reader* getFile() const = 0; virtual bool getTranslationUnitSource(const char** dir, const char** name) const = 0; @@ -199,6 +202,7 @@ public: virtual Scope getScope() const = 0; virtual DefinitionKind getDefinitionKind() const = 0; virtual SymbolTableInclusion getSymbolTableInclusion() const = 0; + virtual bool dontDeadStrip() const = 0; virtual bool isZeroFill() const = 0; virtual uint64_t getSize() const = 0; virtual std::vector& getReferences() const = 0; @@ -218,23 +222,20 @@ public: 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; } 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), fDontDeadStrip(false) {} + Atom() : fSegmentOffset(0), fSectionOffset(0), fSortOrder(0), fSection(NULL) {} virtual ~Atom() {} uint64_t fSegmentOffset; uint64_t fSectionOffset; unsigned int fSortOrder; class Section* fSection; - bool fDontDeadStrip; }; diff --git a/src/Options.cpp b/src/Options.cpp index 664bc41..e52b0cc 100644 --- a/src/Options.cpp +++ b/src/Options.cpp @@ -48,21 +48,23 @@ Options::Options(int argc, const char* argv[]) : 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), + fVersionMin(kMinUnset),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), + fUmbrellaName(NULL), fInitFunctionName(NULL), fDotOutputFile(NULL), fExecutablePath(NULL), fBundleLoader(NULL), + fZeroPageSize(ULLONG_MAX), 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) + fTraceDylibSearching(false), fPause(false), fStatistics(false), fPrintOptions(false), + fMakeTentativeDefinitionsReal(false) { this->parsePreCommandLineEnvironmentSettings(); this->parse(argc, argv); this->parsePostCommandLineEnvironmentSettings(); + this->reconfigureDefaults(); this->checkIllegalOptionCombinations(); } @@ -253,6 +255,11 @@ std::vector& Options::initialUndefines() return fInitialUndefines; } +bool Options::printWhyLive(const char* symbolName) +{ + return ( fWhyLive.find(symbolName) != fWhyLive.end() ); +} + std::vector& Options::traceSymbols() { return fTraceSymbols; @@ -273,6 +280,26 @@ bool Options::hasExportRestrictList() return (fExportMode != kExportDefault); } +bool Options::allGlobalsAreDeadStripRoots() +{ + // -exported_symbols_list means globals are not exported by default + if ( fExportMode == kExportSome ) + return false; + // + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + // by default unused globals in a main executable are stripped + return false; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kObjectFile: + case Options::kDyld: + return true; + } + return false; +} + uint32_t Options::minimumHeaderPad() { return fMinimumHeaderPad; @@ -346,6 +373,8 @@ void Options::parseArch(const char* architecture) fArchitecture = CPU_TYPE_POWERPC64; else if ( strcmp(architecture, "i386") == 0 ) fArchitecture = CPU_TYPE_I386; + else if ( strcmp(architecture, "x86_64") == 0 ) + fArchitecture = CPU_TYPE_X86_64; else throw "-arch followed by unknown architecture name"; } @@ -512,18 +541,41 @@ Options::FileInfo Options::findFile(const char* path) void Options::loadFileList(const char* fileOfPaths) { - FILE* file = fopen(fileOfPaths, "r"); - if ( file == NULL ) - throwf("-filelist file not found: %s\n", fileOfPaths); + FILE* file; + const char* comma = strrchr(fileOfPaths, ','); + const char* prefix = NULL; + if ( comma != NULL ) { + prefix = comma+1; + int realFileOfPathsLen = comma-fileOfPaths; + char realFileOfPaths[realFileOfPathsLen+1]; + strncpy(realFileOfPaths,fileOfPaths, realFileOfPathsLen); + realFileOfPaths[realFileOfPathsLen] = '\0'; + file = fopen(realFileOfPaths, "r"); + if ( file == NULL ) + throwf("-filelist file not found: %s\n", realFileOfPaths); + } + else { + file = fopen(fileOfPaths, "r"); + if ( file == NULL ) + throwf("-filelist file not found: %s\n", fileOfPaths); + } - char path[1024]; + char path[PATH_MAX]; while ( fgets(path, 1024, file) != NULL ) { - path[1023] = '\0'; + path[PATH_MAX-1] = '\0'; char* eol = strchr(path, '\n'); if ( eol != NULL ) *eol = '\0'; - - fInputFiles.push_back(findFile(path)); + if ( prefix != NULL ) { + char builtPath[strlen(prefix)+strlen(path)+2]; + strcpy(builtPath, prefix); + strcat(builtPath, "/"); + strcat(builtPath, path); + fInputFiles.push_back(findFile(builtPath)); + } + else { + fInputFiles.push_back(findFile(path)); + } } fclose(file); } @@ -636,18 +688,30 @@ 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 - fprintf(stderr, "ld64: unknown option to -macosx_version_min"); + if ( (strncmp(version, "10.", 3) == 0) && isdigit(version[3]) ) { + int num = version[3] - '0'; + switch ( num ) { + case 0: + case 1: + fVersionMin = k10_1; + break; + case 2: + fVersionMin = k10_2; + break; + case 3: + fVersionMin = k10_3; + break; + case 4: + fVersionMin = k10_4; + break; + default: + fVersionMin = k10_5; + break; + } + } + else { + fprintf(stderr, "ld64: unknown option to -macosx_version_min not 10.x"); + } } void Options::setWeakReferenceMismatchTreatment(const char* treatment) @@ -898,7 +962,7 @@ void Options::parse(int argc, const char* argv[]) fDylibInstallName = argv[++i]; } // Sets the base address of the output. - else if ( strcmp(arg, "-seg1addr") == 0 ) { + else if ( (strcmp(arg, "-seg1addr") == 0) || (strcmp(arg, "-image_base") == 0) ) { fBaseAddress = parseAddress(argv[++i]); } else if ( strcmp(arg, "-e") == 0 ) { @@ -1074,7 +1138,10 @@ void Options::parse(int argc, const char* argv[]) } else if ( strcmp(arg, "-pagezero_size") == 0 ) { fZeroPageSize = parseAddress(argv[++i]); - fZeroPageSize &= (-4096); // page align + uint64_t temp = fZeroPageSize & (-4096); // page align + if ( fZeroPageSize != temp ) + fprintf(stderr, "ld64: warning, -pagezero_size not page aligned, rounding down\n"); + fZeroPageSize = temp; } else if ( strcmp(arg, "-stack_addr") == 0 ) { fStackAddr = parseAddress(argv[++i]); @@ -1097,8 +1164,12 @@ void Options::parse(int argc, const char* argv[]) i += 2; } else if ( strcmp(arg, "-bundle_loader") == 0 ) { - // FIX FIX - ++i; + fBundleLoader = argv[++i]; + if ( (fBundleLoader == NULL) || (fBundleLoader[0] == '-') ) + throw "-bundle_loader missing "; + FileInfo info = findFile(fBundleLoader); + info.options.fBundleLoader = true; + fInputFiles.push_back(info); } else if ( strcmp(arg, "-private_bundle") == 0 ) { // FIX FIX @@ -1154,8 +1225,14 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-m") == 0 ) { fWarnOnMultiplyDefined = true; } - else if ( strcmp(arg, "-whyload") == 0 ) { - // FIX FIX + else if ( (strcmp(arg, "-why_load") == 0) || (strcmp(arg, "-whyload") == 0) ) { + fReaderOptions.fWhyLoad = true; + } + else if ( strcmp(arg, "-why_live") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-why_live missing symbol name argument"; + fWhyLive.insert(name); } else if ( strcmp(arg, "-u") == 0 ) { const char* name = argv[++i]; @@ -1193,12 +1270,10 @@ void Options::parse(int argc, const char* argv[]) fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoMinimal; } else if ( strcmp(arg, "-dead_strip") == 0 ) { - //fDeadStrip = kDeadStripOnPlusUnusedInits; - fprintf(stderr, "ld64: warning -dead_strip not yet supported in ld64\n"); + fDeadStrip = kDeadStripOnPlusUnusedInits; } 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"); + fDeadStrip = kDeadStripOn; } else if ( strcmp(arg, "-w") == 0 ) { // FIX FIX @@ -1292,6 +1367,9 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-print_statistics") == 0 ) { fStatistics = true; } + else if ( strcmp(arg, "-d") == 0 ) { + fMakeTentativeDefinitionsReal = true; + } else if ( strcmp(arg, "-v") == 0 ) { // previously handled by buildSearchPaths() } @@ -1319,6 +1397,8 @@ void Options::parse(int argc, const char* argv[]) } } + + // // -syslibroot is used for SDK support. // The rule is that all search paths (both explicit and default) are @@ -1476,6 +1556,64 @@ void Options::parsePostCommandLineEnvironmentSettings() if ( fExecutablePath == NULL && (fOutputKind == kDynamicExecutable) ) { fExecutablePath = fOutputFile; } + +} + +void Options::reconfigureDefaults() +{ + // sync reader options + switch ( fOutputKind ) { + case Options::kObjectFile: + fReaderOptions.fForFinalLinkedImage = false; + break; + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kDyld: + fReaderOptions.fForFinalLinkedImage = true; + break; + } + + // set default min OS version + if ( fVersionMin == kMinUnset ) { + switch ( fArchitecture ) { + case CPU_TYPE_POWERPC: + fVersionMin = k10_2; + break; + case CPU_TYPE_I386: + case CPU_TYPE_POWERPC64: + case CPU_TYPE_X86_64: + fVersionMin = k10_4; + default: + // architecture not specified + fVersionMin = k10_4; + break; + } + } + + // adjust min based on architecture + switch ( fArchitecture ) { + case CPU_TYPE_I386: + if ( fVersionMin < k10_4 ) { + //fprintf(stderr, "ld64 warning: -macosx_version_min should be 10.4 or later for i386\n"); + fVersionMin = k10_4; + } + break; + case CPU_TYPE_POWERPC64: + if ( fVersionMin < k10_4 ) { + //fprintf(stderr, "ld64 warning: -macosx_version_min should be 10.4 or later for ppc64\n"); + fVersionMin = k10_4; + } + break; + case CPU_TYPE_X86_64: + if ( fVersionMin < k10_4 ) { + //fprintf(stderr, "ld64 warning: -macosx_version_min should be 10.4 or later for x86_64\n"); + fVersionMin = k10_4; + } + break; + } + } void Options::checkIllegalOptionCombinations() @@ -1522,7 +1660,7 @@ void Options::checkIllegalOptionCombinations() const char* lastSlash = strrchr(info.path, '/'); if ( lastSlash == NULL ) lastSlash = info.path - 1; - const char* dot = strchr(lastSlash, '.'); + const char* dot = strchr(&lastSlash[1], '.'); if ( dot == NULL ) dot = &lastSlash[strlen(lastSlash)]; if ( strncmp(&lastSlash[1], subLibrary, dot-lastSlash-1) == 0 ) { @@ -1548,6 +1686,7 @@ void Options::checkIllegalOptionCombinations() throw "-stack_addr must be < 4G for 32-bit processes"; break; case CPU_TYPE_POWERPC64: + case CPU_TYPE_X86_64: break; } if ( (fStackAddr & -4096) != fStackAddr ) @@ -1564,14 +1703,15 @@ void Options::checkIllegalOptionCombinations() 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; } + if ( (fStackAddr > 0xB0000000) && ((fStackAddr-fStackSize) < 0xB0000000) ) + fprintf(stderr, "ld64 warning: custom stack placement overlaps and will disable shared region\n"); break; case CPU_TYPE_POWERPC64: + case CPU_TYPE_X86_64: if ( fStackAddr == 0 ) { - fprintf(stderr, "ld64 warning: -stack_addr not specified, using the default 0x0008000000000000\n"); - fStackAddr = 0x0008000000000000LL; + fStackAddr = 0x0007FFFF00000000LL; } break; } @@ -1613,6 +1753,14 @@ void Options::checkIllegalOptionCombinations() if ( (fInitFunctionName != NULL) && (fOutputKind != Options::kDynamicLibrary) ) throw "-init can only be used with -dynamiclib"; + // check -bundle_loader only used with -bundle + if ( (fBundleLoader != NULL) && (fOutputKind != Options::kDynamicBundle) ) + throw "-bundle_loader can only be used with -bundle"; + + // check -d can only be used with -r + if ( fMakeTentativeDefinitionsReal && (fOutputKind != Options::kObjectFile) ) + throw "-d can only be used with -r"; + // make sure all required exported symbols exist for (NameSet::iterator it=fExportSymbols.begin(); it != fExportSymbols.end(); it++) { const char* name = *it; @@ -1620,5 +1768,51 @@ void Options::checkIllegalOptionCombinations() if ( strcmp(&name[strlen(name)-3], ".eh") != 0 ) fInitialUndefines.push_back(name); } + + // make sure that -init symbol exist + if ( fInitFunctionName != NULL ) + fInitialUndefines.push_back(fInitFunctionName); + + if ( fZeroPageSize == ULLONG_MAX ) { + // zero page size not specified on command line, set default + switch (fArchitecture) { + case CPU_TYPE_I386: + case CPU_TYPE_POWERPC: + // first 4KB for 32-bit architectures + fZeroPageSize = 0x1000; + break; + case CPU_TYPE_POWERPC64: + // first 4GB for ppc64 on 10.5 + if ( fVersionMin >= k10_5 ) + fZeroPageSize = 0x100000000ULL; + else + fZeroPageSize = 0x1000; // 10.4 dyld may not be able to handle >4GB zero page + break; + case CPU_TYPE_X86_64: + // first 4GB for x86_64 on all OS's + fZeroPageSize = 0x100000000ULL; + break; + default: + // if -arch not used, default to 4K zero-page + fZeroPageSize = 0x1000; + } + } + else { + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + // -pagezero_size size only legal when building main executable + break; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kObjectFile: + case Options::kDyld: + throw "-pagezero_size option can only be used when linking a main executable"; + } + } + + // -dead_strip and -r are incompatible + if ( (fDeadStrip != kDeadStripOff) && (fOutputKind == Options::kObjectFile) ) + throw "-r and -dead_strip cannot be used together\n"; } diff --git a/src/Options.h b/src/Options.h index c8ef861..dcedf8e 100644 --- a/src/Options.h +++ b/src/Options.h @@ -39,10 +39,11 @@ void throwf (const char* format, ...) __attribute__ ((noreturn)); class DynamicLibraryOptions { public: - DynamicLibraryOptions() : fWeakImport(false), fReExport(false), fInstallPathOverride(NULL) {} + DynamicLibraryOptions() : fWeakImport(false), fReExport(false), fBundleLoader(false), fInstallPathOverride(NULL) {} bool fWeakImport; bool fReExport; + bool fBundleLoader; const char* fInstallPathOverride; }; @@ -70,7 +71,7 @@ public: kWeakReferenceMismatchNonWeak }; enum CommonsMode { kCommonsIgnoreDylibs, kCommonsOverriddenByDylibs, kCommonsConflictsDylibsError }; enum DeadStripMode { kDeadStripOff, kDeadStripOn, kDeadStripOnPlusUnusedInits }; - enum VersionMin { k10_1, k10_2, k10_3, k10_4, k10_5 }; + enum VersionMin { kMinUnset, k10_1, k10_2, k10_3, k10_4, k10_5 }; struct FileInfo { const char* path; @@ -113,6 +114,7 @@ public: bool keepPrivateExterns(); // only for kObjectFile bool interposable(); // only for kDynamicLibrary bool hasExportRestrictList(); + bool allGlobalsAreDeadStripRoots(); bool shouldExport(const char*); bool ignoreOtherArchInputFiles(); bool forceCpuSubtypeAll(); @@ -138,6 +140,7 @@ public: uint64_t customStackAddr(); bool hasExecutableStack(); std::vector& initialUndefines(); + bool printWhyLive(const char* name); uint32_t minimumHeaderPad(); std::vector& extraSections(); std::vector& sectionAlignments(); @@ -150,6 +153,8 @@ public: bool warnStabs(); bool pauseAtEnd() { return fPause; } bool printStatistics() { return fStatistics; } + bool printArchPrefix() { return fMessagesPrefixedWithArchitecture; } + bool makeTentativeDefinitionsReal() { return fMakeTentativeDefinitionsReal; } private: class CStringEquals @@ -186,6 +191,7 @@ private: void addSectionAlignment(const char* segment, const char* section, const char* alignment); CommonsMode parseCommonsTreatment(const char* mode); Treatment parseTreatment(const char* treatment); + void reconfigureDefaults(); @@ -228,6 +234,7 @@ private: const char* fInitFunctionName; const char* fDotOutputFile; const char* fExecutablePath; + const char* fBundleLoader; uint64_t fZeroPageSize; uint64_t fStackSize; uint64_t fStackAddr; @@ -243,7 +250,9 @@ private: bool fPause; bool fStatistics; bool fPrintOptions; + bool fMakeTentativeDefinitionsReal; std::vector fInitialUndefines; + NameSet fWhyLive; std::vector fTraceSymbols; unsigned long fLimitUndefinedSymbols; std::vector fExtraSections; diff --git a/src/SectCreate.cpp b/src/SectCreate.cpp index 7296c1a..a77a787 100644 --- a/src/SectCreate.cpp +++ b/src/SectCreate.cpp @@ -70,6 +70,7 @@ public: virtual Scope getScope() const { return ObjectFile::Atom::scopeTranslationUnit; } virtual DefinitionKind getDefinitionKind() const { return kRegularDefinition; } virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } + virtual bool dontDeadStrip() const { return true; } virtual bool isZeroFill() const { return false; } virtual uint64_t getSize() const { return fFileLength; } virtual std::vector& getReferences() const { return fgEmptyReferenceList; } @@ -88,7 +89,7 @@ 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) { setDontDeadStrip(); } + : fOwner(owner), fSegment(segment), fSectionName(sectionName), fFileContent(fileContent), fFileLength(fileLength) { } virtual ~Atom() {} Reader& fOwner; diff --git a/src/ld.cpp b/src/ld.cpp index e98df10..e26c581 100644 --- a/src/ld.cpp +++ b/src/ld.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -283,6 +284,10 @@ class Linker { public: Linker(int argc, const char* argv[]); + const char* getArchPrefix(); + const char* architectureName(); + bool showArchitectureInErrors(); + bool isInferredArchitecture(); void createReaders(); void createWriter(); void addInputFile(ObjectFile::Reader* reader); @@ -291,14 +296,23 @@ public: private: + struct WhyLiveBackChain + { + WhyLiveBackChain* previous; + const char* name; + }; + ObjectFile::Reader* createReader(const Options::FileInfo&); void addAtom(ObjectFile::Atom& atom); void addAtoms(std::vector& atoms); void buildAtomList(); + void loadAndResolve(); void loadUndefines(); + void checkUndefines(); void addWeakAtomOverrides(); void resolveReferences(); - void deadStrip(); + void deadStripResolve(); + void addLiveRoot(const char* name); void sortAtoms(); void tweakLayout(); void writeDotOutput(); @@ -309,8 +323,8 @@ private: 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 markLive(ObjectFile::Atom& atom, Linker::WhyLiveBackChain* previous); + void collectStabs(ObjectFile::Reader* reader, std::map& atomOrdinals); void synthesizeStabs(ObjectFile::Reader* reader); void printStatistics(); void printTime(const char* msg, uint64_t partTime, uint64_t totalTime); @@ -330,6 +344,8 @@ private: bool haveDirectLibrary(const char* path); void logTraceInfo(const char* format, ...); + + class SymbolTable { public: @@ -373,12 +389,16 @@ private: std::vector fReadersThatHaveSuppliedAtoms; std::vector fAllAtoms; std::set fDeadAtoms; + std::set fLiveAtoms; + std::set fLiveRootAtoms; std::vector fStabs; bool fCreateUUID; SectionOrder fSectionOrder; unsigned int fNextSortOrder; unsigned int fNextObjectFileOrder; cpu_type_t fArchitecture; + const char* fArchitectureName; + bool fArchitectureInferred; bool fDirectLibrariesComplete; uint64_t fOutputFileSize; uint64_t fStartTime; @@ -401,23 +421,57 @@ private: Linker::Linker(int argc, const char* argv[]) : fOptions(argc, argv), fGlobalSymbolTable(*this), fOutputFile(NULL), fCreateUUID(false), fNextSortOrder(1), - fNextObjectFileOrder(1), fArchitecture(0), fDirectLibrariesComplete(false), fOutputFileSize(0), fTotalObjectSize(0), + fNextObjectFileOrder(1), fArchitecture(0), fArchitectureInferred(false), 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(); + fArchitectureInferred = true; + } + switch (fArchitecture) { + case CPU_TYPE_POWERPC: + fArchitectureName = "ppc"; + break; + case CPU_TYPE_POWERPC64: + fArchitectureName = "ppc64"; + break; + case CPU_TYPE_I386: + fArchitectureName = "i386"; + break; + case CPU_TYPE_X86_64: + fArchitectureName = "x86_64"; + break; + default: + fArchitectureName = "unknown architecture"; + break; } } +const char* Linker::architectureName() +{ + return fArchitectureName; +} + +bool Linker::showArchitectureInErrors() +{ + return fOptions.printArchPrefix(); +} + +bool Linker::isInferredArchitecture() +{ + return fArchitectureInferred; +} + cpu_type_t Linker::inferArchitecture() { - // scan all input files, looking for a thin .o file. + // 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(); @@ -428,21 +482,25 @@ cpu_type_t Linker::inferArchitecture() ::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); + //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); + //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); + //fprintf(stderr, "ld64 warning: -arch not used, infering -arch i386 based on %s\n", it->path); return CPU_TYPE_I386; } + else if ( mach_o::relocatable::Reader::validFile(buffer) ) { + //fprintf(stderr, "ld64 warning: -arch not used, infering -arch x86_64 based on %s\n", it->path); + return CPU_TYPE_X86_64; + } } } } - + // no thin .o files found, so default to same architecture this was built as fprintf(stderr, "ld64 warning: -arch not specified\n"); #if __ppc__ @@ -451,6 +509,8 @@ cpu_type_t Linker::inferArchitecture() return CPU_TYPE_I386; #elif __ppc64__ return CPU_TYPE_POWERPC64; +#elif __x86_64__ + return CPU_TYPE_X86_64; #else #error unknown default architecture #endif @@ -468,12 +528,45 @@ void Linker::setOutputFile(ExecutableFile::Writer* writer) fOutputFile = writer; } +class InSet +{ +public: + InSet(std::set& deadAtoms) : fDeadAtoms(deadAtoms) {} + + bool operator()(ObjectFile::Atom*& atom) const { + return ( fDeadAtoms.count(atom) != 0 ); + } + +private: + std::set& fDeadAtoms; +}; + +void Linker::loadAndResolve() +{ + if ( fOptions.deadStrip() == Options::kDeadStripOff ) { + // without dead-code-stripping: + // find atoms to resolve all undefines + this->loadUndefines(); + // verify nothing is missing + this->checkUndefines(); + // once all undefines fulfill, then bind all references + this->resolveReferences(); + // remove atoms weak atoms that have been overridden + fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), InSet(fDeadAtoms)), fAllAtoms.end()); + } + else { + // with dead code stripping: + // start binding references from roots, + this->deadStripResolve(); + // verify nothing is missing + this->checkUndefines(); + } +} + void Linker::link() { this->buildAtomList(); - this->loadUndefines(); - this->resolveReferences(); - this->deadStrip(); + this->loadAndResolve(); this->sortAtoms(); this->tweakLayout(); this->writeDotOutput(); @@ -569,15 +662,21 @@ 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++) { - ObjectFile::Reference* reference = *it; - if ( reference->isTargetUnbound() ) { - fGlobalSymbolTable.require(reference->getTargetName()); + if ( fOptions.deadStrip() == Options::kDeadStripOff ) { + // not dead-stripping code, so 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++) { + ObjectFile::Reference* reference = *it; + if ( reference->isTargetUnbound() ) { + fGlobalSymbolTable.require(reference->getTargetName()); + } + if ( reference->hasFromTarget() && reference->isFromTargetUnbound() ) + fGlobalSymbolTable.require(reference->getFromTargetName()); } - if ( reference->hasFromTarget() && reference->isFromTargetUnbound() ) - fGlobalSymbolTable.require(reference->getFromTargetName()); + } + else { + if ( atom.dontDeadStrip() ) + fLiveRootAtoms.insert(&atom); } // if in global namespace, add atom itself to symbol table @@ -671,7 +770,10 @@ void Linker::loadUndefines() this->addJustInTimeAtoms(name); } } +} +void Linker::checkUndefines() +{ if ( fOptions.outputKind() != Options::kObjectFile ) { // error out on any remaining undefines bool doPrint = true; @@ -696,7 +798,10 @@ void Linker::loadUndefines() int unresolvableExportsCount = 0; if ( unresolvableCount != 0 ) { if ( doPrint ) { - fprintf(stderr, "can't resolve symbols:\n"); + if ( fOptions.printArchPrefix() ) + fprintf(stderr, "Undefined symbols for architecture %s:\n", fArchitectureName); + else + fprintf(stderr, "Undefined symbols:\n"); for (int i=0; i < unresolvableCount; ++i) { const char* name = unresolvableUndefines[i]; fprintf(stderr, " %s, referenced from:\n", name); @@ -731,12 +836,6 @@ 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 ) - throwf("symbol %s not found for -init", fOptions.initFunctionName()); - } } } @@ -802,7 +901,7 @@ void Linker::resolve(ObjectFile::Reference* reference) const char* targetName = reference->getTargetName(); ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); if ( target == NULL ) { - fprintf(stderr, "can't resolve: %s\n", targetName); + fprintf(stderr, "Undefined symbol: %s\n", targetName); } reference->setTarget(*target, reference->getTargetOffset()); } @@ -813,7 +912,7 @@ void Linker::resolveFrom(ObjectFile::Reference* reference) const char* fromTargetName = reference->getFromTargetName(); ObjectFile::Atom* fromTarget = fGlobalSymbolTable.find(fromTargetName); if ( fromTarget == NULL ) { - fprintf(stderr, "can't resolve: %s\n", fromTargetName); + fprintf(stderr, "Undefined symbol: %s\n", fromTargetName); } reference->setFromTarget(*fromTarget); } @@ -836,18 +935,6 @@ void Linker::resolveReferences() } } -class InSet -{ -public: - InSet(std::set& deadAtoms) : fDeadAtoms(deadAtoms) {} - - 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 @@ -867,81 +954,144 @@ private: }; -class DeadStrippable +class NotLive { public: - DeadStrippable() {} + NotLive(std::set& set) : fLiveAtoms(set) {} bool operator()(ObjectFile::Atom*& atom) const { - //if ( ! atom->dontDeadStrip() ) + //if ( fLiveAtoms.count(atom) == 0 ) // fprintf(stderr, "dead strip %s\n", atom->getDisplayName()); - return ( ! atom->dontDeadStrip() ); + return ( fLiveAtoms.count(atom) == 0 ); } +private: + std::set& fLiveAtoms; }; -void Linker::markLive(ObjectFile::Atom* atom, std::set& liveAtoms) + + +void Linker::markLive(ObjectFile::Atom& atom, struct Linker::WhyLiveBackChain* previous) { - if ( liveAtoms.count(atom) == 0 ) { + if ( fLiveAtoms.count(&atom) == 0 ) { + // if -whylive cares about this symbol, then dump chain + if ( (previous->name != NULL) && fOptions.printWhyLive(previous->name) ) { + int depth = 0; + for(WhyLiveBackChain* p = previous; p != NULL; p = p->previous, ++depth) { + for(int i=depth; i > 0; --i) + fprintf(stderr, " "); + fprintf(stderr, "%s\n", p->name); + } + } + // set up next chain + WhyLiveBackChain thisChain; + thisChain.previous = previous; // this atom is live - liveAtoms.insert(atom); - // so don't dead strip - atom->setDontDeadStrip(); - + fLiveAtoms.insert(&atom); // and all atoms it references - std::vector& references = atom->getReferences(); + 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); + if ( reference->isTargetUnbound() ) { + // look in global symbol table + const char* targetName = reference->getTargetName(); + ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); + if ( target == NULL ) { + // load archives or dylibs + this->addJustInTimeAtoms(targetName); + } + // look again + target = fGlobalSymbolTable.find(targetName); + if ( target != NULL ) { + reference->setTarget(*target, reference->getTargetOffset()); + } + else { + // mark as undefined, for later error processing + fGlobalSymbolTable.require(targetName); + } + } + if ( ! reference->isTargetUnbound() ) { + thisChain.name = reference->getTargetName(); + markLive(reference->getTarget(), &thisChain); + } + if ( reference->hasFromTarget() ) { + // do the same as above, for from target + if ( reference->isFromTargetUnbound() ) { + // look in global symbol table + const char* targetName = reference->getFromTargetName(); + ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); + if ( target == NULL ) { + // load archives or dylibs + this->addJustInTimeAtoms(targetName); + } + // look again + target = fGlobalSymbolTable.find(targetName); + if ( target != NULL ) { + reference->setFromTarget(*target); + } + else { + // mark as undefined, for later error processing + fGlobalSymbolTable.require(targetName); + } + } + if ( ! reference->isFromTargetUnbound() ) { + thisChain.name = reference->getFromTargetName(); + markLive(reference->getFromTarget(), &thisChain); + } + } } } } -void Linker::deadStrip() + +void Linker::addLiveRoot(const char* name) +{ + ObjectFile::Atom* target = fGlobalSymbolTable.find(name); + if ( target == NULL ) { + this->addJustInTimeAtoms(name); + target = fGlobalSymbolTable.find(name); + } + if ( target != NULL ) + fLiveRootAtoms.insert(target); +} + + +void Linker::deadStripResolve() { - if ( fOptions.deadStrip() != Options::kDeadStripOff ) { - std::set liveAtoms; + // add main() to live roots + ObjectFile::Atom* entryPoint = this->entryPoint(); + if ( entryPoint != NULL ) + fLiveRootAtoms.insert(entryPoint); - // mark main() and all atoms reachable from it as live - ObjectFile::Atom* entryPoint = this->entryPoint(); - if ( entryPoint != NULL ) - markLive(entryPoint, liveAtoms); + // add dyld_stub_binding_helper() to live roots + ObjectFile::Atom* dyldHelper = this->dyldHelper(); + if ( dyldHelper != NULL ) + fLiveRootAtoms.insert(dyldHelper); - ObjectFile::Atom* dyldHelper = this->dyldHelper(); - if ( dyldHelper != NULL ) - markLive(dyldHelper, liveAtoms); + // add -exported_symbols_list, -init, and -u entries to live roots + std::vector& initialUndefines = fOptions.initialUndefines(); + for (std::vector::iterator it=initialUndefines.begin(); it != initialUndefines.end(); it++) + addLiveRoot(*it); - // 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 ); + // in some cases, every global scope atom in initial .o files is a root + if ( fOptions.allGlobalsAreDeadStripRoots() ) { 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); + if ( atom->getScope() == ObjectFile::Atom::scopeGlobal ) + fLiveRootAtoms.insert(atom); } - - // 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()); - //} - // 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()); + // mark all roots as live, and all atoms they reference + for (std::set::iterator it=fLiveRootAtoms.begin(); it != fLiveRootAtoms.end(); it++) { + WhyLiveBackChain rootChain; + rootChain.previous = NULL; + rootChain.name = (*it)->getDisplayName(); + markLive(**it, &rootChain); } -} - + // now remove all non-live atoms from fAllAtoms + fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), NotLive(fLiveAtoms)), fAllAtoms.end()); +} void Linker::sortAtoms() { @@ -978,18 +1128,9 @@ void Linker::writeDotOutput() 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); + fprintf(out, "\taddr%p [ shape = plaintext, label = \"%s\" ];\n", atom, name); } else if ( strcmp(atom->getSectionName(), "__cstring") == 0 ) { char cstring[atom->getSize()+2]; @@ -1021,37 +1162,7 @@ void Linker::writeDotOutput() 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, "\taddr%p -> addr%p;\n", fromAtom, toAtom); } } } @@ -1065,11 +1176,7 @@ void Linker::writeDotOutput() 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, "addr%p; ", atom); } } fprintf(out, "};\n "); @@ -1094,14 +1201,14 @@ ObjectFile::Atom* Linker::entryPoint() case Options::kDyld: entryPoint = fGlobalSymbolTable.find(fOptions.entryName()); if ( entryPoint == NULL ) { - throwf("could not find entry point: %s", fOptions.entryName()); + throwf("could not find entry point \"%s\" (perhaps missing crt1.o)", fOptions.entryName()); } break; case Options::kDynamicLibrary: if ( fOptions.initFunctionName() != NULL ) { entryPoint = fGlobalSymbolTable.find(fOptions.initFunctionName()); if ( entryPoint == NULL ) { - throwf("could not find -init function: %s", fOptions.initFunctionName()); + throwf("could not find -init function: \"%s\"", fOptions.initFunctionName()); } } break; @@ -1136,7 +1243,7 @@ const char* Linker::assureFullPath(const char* path) // // The stab strings are of the form: // ':' -// but the contain a colon. +// 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) ) // @@ -1200,8 +1307,9 @@ bool Linker::minimizeStab(ObjectFile::Reader::Stab& stab) } } -struct HeaderRange { - std::vector::iterator begin; + +struct HeaderRange { + std::vector::iterator begin; std::vector::iterator end; int parentRangeIndex; uint32_t sum; @@ -1217,19 +1325,25 @@ typedef __gnu_cxx::hash_map, __gnu_cxx::hash< static PathToSums sKnownBINCLs; -void Linker::collectStabs(ObjectFile::Reader* reader) +void Linker::collectStabs(ObjectFile::Reader* reader, std::map& atomOrdinals) { 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; + ObjectFile::Atom* atomWithLowestOrdinal = NULL; + ObjectFile::Atom* atomWithHighestOrdinal = NULL; + uint32_t highestOrdinal = 0; + uint32_t lowestOrdinal = UINT_MAX; + std::vector > soRanges; + // 1) find all (possibly nested) BINCL/EINCL ranges and their checksums + // 2) find all SO/SO ranges and the first/last atom own by a FUN stab therein for(std::vector::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) { ++count; switch ( it->type ) { @@ -1239,7 +1353,7 @@ void Linker::collectStabs(ObjectFile::Reader* reader) range.begin = it; range.end = readerStabs->end(); range.parentRangeIndex = curRangeIndex; - range.sum = it->value; + range.sum = it->value; range.sumPrecomputed = (range.sum != 0); range.useEXCL = false; range.cannotEXCL = false; @@ -1257,8 +1371,23 @@ void Linker::collectStabs(ObjectFile::Reader* reader) if ( log ) fprintf(stderr, "[%d->%d]EINCL %s\n", curRangeIndex, ranges[curRangeIndex].parentRangeIndex, it->string); curRangeIndex = ranges[curRangeIndex].parentRangeIndex; } - break; + break; case N_FUN: + { + std::map::iterator pos = atomOrdinals.find(it->atom); + if ( pos != atomOrdinals.end() ) { + uint32_t ordinal = pos->second; + if ( ordinal > highestOrdinal ) { + highestOrdinal = ordinal; + atomWithHighestOrdinal = it->atom; + } + if ( ordinal < lowestOrdinal ) { + lowestOrdinal = ordinal; + atomWithLowestOrdinal = it->atom; + } + } + } + // fall through case N_BNSYM: case N_ENSYM: case N_LBRAC: @@ -1272,6 +1401,19 @@ void Linker::collectStabs(ObjectFile::Reader* reader) fprintf(stderr, "ld64: cannot do BINCL/EINCL optimzation because of stabs kinds in %s for %s\n", ranges[curRangeIndex].begin->string, reader->getPath()); } break; + case N_SO: + if ( (it->string != NULL) && (strlen(it->string) > 0) ) { + // start SO, reset hi/low FUN tracking + atomWithLowestOrdinal = NULL; + atomWithHighestOrdinal = NULL; + highestOrdinal = 0; + lowestOrdinal = UINT_MAX; + } + else { + // end SO, record hi/low atoms for this SO range + soRanges.push_back(std::make_pair(atomWithLowestOrdinal, atomWithHighestOrdinal)); + } + // fall through default: if ( curRangeIndex != -1 ) { if ( ! ranges[curRangeIndex].sumPrecomputed ) { @@ -1289,34 +1431,42 @@ void Linker::collectStabs(ObjectFile::Reader* reader) 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); + int soIndex = 0; + for(std::vector::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) { + // copy minimal or all stabs + ObjectFile::Reader::Stab stab = *it; + if ( !minimal || minimizeStab(stab) ) { + if ( stab.type == N_SO ) { + if ( (stab.string != NULL) && (strlen(stab.string) > 0) ) { + // starting SO is associated with first atom + stab.atom = soRanges[soIndex].first; + } + else { + // ending SO is associated with last atom + stab.atom = soRanges[soIndex].second; + ++soIndex; + } + } + fStabs.push_back(stab); } } - 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); //} - + // 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 ) { @@ -1347,10 +1497,11 @@ void Linker::collectStabs(ObjectFile::Reader* reader) } } } - + // 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(); + int soIndex = 0; for(std::vector::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) { switch ( it->type ) { case N_BINCL: @@ -1360,7 +1511,7 @@ void Linker::collectStabs(ObjectFile::Reader* reader) 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 ) + if ( range.useEXCL ) stab.type = N_EXCL; // transform BINCL into EXCL if ( !minimal ) fStabs.push_back(stab); @@ -1374,11 +1525,24 @@ void Linker::collectStabs(ObjectFile::Reader* reader) fStabs.push_back(*it); curRangeIndex = ranges[curRangeIndex].parentRangeIndex; } - break; + break; default: if ( (curRangeIndex == -1) || !ranges[curRangeIndex].useEXCL ) { - if ( !minimal || minimizeStab(*it) ) - fStabs.push_back(*it); + ObjectFile::Reader::Stab stab = *it; + if ( !minimal || minimizeStab(stab) ) { + if ( stab.type == N_SO ) { + if ( (stab.string != NULL) || (strlen(stab.string) > 0) ) { + // starting SO is associated with first atom + stab.atom = soRanges[soIndex].first; + } + else { + // ending SO is associated with last atom + stab.atom = soRanges[soIndex].second; + ++soIndex; + } + } + fStabs.push_back(stab); + } } } } @@ -1397,10 +1561,13 @@ void Linker::synthesizeStabs(ObjectFile::Reader* reader) ObjectFile::Atom* atom = *it; if ( atom->getFile() == reader ) { const char* name = atom->getName(); - if ( (name != NULL) && (name[0] == '_' ) && (atom->getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn) ) { + if ( (name != NULL) && (atom->getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn) ) { const char* newDirPath; const char* newFilename; if ( atom->getTranslationUnitSource(&newDirPath, &newFilename) ) { + // gdb like directory SO's to end in '/', but dwarf DW_AT_comp_dir usually does not have trailing '/' + if ( (newDirPath != NULL) && (strlen(newDirPath) > 1 ) && (newDirPath[strlen(newDirPath)-1] != '/') ) + asprintf((char**)&newDirPath, "%s/", newDirPath); // need SO's whenever the translation unit source file changes if ( newFilename != filename ) { if ( filename != NULL ) { @@ -1559,6 +1726,14 @@ void Linker::synthesizeStabs(ObjectFile::Reader* reader) void Linker::collectStabs() { if ( fOptions.readerOptions().fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone ) { + + // make mapping from atoms to ordinal + std::map atomOrdinals; + uint32_t ordinal = 1; + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + atomOrdinals[*it] = ordinal++; + } + fStabs.reserve(1024); // try to minimize re-allocations // get stabs from each reader, in command line order for (std::vector::iterator it=fReadersThatHaveSuppliedAtoms.begin(); @@ -1571,14 +1746,14 @@ void Linker::collectStabs() // do nothing break; case ObjectFile::Reader::kDebugInfoStabs: - collectStabs(reader); + collectStabs(reader, atomOrdinals); break; case ObjectFile::Reader::kDebugInfoDwarf: synthesizeStabs(reader); fCreateUUID = true; break; case ObjectFile::Reader::kDebugInfoStabsUUID: - collectStabs(reader); + collectStabs(reader, atomOrdinals); fCreateUUID = true; break; default: @@ -1645,44 +1820,40 @@ ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) 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::dylib::Reader::validFile(p, info.options.fBundleLoader) ) + return this->addDylib(mach_o::dylib::Reader::make(p, len, info.path, info.options.fBundleLoader, 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::dylib::Reader::validFile(p, info.options.fBundleLoader) ) + return this->addDylib(mach_o::dylib::Reader::make(p, len, info.path, info.options.fBundleLoader, 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::dylib::Reader::validFile(p, info.options.fBundleLoader) ) + return this->addDylib(mach_o::dylib::Reader::make(p, len, info.path, info.options.fBundleLoader, 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_X86_64: + 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, info.options.fBundleLoader) ) + return this->addDylib(mach_o::dylib::Reader::make(p, len, info.path, info.options.fBundleLoader, 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 (fArchitecture) { - case CPU_TYPE_POWERPC: - archName = "ppc"; - break; - case CPU_TYPE_POWERPC64: - archName = "ppc64"; - break; - case CPU_TYPE_I386: - archName = "i386"; - break; - } - throwf("missing required architecture %s in fat file", archName); + throwf("missing required architecture %s in file", fArchitectureName); } else { throw "file is not of required architecture"; @@ -1847,7 +2018,7 @@ ObjectFile::Reader* Linker::addObject(ObjectFile::Reader* reader, const Options: ObjectFile::Reader* Linker::addDylib(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen) { - if ( reader->getInstallPath() == NULL ) { + if ( (reader->getInstallPath() == NULL) && !info.options.fBundleLoader ) { // this is a "blank" stub // silently ignore it return reader; @@ -1870,7 +2041,7 @@ ObjectFile::Reader* Linker::addDylib(ObjectFile::Reader* reader, const 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; @@ -1880,11 +2051,11 @@ ObjectFile::Reader* Linker::addDylib(ObjectFile::Reader* reader, const Options:: // 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) + okToLink = ( (outputFilePathLastSlash != NULL) + && (fOptions.umbrellaName() != NULL) && (strcmp(fOptions.umbrellaName(), reader->parentUmbrella()) == 0) ); } @@ -1912,21 +2083,21 @@ ObjectFile::Reader* Linker::addDylib(ObjectFile::Reader* reader, const Options:: 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; @@ -2040,6 +2211,9 @@ void Linker::createWriter() case CPU_TYPE_I386: this->setOutputFile(new mach_o::executable::Writer(path, fOptions, fDynamicLibraries)); break; + case CPU_TYPE_X86_64: + this->setOutputFile(new mach_o::executable::Writer(path, fOptions, fDynamicLibraries)); + break; default: throw "unknown architecture"; } @@ -2292,10 +2466,18 @@ bool Linker::AtomSorter::operator()(ObjectFile::Atom* left, ObjectFile::Atom* ri int main(int argc, const char* argv[]) { + const char* archName = NULL; + bool showArch = false; + bool archInferred = false; try { // create linker object given command line arguments Linker ld(argc, argv); + // save error message prefix + archName = ld.architectureName(); + archInferred = ld.isInferredArchitecture(); + showArch = ld.showArchitectureInErrors(); + // open all input files ld.createReaders(); @@ -2306,10 +2488,15 @@ int main(int argc, const char* argv[]) ld.link(); } catch (const char* msg) { - fprintf(stderr, "ld64 failed: %s\n", msg); + extern const double ld64VersionNumber; + if ( archInferred ) + fprintf(stderr, "ld64-%g failed: %s for inferred architecture %s\n", ld64VersionNumber, msg, archName); + else if ( showArch ) + fprintf(stderr, "ld64-%g failed: %s for architecture %s\n", ld64VersionNumber, msg, archName); + else + fprintf(stderr, "ld64-%g failed: %s\n", ld64VersionNumber, msg); return 1; } - return 0; } diff --git a/src/machochecker.cpp b/src/machochecker.cpp index 55f9fd9..118aa49 100644 --- a/src/machochecker.cpp +++ b/src/machochecker.cpp @@ -32,6 +32,9 @@ #include #include #include +#include +#include +#include #include @@ -74,6 +77,11 @@ private: void checkSection(const macho_segment_command

* segCmd, const macho_section

* sect); uint8_t loadCommandSizeMask(); void checkIndirectSymbolTable(); + void checkRelocations(); + void checkExternalReloation(const macho_relocation_info

* reloc); + void checkLocalReloation(const macho_relocation_info

* reloc); + pint_t relocBase(); + bool addressInWritableSegment(pint_t address); const char* fPath; const macho_header

* fHeader; @@ -84,7 +92,14 @@ private: uint32_t fSymbolCount; const uint32_t* fIndirectTable; uint32_t fIndirectTableCount; - + const macho_relocation_info

* fLocalRelocations; + uint32_t fLocalRelocationsCount; + const macho_relocation_info

* fExternalRelocations; + uint32_t fExternalRelocationsCount; + pint_t fRelocBase; + bool fWriteableSegmentWithAddrOver4G; + const macho_segment_command

* fFirstSegment; + const macho_segment_command

* fFirstWritableSegment; }; @@ -143,16 +158,36 @@ bool MachOChecker::validFile(const uint8_t* fileContent) 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_X86_64 ) + 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 <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x07; } 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) + : fHeader(NULL), fLength(fileLength), fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fIndirectTableCount(0), + fLocalRelocations(NULL), fLocalRelocationsCount(0), fExternalRelocations(NULL), fExternalRelocationsCount(0), + fRelocBase(0), fWriteableSegmentWithAddrOver4G(false), fFirstSegment(NULL), fFirstWritableSegment(NULL) { // sanity check if ( ! validFile(fileContent) ) @@ -169,6 +204,7 @@ MachOChecker::MachOChecker(const uint8_t* fileContent, uint32_t fileLength, c checkIndirectSymbolTable(); + checkRelocations(); } @@ -284,6 +320,12 @@ void MachOChecker::checkLoadCommands() // keep LINKEDIT segment if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) linkEditSegment = segCmd; + + // cache interesting segments + if ( fFirstSegment == NULL ) + fFirstSegment = segCmd; + if ( (fFirstWritableSegment == NULL) && ((segCmd->initprot() & VM_PROT_WRITE) != 0) ) + fFirstWritableSegment = segCmd; // check section ranges const macho_section

* const sectionsStart = (macho_section

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

)); @@ -364,6 +406,22 @@ void MachOChecker::checkLoadCommands() throw "indirect symbol table not in __LINKEDIT"; if ( (dsymtab->indirectsymoff()+fIndirectTableCount*8) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) throw "indirect symbol table not in __LINKEDIT"; + fLocalRelocationsCount = dsymtab->nlocrel(); + if ( fLocalRelocationsCount != 0 ) { + fLocalRelocations = (const macho_relocation_info

*)((char*)fHeader + dsymtab->locreloff()); + if ( dsymtab->locreloff() < linkEditSegment->fileoff() ) + throw "local relocations not in __LINKEDIT"; + if ( (dsymtab->locreloff()+fLocalRelocationsCount*sizeof(macho_relocation_info

)) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) + throw "local relocations not in __LINKEDIT"; + } + fExternalRelocationsCount = dsymtab->nextrel(); + if ( fExternalRelocationsCount != 0 ) { + fExternalRelocations = (const macho_relocation_info

*)((char*)fHeader + dsymtab->extreloff()); + if ( dsymtab->extreloff() < linkEditSegment->fileoff() ) + throw "local relocations not in __LINKEDIT"; + if ( (dsymtab->extreloff()+fExternalRelocationsCount*sizeof(macho_relocation_info

)) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) + throw "local relocations not in __LINKEDIT"; + } } break; } @@ -374,7 +432,7 @@ void MachOChecker::checkLoadCommands() if ( fStrings == NULL ) throw "missing symbol table"; - + fRelocBase = this->relocBase(); } @@ -430,6 +488,177 @@ void MachOChecker::checkIndirectSymbolTable() } + +template <> +ppc::P::uint_t MachOChecker::relocBase() +{ + if ( fHeader->flags() & MH_SPLIT_SEGS ) + return fFirstWritableSegment->vmaddr(); + else + return fFirstSegment->vmaddr(); +} + +template <> +ppc64::P::uint_t MachOChecker::relocBase() +{ + if ( fWriteableSegmentWithAddrOver4G ) + return fFirstWritableSegment->vmaddr(); + else + return fFirstSegment->vmaddr(); +} + +template <> +x86::P::uint_t MachOChecker::relocBase() +{ + if ( fHeader->flags() & MH_SPLIT_SEGS ) + return fFirstWritableSegment->vmaddr(); + else + return fFirstSegment->vmaddr(); +} + +template <> +x86_64::P::uint_t MachOChecker::relocBase() +{ + // check for split-seg + return fFirstWritableSegment->vmaddr(); +} + + +template +bool MachOChecker::addressInWritableSegment(pint_t address) +{ + 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; + if ( (segCmd->initprot() & VM_PROT_WRITE) != 0 ) { + if ( (address >= segCmd->vmaddr()) && (address < segCmd->vmaddr()+segCmd->vmsize()) ) + return true; + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + return false; +} + + +template <> +void MachOChecker::checkExternalReloation(const macho_relocation_info

* reloc) +{ + // FIX +} + +template <> +void MachOChecker::checkExternalReloation(const macho_relocation_info

* reloc) +{ + if ( reloc->r_length() != 3 ) + throw "bad external relocation length"; + if ( reloc->r_type() != GENERIC_RELOC_VANILLA ) + throw "unknown external relocation type"; + if ( reloc->r_pcrel() != 0 ) + throw "bad external relocation pc_rel"; + if ( reloc->r_extern() == 0 ) + throw "local relocation found with external relocations"; + if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) + throw "local relocation address not in writable segment"; + // FIX: check r_symbol +} + +template <> +void MachOChecker::checkExternalReloation(const macho_relocation_info

* reloc) +{ + // FIX +} + + +template <> +void MachOChecker::checkExternalReloation(const macho_relocation_info

* reloc) +{ + if ( reloc->r_length() != 3 ) + throw "bad external relocation length"; + if ( reloc->r_type() != X86_64_RELOC_UNSIGNED ) + throw "unknown external relocation type"; + if ( reloc->r_pcrel() != 0 ) + throw "bad external relocation pc_rel"; + if ( reloc->r_extern() == 0 ) + throw "local relocation found with external relocations"; + if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) + throw "exernal relocation address not in writable segment"; + // FIX: check r_symbol +} + +template <> +void MachOChecker::checkLocalReloation(const macho_relocation_info

* reloc) +{ + if ( reloc->r_address() & R_SCATTERED ) { + // scattered + const macho_scattered_relocation_info

* sreloc = (const macho_scattered_relocation_info

*)reloc; + // FIX + + } + else { + // FIX + if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) + throw "local relocation address not in writable segment"; + } +} + + +template <> +void MachOChecker::checkLocalReloation(const macho_relocation_info

* reloc) +{ + if ( reloc->r_length() != 3 ) + throw "bad local relocation length"; + if ( reloc->r_type() != GENERIC_RELOC_VANILLA ) + throw "unknown local relocation type"; + if ( reloc->r_pcrel() != 0 ) + throw "bad local relocation pc_rel"; + if ( reloc->r_extern() != 0 ) + throw "external relocation found with local relocations"; + if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) + throw "local relocation address not in writable segment"; +} + +template <> +void MachOChecker::checkLocalReloation(const macho_relocation_info

* reloc) +{ + // FIX +} + +template <> +void MachOChecker::checkLocalReloation(const macho_relocation_info

* reloc) +{ + if ( reloc->r_length() != 3 ) + throw "bad local relocation length"; + if ( reloc->r_type() != X86_64_RELOC_UNSIGNED ) + throw "unknown local relocation type"; + if ( reloc->r_pcrel() != 0 ) + throw "bad local relocation pc_rel"; + if ( reloc->r_extern() != 0 ) + throw "external relocation found with local relocations"; + if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) + throw "local relocation address not in writable segment"; +} + + + +template +void MachOChecker::checkRelocations() +{ + const macho_relocation_info

* const externRelocsEnd = &fExternalRelocations[fExternalRelocationsCount]; + for (const macho_relocation_info

* reloc = fExternalRelocations; reloc < externRelocsEnd; ++reloc) { + this->checkExternalReloation(reloc); + } + + const macho_relocation_info

* const localRelocsEnd = &fLocalRelocations[fLocalRelocationsCount]; + for (const macho_relocation_info

* reloc = fLocalRelocations; reloc < localRelocsEnd; ++reloc) { + this->checkLocalReloation(reloc); + } +} + + static void check(const char* path) { struct stat stat_buf; @@ -440,7 +669,9 @@ static void check(const char* path) 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); + uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if ( p == ((uint8_t*)(-1)) ) + throw "cannot map file"; ::close(fd); const mach_header* mh = (mach_header*)p; if ( mh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { @@ -465,6 +696,12 @@ static void check(const char* path) else throw "in universal file, ppc64 slice does not contain ppc64 mach-o"; } + else if ( archs[i].cputype == CPU_TYPE_X86_64 ) { + if ( MachOChecker::validFile(p + archs[i].offset) ) + MachOChecker::make(p + archs[i].offset, archs[i].size, path); + else + throw "in universal file, x86_64 slice does not contain x86_64 mach-o"; + } else { throw "in universal file, unknown architecture slice"; } @@ -479,6 +716,9 @@ static void check(const char* 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"; } diff --git a/src/rebase.cpp b/src/rebase.cpp new file mode 100644 index 0000000..ab62f9b --- /dev/null +++ b/src/rebase.cpp @@ -0,0 +1,907 @@ +/* -*- 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 +#include +#include +#include +#include +#include + +#include "MachOFileAbstraction.hpp" +#include "Architectures.hpp" + + +static bool verbose = false; + +__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; +} + + +class AbstractRebaser +{ +public: + virtual cpu_type_t getArchitecture() const = 0; + virtual uint64_t getBaseAddress() const = 0; + virtual uint64_t getVMSize() const = 0; + virtual void setBaseAddress(uint64_t) = 0; +}; + + +template +class Rebaser : public AbstractRebaser +{ +public: + Rebaser(const void* machHeader); + virtual ~Rebaser() {} + + virtual cpu_type_t getArchitecture() const; + virtual uint64_t getBaseAddress() const; + virtual uint64_t getVMSize() const; + virtual void setBaseAddress(uint64_t); + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + struct vmmap { pint_t vmaddr; pint_t vmsize; pint_t fileoff; }; + + void setRelocBase(); + void buildSectionTable(); + void adjustLoadCommands(); + void adjustSymbolTable(); + void adjustDATA(); + void doLocalRelocation(const macho_relocation_info

* reloc); + pint_t* mappedAddressForVMAddress(uint32_t vmaddress); + + const macho_header

* fHeader; + pint_t fOrignalVMRelocBaseAddress; + pint_t fSlide; + pint_t fRelocBase; + std::vector fVMMApping; +}; + + + +class MultiArchRebaser +{ +public: + MultiArchRebaser(const char* path, bool writable=false); + ~MultiArchRebaser(); + + const std::vector& getArchs() const { return fRebasers; } + void commit(); + +private: + std::vector fRebasers; + void* fMappingAddress; + uint64_t fFileSize; +}; + + + +MultiArchRebaser::MultiArchRebaser(const char* path, bool writable) + : fMappingAddress(0), fFileSize(0) +{ + // map in whole file + int fd = ::open(path, (writable ? O_RDWR : O_RDONLY), 0); + if ( fd == -1 ) + throwf("can't open file, errno=%d", errno); + struct stat stat_buf; + if ( fstat(fd, &stat_buf) == -1) + throwf("can't stat open file %s, errno=%d", path, errno); + if ( stat_buf.st_size < 20 ) + throwf("file too small %s", path); + const int prot = writable ? (PROT_READ | PROT_WRITE) : PROT_READ; + const int flags = writable ? (MAP_FILE | MAP_SHARED) : (MAP_FILE | MAP_PRIVATE); + uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, prot, flags, fd, 0); + if ( p == (uint8_t*)(-1) ) + throwf("can't map file %s, errno=%d", path, errno); + ::close(fd); + + // if fat file, process each architecture + const fat_header* fh = (fat_header*)p; + const mach_header* mh = (mach_header*)p; + if ( fh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + // Fat header is always big-endian + const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); + for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { + uint32_t fileOffset = OSSwapBigToHostInt32(archs[i].offset); + try { + switch ( OSSwapBigToHostInt32(archs[i].cputype) ) { + case CPU_TYPE_POWERPC: + fRebasers.push_back(new Rebaser(&p[fileOffset])); + break; + case CPU_TYPE_POWERPC64: + fRebasers.push_back(new Rebaser(&p[fileOffset])); + break; + case CPU_TYPE_I386: + fRebasers.push_back(new Rebaser(&p[fileOffset])); + break; + case CPU_TYPE_X86_64: + fRebasers.push_back(new Rebaser(&p[fileOffset])); + break; + default: + throw "unknown file format"; + } + } + catch (const char* msg) { + fprintf(stderr, "rebase warning: %s for %s\n", msg, path); + } + } + } + else { + try { + if ( (OSSwapBigToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC)) { + fRebasers.push_back(new Rebaser(mh)); + } + else if ( (OSSwapBigToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC64)) { + fRebasers.push_back(new Rebaser(mh)); + } + else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_I386)) { + fRebasers.push_back(new Rebaser(mh)); + } + else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_X86_64)) { + fRebasers.push_back(new Rebaser(mh)); + } + else { + throw "unknown file format"; + } + } + catch (const char* msg) { + fprintf(stderr, "rebase warning: %s for %s\n", msg, path); + } + } + + fMappingAddress = p; + fFileSize = stat_buf.st_size; +} + + +MultiArchRebaser::~MultiArchRebaser() +{ + ::munmap(fMappingAddress, fFileSize); +} + +void MultiArchRebaser::commit() +{ + ::msync(fMappingAddress, fFileSize, MS_ASYNC); +} + + + +template +Rebaser::Rebaser(const void* machHeader) + : fHeader((const macho_header

*)machHeader) +{ + switch ( fHeader->filetype() ) { + case MH_DYLIB: + if ( (fHeader->flags() & MH_SPLIT_SEGS) != 0 ) + throw "split-seg dylibs cannot be rebased"; + break; + case MH_BUNDLE: + break; + default: + throw "file is not a dylib or bundle"; + } + +} + +template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_POWERPC; } +template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_POWERPC64; } +template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_I386; } +template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_X86_64; } + + +template +uint64_t Rebaser::getBaseAddress() const +{ + uint64_t lowestSegmentAddress = LLONG_MAX; + 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; + if ( segCmd->vmaddr() < lowestSegmentAddress ) { + lowestSegmentAddress = segCmd->vmaddr(); + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + return lowestSegmentAddress; +} + +template +uint64_t Rebaser::getVMSize() const +{ + const macho_segment_command

* highestSegmentCmd = NULL; + 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; + if ( (highestSegmentCmd == NULL) || (segCmd->vmaddr() > highestSegmentCmd->vmaddr()) ) { + highestSegmentCmd = segCmd; + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + + return ((highestSegmentCmd->vmaddr() + highestSegmentCmd->vmsize() - this->getBaseAddress() + 4095) & (-4096)); +} + + +template +void Rebaser::setBaseAddress(uint64_t addr) +{ + // calculate slide + fSlide = addr - this->getBaseAddress(); + + // compute base address for relocations + this->setRelocBase(); + + // build cache of section index to section + this->buildSectionTable(); + + // update load commands + this->adjustLoadCommands(); + + // update symbol table + this->adjustSymbolTable(); + + // update writable segments that have internal pointers + this->adjustDATA(); +} + +template +void Rebaser::adjustLoadCommands() +{ + const macho_segment_command

* highestSegmentCmd = NULL; + 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) { + switch ( cmd->cmd() ) { + case LC_ID_DYLIB: + if ( (fHeader->flags() & MH_PREBOUND) != 0 ) { + // clear timestamp so that any prebound clients are invalidated + macho_dylib_command

* dylib = (macho_dylib_command

*)cmd; + dylib->set_timestamp(1); + } + break; + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + if ( (fHeader->flags() & MH_PREBOUND) != 0 ) { + // clear expected timestamps so that this image will load with invalid prebinding + macho_dylib_command

* dylib = (macho_dylib_command

*)cmd; + dylib->set_timestamp(2); + } + break; + case macho_routines_command

::CMD: + // update -init command + { + struct macho_routines_command

* routines = (struct macho_routines_command

*)cmd; + routines->set_init_address(routines->init_address() + fSlide); + } + break; + case macho_segment_command

::CMD: + // update segment commands + { + macho_segment_command

* seg = (macho_segment_command

*)cmd; + seg->set_vmaddr(seg->vmaddr() + fSlide); + macho_section

* const sectionsStart = (macho_section

*)((char*)seg + sizeof(macho_segment_command

)); + macho_section

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

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + sect->set_addr(sect->addr() + fSlide); + } + } + break; + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } +} + + +template +void Rebaser::buildSectionTable() +{ + // build vector of sections + 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

* seg = (macho_segment_command

*)cmd; + vmmap mapping; + mapping.vmaddr = seg->vmaddr(); + mapping.vmsize = seg->vmsize(); + mapping.fileoff = seg->fileoff(); + fVMMApping.push_back(mapping); + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } +} + + +template +void Rebaser::adjustSymbolTable() +{ + const macho_dysymtab_command

* dysymtab = NULL; + macho_nlist

* symbolTable = NULL; + + // get symbol table info + 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) { + switch (cmd->cmd()) { + case LC_SYMTAB: + { + const macho_symtab_command

* symtab = (macho_symtab_command

*)cmd; + symbolTable = (macho_nlist

*)(((uint8_t*)fHeader) + symtab->symoff()); + } + break; + case LC_DYSYMTAB: + dysymtab = (macho_dysymtab_command

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

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + + // walk all exports and slide their n_value + macho_nlist

* lastExport = &symbolTable[dysymtab->iextdefsym()+dysymtab->nextdefsym()]; + for (macho_nlist

* entry = &symbolTable[dysymtab->iextdefsym()]; entry < lastExport; ++entry) { + if ( (entry->n_type() & N_TYPE) == N_SECT ) + entry->set_n_value(entry->n_value() + fSlide); + } + + // walk all local symbols and slide their n_value + macho_nlist

* lastLocal = &symbolTable[dysymtab->ilocalsym()+dysymtab->nlocalsym()]; + for (macho_nlist

* entry = &symbolTable[dysymtab->ilocalsym()]; entry < lastLocal; ++entry) { + if ( entry->n_sect() != NO_SECT ) + entry->set_n_value(entry->n_value() + fSlide); + } +} + +template +void Rebaser::adjustDATA() +{ + const macho_dysymtab_command

* dysymtab = NULL; + + // get symbol table info + 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) { + switch (cmd->cmd()) { + case LC_DYSYMTAB: + dysymtab = (macho_dysymtab_command

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

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + + // walk all local relocations and slide every pointer + const macho_relocation_info

* const relocsStart = (macho_relocation_info

*)(((uint8_t*)fHeader) + dysymtab->locreloff()); + const macho_relocation_info

* const relocsEnd = &relocsStart[dysymtab->nlocrel()]; + for (const macho_relocation_info

* reloc=relocsStart; reloc < relocsEnd; ++reloc) { + this->doLocalRelocation(reloc); + } + + // walk non-lazy-pointers and slide the ones that are LOCAL + cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd() == macho_segment_command

::CMD ) { + const macho_segment_command

* seg = (macho_segment_command

*)cmd; + const macho_section

* const sectionsStart = (macho_section

*)((char*)seg + sizeof(macho_segment_command

)); + const macho_section

* const sectionsEnd = §ionsStart[seg->nsects()]; + const uint32_t* const indirectTable = (uint32_t*)(((uint8_t*)fHeader) + dysymtab->indirectsymoff()); + for(const macho_section

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + if ( (sect->flags() & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS ) { + const uint32_t indirectTableOffset = sect->reserved1(); + uint32_t pointerCount = sect->size() / sizeof(pint_t); + pint_t* nonLazyPointer = (pint_t*)(((uint8_t*)fHeader) + sect->offset()); + for (uint32_t i=0; i < pointerCount; ++i, ++nonLazyPointer) { + if ( E::get32(indirectTable[indirectTableOffset + i]) == INDIRECT_SYMBOL_LOCAL ) { + P::setP(*nonLazyPointer, A::P::getP(*nonLazyPointer) + fSlide); + } + } + } + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + +} + + +template +typename A::P::uint_t* Rebaser::mappedAddressForVMAddress(uint32_t vmaddress) +{ + for(typename std::vector::iterator it = fVMMApping.begin(); it != fVMMApping.end(); ++it) { + //fprintf(stderr, "vmaddr=0x%08lX, vmsize=0x%08lX\n", it->vmaddr, it->vmsize); + if ( (vmaddress >= it->vmaddr) && (vmaddress < (it->vmaddr+it->vmsize)) ) { + return (pint_t*)((vmaddress - it->vmaddr) + it->fileoff + (uint8_t*)fHeader); + } + } + throwf("reloc address 0x%08X not found", vmaddress); +} + + +template <> +void Rebaser::doLocalRelocation(const macho_relocation_info* reloc) +{ + if ( reloc->r_type() == X86_64_RELOC_UNSIGNED ) { + pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress); + P::setP(*addr, P::getP(*addr) + fSlide); + } + else { + throw "invalid relocation type"; + } +} + +template <> +void Rebaser::doLocalRelocation(const macho_relocation_info

* reloc) +{ + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + if ( reloc->r_type() == GENERIC_RELOC_VANILLA ) { + pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress); + P::setP(*addr, P::getP(*addr) + fSlide); + } + } + else { + macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; + if ( sreloc->r_type() == PPC_RELOC_PB_LA_PTR ) { + sreloc->set_r_value( sreloc->r_value() + fSlide ); + } + else { + throw "cannot rebase final linked image with scattered relocations"; + } + } +} + +template <> +void Rebaser::doLocalRelocation(const macho_relocation_info

* reloc) +{ + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + if ( reloc->r_type() == GENERIC_RELOC_VANILLA ) { + pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress); + P::setP(*addr, P::getP(*addr) + fSlide); + } + } + else { + macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; + if ( sreloc->r_type() == GENERIC_RELOC_PB_LA_PTR ) { + sreloc->set_r_value( sreloc->r_value() + fSlide ); + } + else { + throw "cannot rebase final linked image with scattered relocations"; + } + } +} + +template +void Rebaser::doLocalRelocation(const macho_relocation_info

* reloc) +{ + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + if ( reloc->r_type() == GENERIC_RELOC_VANILLA ) { + pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress); + P::setP(*addr, P::getP(*addr) + fSlide); + } + } + else { + throw "cannot rebase final linked image with scattered relocations"; + } +} + + +template +void Rebaser::setRelocBase() +{ + // reloc addresses are from the start of the mapped file (base address) + fRelocBase = (pint_t)fHeader; + fOrignalVMRelocBaseAddress = this->getBaseAddress(); + //fprintf(stderr, "fOrignalVMRelocBaseAddress=0x%08X\n", fOrignalVMRelocBaseAddress); +} + +template <> +void Rebaser::setRelocBase() +{ + // reloc addresses either: + // 1) from the base address if no writable segment is > 4GB from base address + // 2) from start of first writable segment + 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; + if ( segCmd->initprot() & VM_PROT_WRITE ) { + if ( (segCmd->vmaddr() + segCmd->vmsize() - this->getBaseAddress()) > 0x100000000ULL ) { + // found writable segment with address > 4GB past base address + fRelocBase = segCmd->fileoff() + (pint_t)fHeader; + fOrignalVMRelocBaseAddress = segCmd->vmaddr(); + return; + } + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + // just use base address + fRelocBase = (pint_t)fHeader; + fOrignalVMRelocBaseAddress = this->getBaseAddress(); +} + +template <> +void Rebaser::setRelocBase() +{ + // reloc addresses are always based from the start of the first writable segment + 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; + if ( segCmd->initprot() & VM_PROT_WRITE ) { + fRelocBase = segCmd->fileoff() + (pint_t)fHeader; + fOrignalVMRelocBaseAddress = segCmd->vmaddr(); + return; + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + throw "no writable segment"; +} + + +static void copyFile(const char* srcFile, const char* dstFile) +{ + // open files + int src = open(srcFile, O_RDONLY); + if ( src == -1 ) + throwf("can't open file %s, errno=%d", srcFile, errno); + struct stat stat_buf; + if ( fstat(src, &stat_buf) == -1) + throwf("can't stat open file %s, errno=%d", srcFile, errno); + + // create new file with all same permissions to hold copy of dylib + ::unlink(dstFile); + int dst = open(dstFile, O_CREAT | O_RDWR | O_TRUNC, stat_buf.st_mode); + if ( dst == -1 ) + throwf("can't create temp file %s, errnor=%d", dstFile, errno); + + // mark source as "don't cache" + (void)fcntl(src, F_NOCACHE, 1); + // we want to cache the dst because we are about to map it in and modify it + + // copy permission bits + if ( chmod(dstFile, stat_buf.st_mode & 07777) == -1 ) + throwf("can't chmod temp file %s, errno=%d", dstFile, errno); + if ( chown(dstFile, stat_buf.st_uid, stat_buf.st_gid) == -1) + throwf("can't chown temp file %s, errno=%d", dstFile, errno); + + // copy contents + ssize_t len; + const uint32_t kBufferSize = 128*1024; + static uint8_t* buffer = NULL; + if ( buffer == NULL ) { + vm_address_t addr = 0; + if ( vm_allocate(mach_task_self(), &addr, kBufferSize, true /*find range*/) == KERN_SUCCESS ) + buffer = (uint8_t*)addr; + else + throw "can't allcoate copy buffer"; + } + while ( (len = read(src, buffer, kBufferSize)) > 0 ) { + if ( write(dst, buffer, len) == -1 ) + throwf("write failure copying feil %s, errno=%d", dstFile, errno); + } + + // close files + int result1 = close(dst); + int result2 = close(src); + if ( (result1 != 0) || (result2 != 0) ) + throw "can't close file"; +} + + +// scan dylibs and collect size info +// calculate new base address for each dylib +// rebase each file +// copy to temp and mmap +// update content +// unmap/flush +// rename + +struct archInfo { + cpu_type_t arch; + uint64_t vmSize; + uint64_t orgBase; + uint64_t newBase; +}; + +struct fileInfo +{ + fileInfo(const char* p) : path(p) {} + + const char* path; + std::vector archs; +}; + +// +// add archInfos to fileInfo for every slice of a fat file +// for ppc, there may be duplicate architectures (with different sub-types) +// +static void setSizes(fileInfo& info, const std::set& onlyArchs) +{ + const MultiArchRebaser mar(info.path); + const std::vector& rebasers = mar.getArchs(); + for(std::set::iterator ait=onlyArchs.begin(); ait != onlyArchs.end(); ++ait) { + for(std::vector::const_iterator rit=rebasers.begin(); rit != rebasers.end(); ++rit) { + AbstractRebaser* rebaser = *rit; + if ( rebaser->getArchitecture() == *ait ) { + archInfo ai; + ai.arch = *ait; + ai.vmSize = rebaser->getVMSize(); + ai.orgBase = rebaser->getBaseAddress(); + ai.newBase = 0; + //fprintf(stderr, "base=0x%llX, size=0x%llX\n", ai.orgBase, ai.vmSize); + info.archs.push_back(ai); + } + } + } +} + +static const char* nameForArch(cpu_type_t arch) +{ + switch( arch ) { + case CPU_TYPE_POWERPC: + return "ppc"; + case CPU_TYPE_POWERPC64: + return "ppca64"; + case CPU_TYPE_I386: + return "i386"; + case CPU_TYPE_X86_64: + return "x86_64"; + } + return "unknown"; +} + +static void rebase(const fileInfo& info) +{ + // generate temp file name + char realFilePath[PATH_MAX]; + if ( realpath(info.path, realFilePath) == NULL ) { + throwf("realpath() failed on %s, errno=%d", info.path, errno); + } + const char* tempPath; + asprintf((char**)&tempPath, "%s_rebase", realFilePath); + + // copy whole file to temp file + copyFile(info.path, tempPath); + + try { + // rebase temp file + MultiArchRebaser mar(tempPath, true); + const std::vector& rebasers = mar.getArchs(); + for(std::vector::const_iterator fait=info.archs.begin(); fait != info.archs.end(); ++fait) { + for(std::vector::const_iterator rit=rebasers.begin(); rit != rebasers.end(); ++rit) { + if ( (*rit)->getArchitecture() == fait->arch ) { + (*rit)->setBaseAddress(fait->newBase); + if ( verbose ) + printf("%8s 0x%0llX -> 0x%0llX %s\n", nameForArch(fait->arch), fait->orgBase, fait->newBase, info.path); + } + } + } + + // flush temp file out to disk + mar.commit(); + + // rename + int result = rename(tempPath, info.path); + if ( result != 0 ) { + throwf("can't swap temporary rebased file: rename(%s,%s) returned errno=%d", tempPath, info.path, errno); + } + + // make sure every really gets out to disk + ::sync(); + } + catch (const char* msg) { + // delete temp file + ::unlink(tempPath); + + // throw exception with file name added + const char* newMsg; + asprintf((char**)&newMsg, "%s for file %s", msg, info.path); + throw newMsg; + } +} + +static uint64_t totalVMSize(cpu_type_t arch, std::vector& files) +{ + uint64_t totalSize = 0; + for(std::vector::iterator fit=files.begin(); fit != files.end(); ++fit) { + fileInfo& fi = *fit; + for(std::vector::iterator fait=fi.archs.begin(); fait != fi.archs.end(); ++fait) { + if ( fait->arch == arch ) + totalSize += fait->vmSize; + } + } + return totalSize; +} + +static uint64_t startAddress(cpu_type_t arch, std::vector& files, uint64_t lowAddress, uint64_t highAddress) +{ + if ( lowAddress != 0 ) + return lowAddress; + else if ( highAddress != 0 ) { + uint64_t totalSize = totalVMSize(arch, files); + if ( highAddress < totalSize ) + throwf("cannot use -high_address 0x%X because total size of images is greater: 0x%X", highAddress, totalSize); + return highAddress - totalSize; + } + else { + if ( (arch == CPU_TYPE_I386) || (arch == CPU_TYPE_POWERPC) ) { + // place dylibs below dyld + uint64_t topAddr = 0x8FE00000; + uint64_t totalSize = totalVMSize(arch, files); + if ( totalSize > topAddr ) + throwf("total size of images (0x%X) does not fit below 0x8FE00000", totalSize); + return topAddr - totalSize; + } + else if ( arch == CPU_TYPE_POWERPC64 ) { + return 0x200000000ULL; + } + else if ( arch == CPU_TYPE_X86_64 ) { + return 0x200000000ULL; + } + else + throw "unknown architecture"; + } +} + +static void usage() +{ + fprintf(stderr, "rebase [-low_address] [-high_address] [-v] [-arch ] files...\n"); +} + + +int main(int argc, const char* argv[]) +{ + std::vector files; + std::set onlyArchs; + uint64_t lowAddress = 0; + uint64_t highAddress = 0; + + try { + // parse command line options + char* endptr; + for(int i=1; i < argc; ++i) { + const char* arg = argv[i]; + if ( arg[0] == '-' ) { + if ( strcmp(arg, "-v") == 0 ) { + verbose = true; + } + else if ( strcmp(arg, "-low_address") == 0 ) { + lowAddress = strtoull(argv[++i], &endptr, 16); + } + else if ( strcmp(arg, "-high_address") == 0 ) { + highAddress = strtoull(argv[++i], &endptr, 16); + } + else if ( strcmp(arg, "-arch") == 0 ) { + const char* arch = argv[++i]; + if ( strcmp(arch, "ppc") == 0 ) + onlyArchs.insert(CPU_TYPE_POWERPC); + else if ( strcmp(arch, "ppc64") == 0 ) + onlyArchs.insert(CPU_TYPE_POWERPC64); + else if ( strcmp(arch, "i386") == 0 ) + onlyArchs.insert(CPU_TYPE_I386); + else if ( strcmp(arch, "x86_64") == 0 ) + onlyArchs.insert(CPU_TYPE_X86_64); + else + throwf("unknown architecture %s", arch); + } + else { + usage(); + throwf("unknown option: %s\n", arg); + } + } + else { + files.push_back(fileInfo(arg)); + } + } + + if ( files.size() == 0 ) + throw "no files specified"; + + // use all architectures if no restrictions specified + if ( onlyArchs.size() == 0 ) { + onlyArchs.insert(CPU_TYPE_POWERPC); + onlyArchs.insert(CPU_TYPE_POWERPC64); + onlyArchs.insert(CPU_TYPE_I386); + onlyArchs.insert(CPU_TYPE_X86_64); + } + + // scan files and collect sizes + for(std::vector::iterator it=files.begin(); it != files.end(); ++it) { + setSizes(*it, onlyArchs); + } + + // assign new base address for each arch + for(std::set::iterator ait=onlyArchs.begin(); ait != onlyArchs.end(); ++ait) { + cpu_type_t arch = *ait; + uint64_t baseAddress = startAddress(arch, files, lowAddress, highAddress); + for(std::vector::iterator fit=files.begin(); fit != files.end(); ++fit) { + fileInfo& fi = *fit; + for(std::vector::iterator fait=fi.archs.begin(); fait != fi.archs.end(); ++fait) { + if ( fait->arch == arch ) { + fait->newBase = baseAddress; + baseAddress += fait->vmSize; + baseAddress = (baseAddress + 4095) & (-4096); // page align + } + } + } + } + + // rebase each file if it contains something rebaseable + for(std::vector::iterator it=files.begin(); it != files.end(); ++it) { + fileInfo& fi = *it; + if ( fi.archs.size() > 0 ) + rebase(fi); + } + + } + catch (const char* msg) { + fprintf(stderr, "rebase failed: %s\n", msg); + return 1; + } + + return 0; +} + + + diff --git a/unit-tests/include/common.makefile b/unit-tests/include/common.makefile index 7d41e76..266eb85 100644 --- a/unit-tests/include/common.makefile +++ b/unit-tests/include/common.makefile @@ -21,14 +21,17 @@ OTOOL = otool ifeq (${ARCH},ppc64) OTOOL = otool64 endif +ifeq (${ARCH},x86_64) +OTOOL = otool64 +endif CC = gcc-4.0 -arch ${ARCH} -CCFLAGS = -Wall -g -std=c99 +CCFLAGS = -Wall -std=c99 ASMFLAGS = CXX = g++-4.0 -arch ${ARCH} -CXXFLAGS = -Wall -g +CXXFLAGS = -Wall RM = rm RMFLAGS = -rf diff --git a/unit-tests/run-all-unit-tests b/unit-tests/run-all-unit-tests index a18560a..3756bd1 100755 --- a/unit-tests/run-all-unit-tests +++ b/unit-tests/run-all-unit-tests @@ -3,7 +3,7 @@ # cd into test-cases directory cd `echo "$0" | sed 's/run-all-unit-tests/test-cases/'` -all_archs="ppc ppc64 i386" +all_archs="ppc ppc64 i386 x86_64" for arch in $all_archs do diff --git a/unit-tests/test-cases/bundle_loader/Makefile b/unit-tests/test-cases/bundle_loader/Makefile new file mode 100644 index 0000000..3b31968 --- /dev/null +++ b/unit-tests/test-cases/bundle_loader/Makefile @@ -0,0 +1,43 @@ +## +# 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 +# -bundle_loader works, and _bar is found in the executable +# and not in libbar.dylib + +run: all + +all: + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib + ${CC} ${CCFLAGS} main.c bar.c -o main + ${CC} ${CCFLAGS} bundle.c -bundle -bundle_loader main libbar.dylib -o bundle.bundle + ${FAIL_IF_BAD_MACHO} bundle.bundle + nm -m bundle.bundle | grep _bar | grep "from executable" | ${PASS_IFF_STDIN} + +clean: + rm libbar.dylib main bundle.bundle + + diff --git a/unit-tests/test-cases/bundle_loader/bar.c b/unit-tests/test-cases/bundle_loader/bar.c new file mode 100644 index 0000000..b737953 --- /dev/null +++ b/unit-tests/test-cases/bundle_loader/bar.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@ + */ + + +// funcation called by a loaded bundle +int bar() +{ + return 1; +} + diff --git a/unit-tests/test-cases/bundle_loader/bundle.c b/unit-tests/test-cases/bundle_loader/bundle.c new file mode 100644 index 0000000..ba78c28 --- /dev/null +++ b/unit-tests/test-cases/bundle_loader/bundle.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 bar(); + +int foo() +{ + return bar(); +} diff --git a/unit-tests/test-cases/bundle_loader/main.c b/unit-tests/test-cases/bundle_loader/main.c new file mode 100644 index 0000000..c9e53f9 --- /dev/null +++ b/unit-tests/test-cases/bundle_loader/main.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@ + */ +#include + +int main() +{ + return 0; +} + diff --git a/unit-tests/test-cases/dead_strip/Makefile b/unit-tests/test-cases/dead_strip/Makefile new file mode 100644 index 0000000..5985e96 --- /dev/null +++ b/unit-tests/test-cases/dead_strip/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 -dead_strip +# +# 1) in a main executable, dead globals are removed +# 2) in a dylib/bundle with -exported_symbols_list, dead globals are removed +# 3) in a dylib/bundle without -exported_symbols_list, dead globals are *not* removed +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c deadwood.c -dead_strip -o main-${ARCH} + ${FAIL_IF_BAD_MACHO} main-${ARCH} + nm -j main-${ARCH} | egrep 'dead_wood|dead_door' | ${FAIL_IF_STDIN} + ${CC} ${CCFLAGS} -dynamiclib main.c deadwood.c -dead_strip -exported_symbols_list main.exp -o dylib-${ARCH} + ${FAIL_IF_BAD_MACHO} dylib-${ARCH} + nm -j dylib-${ARCH} | grep dead | ${FAIL_IF_STDIN} + ${CC} ${CCFLAGS} -dynamiclib main.c deadwood.c -dead_strip -o dylib2-${ARCH} + ${FAIL_IF_BAD_MACHO} dylib2-${ARCH} + nm -j dylib2-${ARCH} | grep dead_door_knob | ${FAIL_IF_EMPTY} + nm -j dylib2-${ARCH} | grep deadwood | ${PASS_IFF_EMPTY} + + +clean: + rm -rf main-${ARCH} dylib-${ARCH} dylib2-${ARCH} + + diff --git a/unit-tests/test-cases/dead_strip/deadwood.c b/unit-tests/test-cases/dead_strip/deadwood.c new file mode 100644 index 0000000..176ec78 --- /dev/null +++ b/unit-tests/test-cases/dead_strip/deadwood.c @@ -0,0 +1,11 @@ + + +// deadwood() is local to its linkage unit and is unsed, +// so reference to undef() is ok + +extern void undef(); + +void dead_wood() __attribute__((visibility("hidden"))); +void dead_wood() { undef(); } + + diff --git a/unit-tests/test-cases/dead_strip/main.c b/unit-tests/test-cases/dead_strip/main.c new file mode 100644 index 0000000..029aed9 --- /dev/null +++ b/unit-tests/test-cases/dead_strip/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@ + */ + +int main() +{ + return 0; +} + + + +void dead_door_knob() { } diff --git a/unit-tests/test-cases/dead_strip/main.exp b/unit-tests/test-cases/dead_strip/main.exp new file mode 100644 index 0000000..4eb9e89 --- /dev/null +++ b/unit-tests/test-cases/dead_strip/main.exp @@ -0,0 +1 @@ +_main diff --git a/unit-tests/test-cases/dwarf-debug-notes-r/Makefile b/unit-tests/test-cases/dwarf-debug-notes-r/Makefile new file mode 100644 index 0000000..b7f4eeb --- /dev/null +++ b/unit-tests/test-cases/dwarf-debug-notes-r/Makefile @@ -0,0 +1,61 @@ +## +# 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 after +# some of the .o files are merged with ld -r. +# 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: foobar.o main.o crt1.o + ${CXX} ${CCXXFLAGS} foobar.o main.o -o dwarf-test-${ARCH} -L. + ${FAIL_IF_BAD_MACHO} dwarf-test-${ARCH} + nm -ap dwarf-test-${ARCH} | ./stabs-filter.pl > dwarf-test-${ARCH}.stabs + ${PASS_IFF} diff dwarf-test-${ARCH}.stabs expected-stabs + +foobar.o : foo.o bar.o + ${LD} -r foo.o bar.o -o foobar.o + +foo.o : foo.cxx + ${CXX} ${CCXXFLAGS} -gdwarf-2 foo.cxx -c -o foo.o -mdynamic-no-pic + +bar.o : bar.cxx + ${CXX} ${CCXXFLAGS} -gdwarf-2 bar.cxx -c -o bar.o -mdynamic-no-pic + +main.o : main.cxx + ${CXX} ${CCXXFLAGS} -gdwarf-2 main.cxx -c -o main.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-test-${ARCH} foo.o bar.o foobar.o main.o crt1.o dwarf-test-${ARCH}.stabs + + diff --git a/unit-tests/test-cases/dwarf-debug-notes-r/bar.cxx b/unit-tests/test-cases/dwarf-debug-notes-r/bar.cxx new file mode 100644 index 0000000..c3f6a18 --- /dev/null +++ b/unit-tests/test-cases/dwarf-debug-notes-r/bar.cxx @@ -0,0 +1,4 @@ +int bar() +{ + return 10; +} diff --git a/unit-tests/test-cases/dwarf-debug-notes-r/expected-stabs b/unit-tests/test-cases/dwarf-debug-notes-r/expected-stabs new file mode 100644 index 0000000..4853628 --- /dev/null +++ b/unit-tests/test-cases/dwarf-debug-notes-r/expected-stabs @@ -0,0 +1,24 @@ +0000 SO CWD/ +0000 SO foo.cxx +0001 OSO CWD/foo.o +0000 BNSYM +0000 FUN __Z3foov +0000 FUN +0000 ENSYM +0000 SO +0000 SO CWD/ +0000 SO bar.cxx +0001 OSO CWD/bar.o +0000 BNSYM +0000 FUN __Z3barv +0000 FUN +0000 ENSYM +0000 SO +0000 SO CWD/ +0000 SO main.cxx +0001 OSO CWD/main.o +0000 BNSYM +0000 FUN _main +0000 FUN +0000 ENSYM +0000 SO diff --git a/unit-tests/test-cases/dwarf-debug-notes-r/foo.cxx b/unit-tests/test-cases/dwarf-debug-notes-r/foo.cxx new file mode 100644 index 0000000..1f46ef4 --- /dev/null +++ b/unit-tests/test-cases/dwarf-debug-notes-r/foo.cxx @@ -0,0 +1,4 @@ +int foo() +{ + return 10; +} diff --git a/unit-tests/test-cases/dwarf-debug-notes-r/main.cxx b/unit-tests/test-cases/dwarf-debug-notes-r/main.cxx new file mode 100644 index 0000000..f8b643a --- /dev/null +++ b/unit-tests/test-cases/dwarf-debug-notes-r/main.cxx @@ -0,0 +1,4 @@ +int main() +{ + return 0; +} diff --git a/unit-tests/test-cases/dwarf-debug-notes-r/stabs-filter.pl b/unit-tests/test-cases/dwarf-debug-notes-r/stabs-filter.pl new file mode 100755 index 0000000..706fd12 --- /dev/null +++ b/unit-tests/test-cases/dwarf-debug-notes-r/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-debug-notes/expected-stabs b/unit-tests/test-cases/dwarf-debug-notes/expected-stabs index 756776e..38f4c8c 100644 --- a/unit-tests/test-cases/dwarf-debug-notes/expected-stabs +++ b/unit-tests/test-cases/dwarf-debug-notes/expected-stabs @@ -1,4 +1,4 @@ -0000 SO CWD +0000 SO CWD/ 0000 SO hello.cxx 0001 OSO CWD/hello.o 0000 BNSYM @@ -11,7 +11,7 @@ 0000 FUN 0000 ENSYM 0000 SO -0000 SO CWD +0000 SO CWD/ 0000 SO other.cxx 0001 OSO CWD/other.o 0000 BNSYM diff --git a/unit-tests/test-cases/filelist/Makefile b/unit-tests/test-cases/filelist/Makefile new file mode 100644 index 0000000..78a1004 --- /dev/null +++ b/unit-tests/test-cases/filelist/Makefile @@ -0,0 +1,48 @@ +## +# 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 + + +PWD = $(shell pwd) + +# +# The point of this test is to check the two forms of the' +# -filelist option +# + +run: all + +all: + ${CC} ${CCFLAGS} -c hello.c -o hello-${ARCH}.o + echo "${PWD}/hello-${ARCH}.o" > "${PWD}/filelist1" + cd /tmp && ${CC} ${CCFLAGS} -arch ${ARCH} -filelist "${PWD}/filelist1" -o "${PWD}/hello-${ARCH}" + ${FAIL_IF_BAD_MACHO} hello-${ARCH} + echo "hello-${ARCH}.o" > "${PWD}/filelist2" + cd /tmp && ${CC} ${CCFLAGS} -arch ${ARCH} -filelist "${PWD}/filelist2,${PWD}" -o "${PWD}/hello-${ARCH}" + ${PASS_IFF_GOOD_MACHO} hello-${ARCH} + +clean: + rm hello-${ARCH} hello-${ARCH}.o filelist1 filelist2 + + diff --git a/unit-tests/test-cases/filelist/hello.c b/unit-tests/test-cases/filelist/hello.c new file mode 100644 index 0000000..14d9363 --- /dev/null +++ b/unit-tests/test-cases/filelist/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/late-link-error/Makefile b/unit-tests/test-cases/late-link-error/Makefile new file mode 100644 index 0000000..209d5a3 --- /dev/null +++ b/unit-tests/test-cases/late-link-error/Makefile @@ -0,0 +1,43 @@ +## +# 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 sanity check that if +# ld errors out during linking, that no output file is remaining +# + +run: all + +all: + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} link_error.s -dynamiclib -o link_error-${ARCH} 2> fail.log + ${FAIL_IFF} cat link_error-${ARCH} 2> fail.log + +clean: + rm link_error-${ARCH} fail.log + + diff --git a/unit-tests/test-cases/late-link-error/link_error.s b/unit-tests/test-cases/late-link-error/link_error.s new file mode 100644 index 0000000..65de2ef --- /dev/null +++ b/unit-tests/test-cases/late-link-error/link_error.s @@ -0,0 +1,18 @@ + + +#if __ppc__ || __ppc64__ + ; illegal absolute load +_foo: lis r2,ha16(_strcmp) +#endif + +#if __i386__ + // illegal absolute load +_foo: movl _strcmp, %eax +#endif + + +#if __x86_64__ + // illegal external load +_foo: movl _strcmp(%rip), %eax +#endif + diff --git a/unit-tests/test-cases/multiple-entry-points/test.s b/unit-tests/test-cases/multiple-entry-points/test.s index dc3b340..4559893 100644 --- a/unit-tests/test-cases/multiple-entry-points/test.s +++ b/unit-tests/test-cases/multiple-entry-points/test.s @@ -35,10 +35,12 @@ _foo3: + .align 2 _bar: nop + .align 2 .globl _xx .globl __xx _xx: @@ -46,6 +48,7 @@ __xx: nop + .align 2 _ok: nop diff --git a/unit-tests/test-cases/objc-references/Makefile b/unit-tests/test-cases/objc-references/Makefile new file mode 100644 index 0000000..169963b --- /dev/null +++ b/unit-tests/test-cases/objc-references/Makefile @@ -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@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# The point of this test is to verify an Objective-C object file +# is parsed to find the proper class references +# + +run: all + +all: + ${CC} ${CCFLAGS} test.m -c -o test.${ARCH}.o + ${LD} -arch ${ARCH} -r test.${ARCH}.o -o test-r.${ARCH}.o + nm test-r.${ARCH}.o | grep '.objc_class_name_NSObject' | ${FAIL_IF_EMPTY} + nm test-r.${ARCH}.o | grep '.objc_class_name_NSData' | ${FAIL_IF_EMPTY} + nm test-r.${ARCH}.o | grep '.objc_class_name_NSArray' | ${FAIL_IF_EMPTY} + nm test-r.${ARCH}.o | grep '.objc_class_name_NSString' | ${PASS_IFF_STDIN} + +clean: + rm -rf test.${ARCH}.o test.${ARCH}.o.dump + + + #${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump + #grep '.objc_class_name_NSObject' test.${ARCH}.o.dump | ${FAIL_IF_EMPTY} + #grep '.objc_class_name_NSString' test.${ARCH}.o.dump | ${PASS_IFF_STDIN} diff --git a/unit-tests/test-cases/objc-references/test.m b/unit-tests/test-cases/objc-references/test.m new file mode 100644 index 0000000..d845227 --- /dev/null +++ b/unit-tests/test-cases/objc-references/test.m @@ -0,0 +1,52 @@ +/* -*- 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 + + +@interface Foo : NSObject +- (NSString*) foo; +@end + + +@implementation Foo +- (NSString*) foo +{ + return [NSString stringWithUTF8String:"hello"]; +} +@end + + +@interface Bar : NSData +- (NSArray*) bar; +@end + + +@implementation Bar +- (NSArray*) bar +{ + return [NSArray array]; +} +@end + diff --git a/unit-tests/test-cases/rebase-basic/Makefile b/unit-tests/test-cases/rebase-basic/Makefile new file mode 100644 index 0000000..2077033 --- /dev/null +++ b/unit-tests/test-cases/rebase-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 to see that a dylib +# run through the rebase tool is the same as if +# the dylib was originally built at that address +# + +run: all + +all: + ${CC} -arch ${ARCH} -c foo.c -o foo.${ARCH}.o + ${CC} -arch ${ARCH} -c bar.m -o bar.${ARCH}.o + ${CC} -arch ${ARCH} foo.${ARCH}.o bar.${ARCH}.o -dynamiclib -o libfoo.${ARCH}.dylib -framework Foundation -single_module -mmacosx-version-min=10.5 + ${CC} -arch ${ARCH} foo.${ARCH}.o bar.${ARCH}.o -dynamiclib -o libfoo-alt.${ARCH}.dylib -framework Foundation -single_module -mmacosx-version-min=10.5 -seg1addr 0x12340000 -install_name libfoo.${ARCH}.dylib + rebase -arch ${ARCH} -low_address 0x12340000 libfoo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} libfoo.${ARCH}.dylib + ${PASS_IFF} diff libfoo.${ARCH}.dylib libfoo-alt.${ARCH}.dylib + +clean: + rm foo.${ARCH}.o bar.${ARCH}.o libfoo.${ARCH}.dylib libfoo-alt.${ARCH}.dylib + + diff --git a/unit-tests/test-cases/rebase-basic/bar.m b/unit-tests/test-cases/rebase-basic/bar.m new file mode 100644 index 0000000..ff4f2ac --- /dev/null +++ b/unit-tests/test-cases/rebase-basic/bar.m @@ -0,0 +1,13 @@ +#include + +@interface Bar : NSObject + +-(void) blah; + +@end + +@implementation Bar + +-(void) blah {} + +@end \ No newline at end of file diff --git a/unit-tests/test-cases/rebase-basic/foo.c b/unit-tests/test-cases/rebase-basic/foo.c new file mode 100644 index 0000000..15d4ae6 --- /dev/null +++ b/unit-tests/test-cases/rebase-basic/foo.c @@ -0,0 +1,14 @@ + +int foo() { return 10; } + +void* foop = &foo; + +int glob = 5; + +int* globp = &glob; + + +int big[3000]; + + + \ No newline at end of file diff --git a/unit-tests/test-cases/relocs-asm/relocs-asm.s b/unit-tests/test-cases/relocs-asm/relocs-asm.s index 5a40e49..0629125 100644 --- a/unit-tests/test-cases/relocs-asm/relocs-asm.s +++ b/unit-tests/test-cases/relocs-asm/relocs-asm.s @@ -190,6 +190,77 @@ _test_branches: +#if __x86_64__ + .text + .align 2 + + .globl _test_loads +_test_loads: + + # PIC load of a + movl _a(%rip), %eax + + # PIC load of a + addend + movl _a+0x1234(%rip), %eax + + # PIC lea + leaq _a(%rip), %rax + + # PIC lea through GOT + movq _a@GOTPCREL(%rip), %rax + + # PIC access of GOT + pushq _a@GOTPCREL(%rip) + + # PIC lea external through GOT + movq _ax@GOTPCREL(%rip), %rax + + # PIC external access of GOT + pushq _ax@GOTPCREL(%rip) + + # 1-byte store + movb $0x12, _a(%rip) + + # 4-byte store + movl $0x12345678, _a(%rip) + + # test local labels +# lea L1(%rip), %rax ### assembler bug +# movl L0(%rip), %eax ### assembler bug + + 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: @@ -202,6 +273,32 @@ Llocal2: #endif +#if __x86_64__ + .data +L0: .quad _test_branches +_prev: + .quad _test_branches+4 +L1: .quad _test_branches - _test_diffs + .quad _test_branches - _test_diffs + 4 + .long _test_branches - _test_diffs +# .long LCL0-. ### assembler bug: content value should be (address(LCL0) - 0x24) + .quad L1 +# .quad L0 ### assembler bug: should be internal reloc to L0 + .quad _test_branches - . + .quad _test_branches - L1 + .quad L1 - _prev + +# the following generates: _foo cannot be undefined in a subtraction expression +# but it should be ok (it will be a linker error if _foo and _bar are not in same linkage unit) +# .quad _foo - _bar ### assembler bug + + .section __DATA,__data2 +LCL0: .long 2 + + +#endif + + .data _a: .long 0 @@ -212,7 +309,7 @@ _b: .long _test_calls+16 .long _external .long _external+16 -#elif __ppc64__ +#elif __ppc64__ || __x86_64__ .quad _test_calls .quad _test_calls+16 .quad _external diff --git a/unit-tests/test-cases/relocs-c/Makefile b/unit-tests/test-cases/relocs-c/Makefile index b400705..610a02b 100644 --- a/unit-tests/test-cases/relocs-c/Makefile +++ b/unit-tests/test-cases/relocs-c/Makefile @@ -38,9 +38,9 @@ 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} + #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} + #grep "plus" test-r.${ARCH}.o.dump | ${FAIL_IF_STDIN} ${PASS_IFF} diff test.${ARCH}.o.dump test-r.${ARCH}.o.dump clean: diff --git a/unit-tests/test-cases/tentative-to-real/Makefile b/unit-tests/test-cases/tentative-to-real/Makefile new file mode 100644 index 0000000..2115e7b --- /dev/null +++ b/unit-tests/test-cases/tentative-to-real/Makefile @@ -0,0 +1,44 @@ +## +# 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 -r -d +# will transform a tentative definition into a real one. +# + +run: all + +all: + ${CC} ${CCFLAGS} test.c -c -o test.${ARCH}.o + ${LD} -arch ${ARCH} -r test.${ARCH}.o -o test-r.${ARCH}.o + ${OBJECTDUMP} test-r.${ARCH}.o | grep tentative | ${PASS_IFF_STDIN} + ${LD} -arch ${ARCH} -r -d test.${ARCH}.o -o test-r-d.${ARCH}.o + ${OBJECTDUMP} test-r-d.${ARCH}.o | grep tentative | ${PASS_IFF_EMPTY} + +clean: + rm -rf test.${ARCH}.o test-r.${ARCH}.o + + diff --git a/unit-tests/test-cases/tentative-to-real/test.c b/unit-tests/test-cases/tentative-to-real/test.c new file mode 100644 index 0000000..87360fc --- /dev/null +++ b/unit-tests/test-cases/tentative-to-real/test.c @@ -0,0 +1,3 @@ + +// a tentative definition +int a; -- 2.45.2