------ Tagged ld64-47
-
+----- Tagged ld64-59
------ Tagged ld64-46
-
-2006-03-10 Nick Kledzik <kledzik@apple.com>
+2006-06-22 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/4419505> 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 <kledzik@apple.com>
-
- <rdar://problem/4465004> 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
+ <rdar://problem/4596726> 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 <kledzik@apple.com>
-
- <rdar://problem/4471424> crash in stub with 2GB pagezero
- * src/MachOWriterExecutable.hpp: StubAtom<ppc64> can't be no-pic if a large zero-page is used
-
-2006-03-06 Nick Kledzik <kledzik@apple.com>
-
- * src/Options.cpp: addSectionAlignment, warn if -sectalign alignment is not a power of two
-
------ Tagged ld64-45
-
-
-2006-03-06 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/4466930> 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 <kledzik@apple.com>
+ <rdar://problem/4567995> 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 <kledzik@apple.com>
-
- * 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 <kledzik@apple.com>
-
- * 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 <kledzik@apple.com>
-
- * src/MachOReaderRelocatable.hpp: <rdar://problem/4464370> tighten detection of anonymous non-lazy-pointer
-
------ Tagged ld64-42
-
-2006-02-28 Nick Kledzik <kledzik@apple.com>
-
- * src/MachOReaderRelocatable.hpp: fix x86 __IMPORT permissions for class Segment
-
-2006-02-28 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/4461240> SWB: ld64-37 (can't resolve symbol ___dso_handle)
- * src/MachOWriterExecutable.hpp: add class DsoHandleAtom
-
-2006-02-28 Nick Kledzik <kledzik@apple.com>
-
- * unit-tests/test-cases/literals-coalesce-alignment: added test case
- * src/ld.cpp: when coalescing strings pick one with greater alignment
- <rdar://problem/4458660> 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 <kledzik@apple.com>
-
- * 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 <kledzik@apple.com>
-
- <rdar://problem/4454698> 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 <kledzik@apple.com>
-
- * src/Options.cpp: <rdar://problem/4456093> ld64 doesn't realpath(3) B&I tracing paths
+2006-06-21 Nick Kledzik <kledzik@apple.com>
-2006-02-24 Nick Kledzik <kledzik@apple.com>
+ <rdar://problem/4535036> ld64 seems to offset things incorrectly when using -r
+ src/MachOWriterExecutable.hpp: in -r mode, virtual sections don't increment address
- * src/Options.cpp: <rdar://problem/4457078> 9A110: ld64 can't deal with section names >16 chars
-2006-02-23 Nick Kledzik <kledzik@apple.com>
+----- 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 <kledzik@apple.com>
-2006-02-23 Nick Kledzik <kledzik@apple.com>
+ src/rebase.cpp: fix page alignment problem
+ src/rebase.cpp: fix endianess problem with local non-lazy pointers
- <rdar://problem/4455927> ld64 creates corrupt executables (and has malloc errors) with -headerpad option
- * src/MachOWriterExecutable.hpp: Change LoadCommandsPaddingAtom<A>::setSize() to update fLargestAtomSize
- * unit-tests/test-cases/header-pad: added
+2006-06-15 Nick Kledzik <kledzik@apple.com>
-2006-02-23 Nick Kledzik <kledzik@apple.com>
+ src/rebase.cpp: fix to build in CurryWeed
+ ld64.xcodeproj/project.pbxproj: fix to build properly in CurryWeed
- <rdar://problem/4455192> ld64 creates invalid static executables
- * src/MachOWriterExecutable.hpp: Change MachHeaderAtom<A>::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 <kledzik@apple.com>
-2006-02-22 Nick Kledzik <kledzik@apple.com>
+ <rdar://problem/4495309> 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: <rdar://problem/4453468> chnage printf on unknown arg to a throw
+2006-06-15 Nick Kledzik <kledzik@apple.com>
------ Tagged ld64-39
+ <rdar://problem/4484369> SECTION_ATTRIBUTES unset in ppc64 mach-o header
+ src/MachOWriterExecutable.hpp: add section attribute for sections with code
-2006-02-20 Nick Kledzik <kledzik@apple.com>
+2006-06-15 Nick Kledzik <kledzik@apple.com>
- * unit-tests/test-cases/read-only-relocs: added new test case
- * src/MachOWriterExecutable.hpp: <rdar://problem/4448922> detect and error on relocs in read-only sections
- * src/MachOReaderRelocatable.hpp: fix parsing of i386 absolute addressing relocs
+ <rdar://problem/4569407> 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 <kledzik@apple.com>
+2006-06-15 Nick Kledzik <kledzik@apple.com>
- * unit-tests/test-cases/stabs-coalesce: added new test case
- * src/ld.cpp.hpp: <rdar://problem/4449226> in collectStabs removed unused stabs
+ <rdar://problem/4582999> 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 <kledzik@apple.com>
-2006-02-17 Nick Kledzik <kledzik@apple.com>
+ <rdar://problem/4585335> 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: <rdar://problem/4434578> set correct n_sect field of stabs
+2006-06-13 Nick Kledzik <kledzik@apple.com>
-2006-02-15 Nick Kledzik <kledzik@apple.com>
+ src/Options.cpp: allow -image_base as an alias for -seg1addr
- * src/MachOReaderArchive.hpp: <rdar://problem/4441920> 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 <kledzik@apple.com>
------ Tagged ld64-37
+ <rdar://problem/4585115> 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 <echristo@apple.com>
+2006-06-13 Nick Kledzik <kledzik@apple.com>
- * src/MachOWriterExecutable.hpp (assignFileOffsets): Simplify. Add comments.
- Adjust whitespace.
+ <rdar://problem/4584355> 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 <kledzik@apple.com>
+2006-06-12 Nick Kledzik <kledzik@apple.com>
- * src/MachOWriterExecutable.hpp: in Writer<x86>::fixUpReferenceRelocatable() fix kPCRel32 for external case
+ <rdar://problem/4583347> -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 <kledzik@apple.com>
- * 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 <kledzik@apple.com>
-2006-02-12 Nick Kledzik <kledzik@apple.com>
-
- * src/MachOReaderRelocatable.hpp: <rdar://problem/4440880> fix use of first zero-length c-string in .o file
-
-2006-02-12 Nick Kledzik <kledzik@apple.com>
-
- * src/MachOReaderRelocatable.hpp: <rdar://problem/4440905> fix uninitialized fAlignment
-
-2006-02-12 Nick Kledzik <kledzik@apple.com>
-
- * 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 <kledzik@apple.com>
-
- * src/MachOReaderRelocatable.cpp: rdar://problem/4438677 Handle when a .o file dwarf line info entries but no functions
-
-2006-02-08 Nick Kledzik <kledzik@apple.com>
-
- * src/MachOWriterExecutable.cpp: Properly set address of first TEXT section
- Keep S_COALESCED attribute for __eh_frame
-
-2006-02-08 Nick Kledzik <kledzik@apple.com>
-
- * 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 <kledzik@apple.com>
-
- * 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 <kledzik@apple.com>
-
- * 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 <kledzik@apple.com>
-
- * 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 <kledzik@apple.com>
-
- * 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 <kledzik@apple.com>
-
- * 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 <kledzik@apple.com>
-
- * src/ld.cpp: <rdar://problem/4432917> fix -no_arch_warnings
- <rdar://problem/4432932> 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 <kledzik@apple.com>
-
- * 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 <kledzik@apple.com>
-
- * 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
+ <rdar://problem/4548935> 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 <kledzik@apple.com>
+2006-06-10 Nick Kledzik <kledzik@apple.com>
- * 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 <kledzik@apple.com>
+2006-06-06 Nick Kledzik <kledzik@apple.com>
- * src/MachOReaderRelocatable.hpp: support anonymous zero fill atoms
+ <rdar://problem/4565088> 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 <kledzik@apple.com>
-2006-02-02 Nick Kledzik <kledzik@apple.com>
+ <rdar://problem/4572702> -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 <echristo@apple.com>
-2006-02-01 Nick Kledzik <kledzik@apple.com>
+ 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 <kledzik@apple.com>
+2006-05-23 Nick Kledzik <kledzik@apple.com>
- * src/MachOReaderRelocatable.hpp: don't try to parse debug_line dwarf if no symboled atoms
+ <rdar://problem/4558079> 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 <kledzik@apple.com>
-2006-02-01 Eric Christopher <echristo@apple.com>
+ <rdar://problem/4556982> ld64 spends much time in mach_o::relocatable::Reader<x86_64>::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 <kledzik@apple.com>
+ <rdar://problem/4535044> 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 <kledzik@apple.com>
+2006-05-19 Nick Kledzik <kledzik@apple.com>
- * 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
+ <rdar://problem/4544001> ld64 does not honor -arch_multiple
+ * ld.cpp: If fOptions.printArchPrefix(), add architecture name to error message
-2006-01-31 Nick Kledzik <kledzik@apple.com>
+2006-05-19 Nick Kledzik <kledzik@apple.com>
- * ld64.xcodeproj/project.pbxproj : Make buildable on Leopard
- * src/MachOFileAbstraction.hpp: make buildable without latest cctools headers
+ <rdar://problem/4555973> 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 <kledzik@apple.com>
+2006-05-19 Nick Kledzik <kledzik@apple.com>
- * 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
+ <rdar://problem/4548803> "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 <echristo@apple.com>
+----- Tagged ld64-55
- * src/ExecutableFile.h: Indent.
+2006-05-18 Nick Kledzik <kledzik@apple.com>
-2006-01-30 Nick Kledzik <kledzik@apple.com>
+ <rdar://problem/4534339> 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 <kledzik@apple.com>
-2006-01-30 Nick Kledzik <kledzik@apple.com>
+ <rdar://problem/4552825> 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 <kledzik@apple.com>
-2006-01-29 Nick Kledzik <kledzik@apple.com>
+ <rdar://problem/4553555> 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 <kledzik@apple.com>
-2006-01-29 Nick Kledzik <kledzik@apple.com>
+ <rdar://problem/4551990> -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 <kledzik@apple.com>
-2006-01-29 Nick Kledzik <kledzik@apple.com>
+ * 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 <echristo@apple.com>
+2006-05-11 Nick Kledzik <kledzik@apple.com>
- * src/ld.cpp (Linker::syntesizeStabs): Correct spelling. Update all uses.
+ <rdar://problem/4545108> CF-393 failes to link for x86_64
+ * src/MachOWriterExecutable.cpp: fix sign extension for Rel32 relocs in Writer<x86_64>::fixUpReferenceRelocatable
-2006-01-27 Eric Christopher <echristo@apple.com>
+2006-05-11 Nick Kledzik <kledzik@apple.com>
- * 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.
+ <rdar://problem/4501434> 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 <kledzik@apple.com>
- * 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 <kledzik@apple.com>
-2006-01-27 Eric Christopher <echristo@apple.com>
+ <rdar://problem/4543754> 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 <kledzik@apple.com>
+2006-05-08 Nick Kledzik <kledzik@apple.com>
- * src/MachOWriterExecutable.hpp: handle NULL strings in SO debug notes
+ <rdar://problem/3894083> 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 <kledzik@apple.com>
+----- Tagged ld64-53
- * src/MachOWriterExecutable.hpp: fix header padding calculation and thread state
+2006-05-05 Nick Kledzik <kledzik@apple.com>
-2006-01-26 Nick Kledzik <kledzik@apple.com>
+ * 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 <kledzik@apple.com>
-2006-01-26 Nick Kledzik <kledzik@apple.com>
+ <rdar://problem/4147604> 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 <kledzik@apple.com>
-2006-01-25 Nick Kledzik <kledzik@apple.com>
+ * 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 <kledzik@apple.com>
+2006-05-02 Nick Kledzik <kledzik@apple.com>
- * src/MachOWriterExecutable.hpp: fix bugs in stub/lazy-pointer synthesis
+ <rdar://problem/4496250> 64-bit main executables should have 4GB zero page by default
+ * src/Opptions.cpp: change default pagezero_size to 4GB for ppc64
+ <rdar://problem/4492850> 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 <echristo@apple.com>
+2006-05-02 Nick Kledzik <kledzik@apple.com>
- * 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 <kledzik@apple.com>
+----- Tagged ld64-52.1
- * src/MachOReaderRelocatable.hpp: better C++ eh parsing
+2006-05-01 Nick Kledzik <kledzik@apple.com>
-2006-01-23 Eric Christopher <echristo@apple.com>
+ * 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 <kledzik@apple.com>
+2006-04-28 Nick Kledzik <kledzik@apple.com>
- * unit-tests/test-cases/archive-basic: added
- * src/ld.cpp: fix shadowed local variable
- * src/FileAbstraction.hpp: <rdar://problem/4417372> ld64 shouldn't inline when building debug
+ <rdar://problem/4513304> 64-bit: 9A152 TextEdit crashes in dlopen on bring-up
+ * src/MachOReaderRelocatable.cpp: rework anonymous non-lazy-pointer detection
-2006-01-23 Nick Kledzik <kledzik@apple.com>
+2006-04-28 Nick Kledzik <kledzik@apple.com>
- * 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
+ <rdar://problem/4528054> 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 <kledzik@apple.com>
- * src/MachOReaderRelocatable.hpp: tweaks to synthesizing debug notes
+2006-04-27 Nick Kledzik <kledzik@apple.com>
-2006-01-16 Nick Kledzik <kledzik@apple.com>
+ <rdar://problem/4498971> 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 <kledzik@apple.com>
-2006-01-11 Nick Kledzik <kledzik@apple.com>
+ * 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 <kledzik@apple.com>
+2006-04-14 Nick Kledzik <kledzik@apple.com>
- * 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 <kledzik@apple.com>
+----- 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 <kledzik@apple.com>
-2006-01-09 Nick Kledzik <kledzik@apple.com>
+ <rdar://problem/4513304> 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 <kledzik@apple.com>
+----- 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 <kledzik@apple.com>
-2006-01-09 Nick Kledzik <kledzik@apple.com>
+ <rdar://problem/4499168> 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 <kledzik@apple.com>
-2006-01-05 Nick Kledzik <kledzik@apple.com>
+ <rdar://problem/4498391> 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 <kledzik@apple.com>
+2006-03-31 Nick Kledzik <kledzik@apple.com>
- * src/MachOReaderDylib.hpp: support MH_DYLIB_STUB
- * src/MachOReaderRelocatable.hpp: Add Geoff's comp unit extractor
+ <rdar://problem/4496499> ld64 should remove generated file if link errors out
+ * src/MachOWriterExecutable.hpp: catch exceptions in Writer<A>::write(), delete output file, and rethrow
-2006-01-05 Nick Kledzik <kledzik@apple.com>
- 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 <echristo@apple.com>
+2006-03-29 Nick Kledzik <kledzik@apple.com>
- * 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 <kledzik@apple.com>
+2006-03-29 Nick Kledzik <kledzik@apple.com>
- * 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 <kledzik@apple.com>
+2006-03-28 Nick Kledzik <kledzik@apple.com>
- * 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 <kledzik@apple.com>
+----- Tagged ld64-49.1
- * src/MachOReaderRelocatable.hpp: fixes for Objective-C
- * unit-tests/test-cases/relocs-objc: Added
+2006-03-25 Nick Kledzik <kledzik@apple.com>
-2005-12-22 Nick Kledzik <kledzik@apple.com>
+ * 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 <kledzik@apple.com>
+2006-03-24 Nick Kledzik <kledzik@apple.com>
- * 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
+ <rdar://problem/4488113> 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 <echristo@apple.com>
+2006-03-23 Nick Kledzik <kledzik@apple.com>
- * 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<x86>::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 <kledzik@apple.com>
-2005-12-15 Nick Kledzik <kledzik@apple.com>
+ * 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 <kledzik@apple.com>
+2006-03-21 Nick Kledzik <kledzik@apple.com>
- * src/ELFFileAbstraction.hpp: Added
- * src/ELFReaderRelocatable.hpp: Added
- * Lot of fixes for new architecture
+ <rdar://problem/4481406> 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 <kledzik@apple.com>
+2006-03-21 Nick Kledzik <kledzik@apple.com>
- * src/MachOReaderRelocatable.hpp: check for S_ATTR_DEBUG and ignore those sections
- * unit-tests/test-cases/dwarf-ignore: added
+ <rdar://problem/4180168> .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 <kledzik@apple.com>
+2006-03-21 Nick Kledzik <kledzik@apple.com>
- * Added test harness and three initial tests:
- relocs-asm, relocs-c, and hello-world
+ <rdar://problem/4473742> 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 <kledzik@apple.com>
- * 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 <kledzik@apple.com>
- * src/ObjectDump.cpp: Add command line options: -no_content, -stabs, -no_sort
+----- Tagged ld64-46
-2005-12-11 Eric Christopher <echristo@apple.com>
+2006-03-10 Nick Kledzik <kledzik@apple.com>
- * src/Options.cpp: Reformat.
- * src/Options.h: Ditto.
+ <rdar://problem/4419505> 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 <echristo@apple.com>
- * 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 <kledzik@apple.com>
-2005-12-06 Nick Kledzik <kledzik@apple.com>
+ <rdar://problem/4465004> 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 <kledzik@apple.com>
-2005-12-05 Eric Christopher <echristo@apple.com>
+ <rdar://problem/4471424> crash in stub with 2GB pagezero
+ * src/MachOWriterExecutable.hpp: StubAtom<ppc64> 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 <kledzik@apple.com>
-2005-12-05 Eric Christopher <echristo@apple.com>
+ * 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 <kledzik@apple.com>
+2006-03-06 Nick Kledzik <kledzik@apple.com>
- * src/ObjectFile.h: Add design comments
+ <rdar://problem/4466930> <rdar://problem/4467982> ld64 failed: rel32 out of range when linking a dylib
+ * src/MachOWriterExecutable.cpp: in Writer<x86_64>::fixUpReferenceFinal add (int32_t) cast
-2005-11-28 Nick Kledzik <kledzik@apple.com>
+2006-03-06 Nick Kledzik <kledzik@apple.com>
- * Refactor Atom to use getDefinitionKind()
+ <rdar://problem/4466930> 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 <kledzik@apple.com>
+2006-03-06 Nick Kledzik <kledzik@apple.com>
- * src/MachOWriterExecutable.hpp: don't generate section for commons in -r mode
+ <rdar://problem/4457818> x86_THREAD_STATE64_COUNT will change, ld64 must adapt
+ * src/MachOWriterExecutable.hpp: update ThreadsLoadCommandsAtom<x86_64> for new thread status layout
-2005-11-18 Nick Kledzik <kledzik@apple.com>
+----- Tagged ld64-44
- * x86 tweaks
+2006-03-04 Nick Kledzik <kledzik@apple.com>
-2005-11-18 Nick Kledzik <kledzik@apple.com>
+ * 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 <kledzik@apple.com>
-2005-11-18 Nick Kledzik <kledzik@apple.com>
+ * 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 <kledzik@apple.com>
+2006-03-03 Nick Kledzik <kledzik@apple.com>
- * Created new Subversion repository for ld64 from cvs tag ld64-27.2
+ <rdar://problem/4465443> 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_*
--- /dev/null
+.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
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 */
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 */
};
/* 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 */;
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 */;
);
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 */
F97288E607D277570031794D /* SectCreate.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = SectCreate.cpp; path = src/SectCreate.cpp; sourceTree = "<group>"; };
F972890007D27FD00031794D /* SectCreate.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = SectCreate.h; path = src/SectCreate.h; sourceTree = "<group>"; };
F97F5028070D0BB200B9FCD7 /* ld64.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = ld64.1; path = doc/man/man1/ld64.1; sourceTree = "<group>"; };
+ F9B1A2580A3A448800DA8FAB /* rebase.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = rebase.1; path = doc/man/man1/rebase.1; sourceTree = "<group>"; };
F9C0D48A06DD1E1B001C7193 /* Options.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Options.cpp; path = src/Options.cpp; sourceTree = "<group>"; };
F9C0D48B06DD1E1B001C7193 /* Options.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Options.h; path = src/Options.h; sourceTree = "<group>"; };
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 = "<group>"; };
F9EA7582097882F3008B4F1D /* debugline.c */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 2; lastKnownFileType = sourcecode.c.c; name = debugline.c; path = src/debugline.c; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
F9EA7583097882F3008B4F1D /* debugline.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = debugline.h; path = src/debugline.h; sourceTree = "<group>"; };
+ 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 = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
);
runOnlyForDeploymentPostprocessing = 0;
};
+ F9EC77EC0A2F85F6002A3E39 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
F9EA7583097882F3008B4F1D /* debugline.h */,
F9EA72D4097454FF008B4F1D /* machochecker.cpp */,
F971EED706D5AD240041D381 /* ObjectDump.cpp */,
+ F9EC78050A2F8674002A3E39 /* rebase.cpp */,
F97F5028070D0BB200B9FCD7 /* ld64.1 */,
+ F9B1A2580A3A448800DA8FAB /* rebase.1 */,
F9023C3A06D5A23E001BBF46 /* Products */,
);
sourceTree = "<group>";
F9023C3906D5A23E001BBF46 /* ld64 */,
F971EED306D5ACF60041D381 /* ObjectDump */,
F9EA72CB097454A6008B4F1D /* machocheck */,
+ F9EC77EE0A2F85F6002A3E39 /* rebase */,
);
name = Products;
sourceTree = "<group>";
F9E8D4BE07FCAF2A00FD5801 /* PBXBuildRule */,
F9E8D4BD07FCAF2000FD5801 /* PBXBuildRule */,
);
- buildSettings = {
- PRODUCT_NAME = ld64;
- };
dependencies = (
);
name = ld;
);
buildRules = (
);
- buildSettings = {
- PRODUCT_NAME = ObjectDump;
- };
dependencies = (
);
name = ObjectDump;
);
buildRules = (
);
- buildSettings = {
- GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
- GCC_MODEL_TUNING = G5;
- INSTALL_PATH = "$(HOME)/bin";
- PREBINDING = NO;
- PRODUCT_NAME = machocheck;
- };
dependencies = (
);
name = machocheck;
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 */,
);
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 */;
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 */;
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;
INSTALL_PATH = /usr/bin;
OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)";
OTHER_LDFLAGS = "";
- PREBINDING = NO;
PRODUCT_NAME = ld64;
SECTORDER_FLAGS = "";
VERSIONING_SYSTEM = "apple-generic";
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;
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";
};
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";
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",
};
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;
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;
};
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 */
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 = (
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 */;
typedef Pointer32<LittleEndian> P;
enum ReferenceKinds { kNoFixUp, kFollowOn, kPointer, kPointerWeakImport, kPointerDiff,
- kPCRel32, kPCRel32WeakImport };
+ kPCRel32, kPCRel32WeakImport, kAbsolute32 };
};
+struct x86_64
+{
+ typedef Pointer64<LittleEndian> P;
+
+ enum ReferenceKinds { kNoFixUp, kFollowOn, kPointer, kPointerWeakImport, kPointerDiff, kPointerDiff32,
+ kPCRel32, kPCRel32_1, kPCRel32_2, kPCRel32_4,
+ kBranchPCRel32, kBranchPCRel32WeakImport,
+ kPCRel32GOTLoad, kPCRel32GOTLoadWeakImport,
+ kPCRel32GOT, kPCRel32GOTWeakImport };
+};
};
#endif
+#ifndef S_16BYTE_LITERALS
+ #define S_16BYTE_LITERALS 0xE
+#endif
#include "FileAbstraction.hpp"
#include "Architectures.hpp"
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); }
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); }
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<class ObjectFile::Atom*>(r->getAtoms());
}
}
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<ObjectFile::Reference*>& getReferences() const { return fgEmptyReferenceList; }
class Reader : public ObjectFile::Reader
{
public:
- static bool validFile(const uint8_t* fileContent);
- static Reader<A>* make(const uint8_t* fileContent, uint64_t fileLength, const char* path, const ObjectFile::ReaderOptions& options)
- { return new Reader<A>(fileContent, fileLength, path, options); }
- virtual ~Reader() {}
+ static bool validFile(const uint8_t* fileContent, bool executableOrDylib);
+ static Reader<A>* make(const uint8_t* fileContent, uint64_t fileLength, const char* path,
+ bool executableOrDylib, const ObjectFile::ReaderOptions& options)
+ { return new Reader<A>(fileContent, fileLength, path, executableOrDylib, options); }
+ virtual ~Reader() {}
virtual const char* getPath() { return fPath; }
virtual time_t getModificationTime() { return 0; }
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;
template <typename A>
-Reader<A>::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, const ObjectFile::ReaderOptions& options)
+Reader<A>::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);
}
// 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";
{
// 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<PathAndFlag>::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;
}
}
template <>
-bool Reader<ppc>::validFile(const uint8_t* fileContent)
+bool Reader<ppc>::validFile(const uint8_t* fileContent, bool executableOrDylib)
{
const macho_header<P>* header = (const macho_header<P>*)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<ppc64>::validFile(const uint8_t* fileContent)
+bool Reader<ppc64>::validFile(const uint8_t* fileContent, bool executableOrDylib)
{
const macho_header<P>* header = (const macho_header<P>*)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<x86>::validFile(const uint8_t* fileContent)
+bool Reader<x86>::validFile(const uint8_t* fileContent, bool executableOrDylib)
{
const macho_header<P>* header = (const macho_header<P>*)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<x86_64>::validFile(const uint8_t* fileContent, bool executableOrDylib)
+{
+ const macho_header<P>* header = (const macho_header<P>*)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;
+ }
+}
#include <sys/param.h>
#include <mach-o/ppc/reloc.h>
#include <mach-o/stab.h>
+#include <mach-o/x86_64/reloc.h>
#ifndef S_ATTR_DEBUG
#define S_ATTR_DEBUG 0x02000000
#endif
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(); }
: 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);
template <typename A>
Segment<A>::Segment(const macho_section<typename A::P>* 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 ) {
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<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(fReferences); }
ReferenceVector fReferences;
std::vector<ObjectFile::LineInfo> fLineInfo;
ObjectFile::Atom::Scope fScope;
+ SymbolTableInclusion fSymbolTableInclusion;
uint8_t fAlignment;
};
// real definition
fSegment = new Segment<A>(fSection);
fAddress = fSymbol->n_value();
- if ( (fSymbol->n_desc() & N_NO_DEAD_STRIP) != 0 )
- this->setDontDeadStrip();
- }
+ }
else {
printf("unknown symbol type: %d\n", type);
}
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;
+ }
}
{
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);
+ }
}
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<ObjectFile::Reference*>& getReferences() const { return fgNoReferences; }
virtual bool mustRemainInSection() const { return true; }
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:
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<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(fReferences); }
virtual std::vector<ObjectFile::LineInfo>* 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<A>*)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; }
Segment<A>* 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 <typename A>
AnonymousAtom<A>::AnonymousAtom(Reader<A>& owner, const macho_section<P>* 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<A>(fSection);
fRedirect = this;
}
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:
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<P>* end = &fOwner.fSymbols[fOwner.fSymbolCount];
for (const macho_nlist<P>* 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);
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<P>* targetSymbol = &fOwner.fSymbols[symbolIndex];
const char* name = &fOwner.fStrings[targetSymbol->n_strx()];
strcat(str, "$non_lazy_ptr");
fSynthesizedName = str;
- if ( fOwner.isWeakImportSymbol(targetSymbol) )
- new Reference<A>(A::kPointerWeakImport, AtomAndOffset(this), name, 0);
- else
- new Reference<A>(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>(A::kPointer, AtomAndOffset(this), fOwner.findAtomAndOffset(targetSymbol->n_value()));
+ }
+ else {
+ if ( fOwner.isWeakImportSymbol(targetSymbol) )
+ new Reference<A>(A::kPointerWeakImport, AtomAndOffset(this), name, 0);
+ else
+ new Reference<A>(A::kPointer, AtomAndOffset(this), name, 0);
+ }
}
break;
default:
ObjectFile::Atom::Scope AnonymousAtom<A>::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 <typename A>
ObjectFile::Atom::DefinitionKind AnonymousAtom<A>::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;
template <typename A>
const char* AnonymousAtom<A>::getSectionName() const
{
- if ( fReallyNonLazyPointer )
- return "__nl_symbol_ptr";
if ( strlen(fSection->sectname()) > 15 ) {
static char temp[18];
strncpy(temp, fSection->sectname(), 16);
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:
Reference<A>* makeReferenceWithToBase(Kinds kind, uint32_t atAddr, uint32_t fromAddr, uint32_t toAddr, uint32_t toBaseAddr);
Reference<A>* makeByNameReference(Kinds kind, uint32_t atAddr, const char* toName, uint32_t toOffset);
Reference<A>* makeReferenceToEH(const char* ehName, pint_t ehAtomAddress, const macho_section<P>* ehSect);
+ Reference<A>* makeReferenceToSymbol(Kinds kind, uint32_t atAddr, const macho_nlist<P>* toSymbol, uint32_t toOffset);
void validSectionType(uint8_t type);
+ void handleAnonymousNonLazyPointers(const macho_section<P>* sect);
BaseAtom* findAtomByName(const char*);
std::map<uint32_t, BaseAtom*> fAddrToAtom;
std::vector<class AnonymousAtom<A>*> fLocalNonLazys;
ObjectFile::Reader::DebugInfoKind fDebugInfo;
+ bool fHasUUID;
const macho_section<P>* fDwarfDebugInfoSect;
const macho_section<P>* fDwarfDebugAbbrevSect;
const macho_section<P>* fDwarfDebugLineSect;
const char* fDwarfTranslationUnitFile;
std::map<uint32_t,const char*> fDwarfIndexToFile;
std::vector<Stab> fStabs;
+ bool fAppleObjc;
};
+// usually do nothing
+template <typename A> void Reader<A>::handleAnonymousNonLazyPointers(const macho_section<P>* 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<ppc64>::handleAnonymousNonLazyPointers(const macho_section<P>* 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<uint32_t> lo14targets;
+ const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)fSegment + sizeof(macho_segment_command<P>));
+ const macho_section<P>* const sectionsEnd = §ionsStart[fSegment->nsects()];
+ for (const macho_section<P>* sect=sectionsStart; sect < sectionsEnd; ++sect) {
+ if ( strncmp(sect->sectname(), "__text", 6) == 0 ) {
+ const macho_relocation_info<P>* relocs = (macho_relocation_info<P>*)((char*)(fHeader) + sect->reloff());
+ const macho_relocation_info<P>* relocsEnd = &relocs[sect->nreloc()];
+ for (const macho_relocation_info<P>* r = relocs; r < relocsEnd; ++r) {
+ if ( (r->r_address() & R_SCATTERED) != 0 ) {
+ const macho_scattered_relocation_info<P>* sreloc = (macho_scattered_relocation_info<P>*)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<uint32_t>::reverse_iterator it=lo14targets.rbegin(); it != lo14targets.rend(); it++) {
+ uint32_t targetOfLO14 = *it;
+ AtomAndOffset found = this->findAtomAndOffset(targetOfLO14);
+ if ( (found.offset & 0x7) != 0 ) {
+ AnonymousAtom<ppc64>* newAtom = new AnonymousAtom<ppc64>(*this, dataSect, targetOfLO14, sizeof(pint_t));
+ newAtom->fReallyNonLazyPointer = true;
+ fAtoms.push_back(newAtom);
+ fAddrToAtom[targetOfLO14] = newAtom;
+ }
+ }
+ }
+}
template <typename A>
Reader<A>::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<P>*)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) )
const uint32_t cmd_count = header->ncmds();
const macho_load_command<P>* const cmds = (macho_load_command<P>*)((char*)header + sizeof(macho_header<P>));
const macho_load_command<P>* 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:
{
const macho_dysymtab_command<P>* dsymtab = (struct macho_dysymtab_command<P>*)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:
case S_COALESCED:
case S_4BYTE_LITERALS:
case S_8BYTE_LITERALS:
+ case S_16BYTE_LITERALS:
case S_CSTRING_LITERALS:
{
BaseAtom* newAtom = new SymbolAtom<A>(*this, &sym, section);
else if ( (type == N_UNDF) && (sym.n_value() != 0) ) {
fAtoms.push_back(new TentativeAtom<A>(*this, &sym));
}
+ else if ( (type == N_ABS) && (strncmp(&fStrings[sym.n_strx()], ".objc_class_name_", 16) == 0) ) {
+ fAppleObjc = true;
+ }
}
}
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) {
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<P>* relocs = (macho_relocation_info<P>*)((char*)(fHeader) + sect->reloff());
- const macho_relocation_info<P>* relocsEnd = &relocs[sect->nreloc()];
- for (const macho_relocation_info<P>* 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<A>* newAtom = new AnonymousAtom<A>(*this, sect, sect->addr()+possiblePointerAddress, sizeof(pint_t));
- const macho_nlist<P>* 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<A>(*this, sect, sect->addr(), 0);
fAtoms.push_back(newAtom);
fAddrToAtom[sect->addr()] = newAtom;
}
}
+ // check of object file that defines no classes, but uses classes
+ if ( !fAppleObjc ) {
+ for (uint32_t i=undefinedStartIndex; i < undefinedEndIndex; ++i) {
+ const macho_nlist<P>& 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<P>* 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<AnonymousAtom<A>*>::iterator it=fLocalNonLazys.begin(); it != fLocalNonLazys.end(); it++) {
AnonymousAtom<A>* localNonLazy = *it;
// 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;
+ }
}
}
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 {
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;
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);
}
enum { start, inBeginEnd, inFun } state = start;
for (uint32_t symbolIndex = 0; symbolIndex < fSymbolCount; ++symbolIndex ) {
const macho_nlist<P>* 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;
}
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:
break;
}
// add to list of stabs for this .o file
- fStabs.push_back(stab);
+ if ( useStab )
+ fStabs.push_back(stab);
}
}
}
#endif
}
+template <>
+void Reader<x86_64>::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 <typename A>
void Reader<A>::validSectionType(uint8_t type)
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<x86_64>* Reader<x86_64>::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<x86_64>(kind, findAtomAndOffset(atAddr), AtomAndOffset(target, toOffset));
+ else
+ return new Reference<x86_64>(kind, findAtomAndOffset(atAddr), toName, toOffset);
+}
+
+template <>
+Reference<x86_64>* Reader<x86_64>::makeReferenceToSymbol(Kinds kind, uint32_t atAddr, const macho_nlist<P>* 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<x86_64>(kind, findAtomAndOffset(atAddr), findAtomAndOffset(toSymbol->n_value(), toSymbol->n_value()+toOffset));
+ else
+ return new Reference<x86_64>(kind, findAtomAndOffset(atAddr), &fStrings[toSymbol->n_strx()], toOffset);
+}
+
+
+template <>
+Reference<x86_64>* Reader<x86_64>::makeReferenceToEH(const char* ehName, pint_t ehAtomAddress, const macho_section<P>* 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<P>* relocs = (macho_relocation_info<P>*)((char*)(fHeader) + ehSect->reloff());
+ const macho_relocation_info<P>* relocsEnd = &relocs[ehSect->nreloc()];
+ for (const macho_relocation_info<P>* 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 <typename A>
AtomAndOffset Reader<A>::findAtomAndOffset(uint32_t addr)
return true;
}
+template <>
+bool Reader<x86_64>::validFile(const uint8_t* fileContent)
+{
+ const macho_header<P>* header = (const macho_header<P>*)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 <typename A>
Reference<A>* ref = makeReference(A::kAbsLow14, srcAddr, dstAddr);
BaseAtom* target = ((BaseAtom*)&(ref->getTarget()));
if ( target != NULL )
- target->alignAtLeast(2);
+ target->alignAtLeast(3);
}
}
break;
Reference<A>* 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:
kind = x86::kPCRel32;
pointerValue += srcAddr + sizeof(uint32_t);
}
+ else if ( strcmp(sect->segname(), "__TEXT") == 0 ) {
+ kind = x86::kAbsolute32;
+ }
else {
kind = x86::kPointer;
}
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:
return result;
}
+template <>
+bool Reader<x86_64>::addRelocReference(const macho_section<x86_64::P>* sect, const macho_relocation_info<x86_64::P>* reloc)
+{
+ uint64_t srcAddr;
+ uint64_t dstAddr = 0;
+ uint64_t addend;
+ uint32_t* fixUpPtr;
+ x86_64::ReferenceKinds kind;
+ bool result = false;
+ const macho_nlist<P>* 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<x86_64::P>* 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<x86_64>* 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<P>* 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<P>* 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<x86>::getDescription() const
{
- static char temp[1024];
+ static char temp[2048];
switch( fKind ) {
case x86::kNoFixUp:
sprintf(temp, "reference to ");
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 ) {
template <>
const char* Reference<ppc>::getDescription() const
{
- static char temp[1024];
+ static char temp[2048];
switch( fKind ) {
case ppc::kNoFixUp:
sprintf(temp, "reference to ");
template <>
const char* Reference<ppc64>::getDescription() const
{
- static char temp[1024];
+ static char temp[2048];
switch( fKind ) {
case ppc64::kNoFixUp:
sprintf(temp, "reference to ");
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<x86_64>::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
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;
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 {
class UUIDLoadCommandAtom<A>* fUUIDAtom;
std::vector<class ObjectFile::Atom*> fWriterSynthesizedAtoms;
std::vector<SegmentInfo*> fSegmentInfos;
+ class SegmentInfo* fPadSegmentInfo;
class ObjectFile::Atom* fEntryPoint;
class ObjectFile::Atom* fDyldHelper;
std::vector<DirectLibrary> fDirectLibraries;
class SymbolTableLinkEditAtom<A>* fSymbolTableAtom;
class IndirectTableLinkEditAtom<A>* fIndirectTableAtom;
class StringsLinkEditAtom<A>* fStringsAtom;
+ class PageZeroAtom<A>* fPageZeroAtom;
macho_nlist<P>* fSymbolTable;
std::vector<macho_relocation_info<P> > fSectionRelocs;
std::vector<macho_relocation_info<P> > fInternalRelocs;
bool fHasWeakExports;
bool fReferencesWeakImports;
bool fSeenFollowOnReferences;
+ bool fWritableSegmentPastFirst4GB;
std::map<const ObjectFile::Atom*,bool> fWeakImportMap;
+ SegmentInfo* fFirstWritableSegment;
};
static Segment fgStackSegment;
static Segment fgImportSegment;
static Segment fgDataSegment;
+
private:
const char* fName;
const bool fReadable;
{
public:
enum Kind { zeropage, machHeaderApp, machHeaderDylib, machHeaderBundle, machHeaderObject, loadCommands, undefinedProxy };
- WriterAtom(Writer<A>& writer, Segment& segment) : fWriter(writer), fSegment(segment) { setDontDeadStrip(); }
+ WriterAtom(Writer<A>& 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; }
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<ObjectFile::Reference*>& getReferences() const { return fgEmptyReferenceList; }
virtual bool mustRemainInSection() const { return true; }
class PageZeroAtom : public WriterAtom<A>
{
public:
- PageZeroAtom(Writer<A>& writer) : WriterAtom<A>(writer, Segment::fgPageZeroSegment) {}
+ PageZeroAtom(Writer<A>& writer) : WriterAtom<A>(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<A>::fWriter;
typedef typename A::P P;
+ uint64_t fSize;
};
+
template <typename A>
class DsoHandleAtom : public WriterAtom<A>
{
void computeSize();
void setup();
unsigned int commandCount() { return fCommandCount; }
- void assignFileOffsets();
private:
using WriterAtom<A>::fWriter;
typedef typename A::P P;
uint32_t fSize;
};
+
template <typename A>
class SymbolTableLoadCommandsAtom : public LoadCommandAtom<A>
{
std::vector<ObjectFile::Reference*> fReferences;
};
+template <typename A>
+class StubHelperAtom : public WriterAtom<A>
+{
+public:
+ StubHelperAtom(Writer<A>& 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<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(fReferences); }
+ virtual void copyRawContent(uint8_t buffer[]) const;
+ ObjectFile::Atom* getTarget() { return &fTarget; }
+private:
+ static const char* stubName(const char* importName);
+ using WriterAtom<A>::fWriter;
+ const char* fName;
+ ObjectFile::Atom& fTarget;
+ std::vector<ObjectFile::Reference*> fReferences;
+};
template <typename A>
class LazyPointerAtom : public WriterAtom<A>
template <typename A>
Writer<A>::Writer(const char* path, Options& options, std::vector<ExecutableFile::DyLibUsed>& 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 )
switch ( fOptions.outputKind() ) {
case Options::kDynamicExecutable:
case Options::kStaticExecutable:
- fWriterSynthesizedAtoms.push_back(new PageZeroAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fPageZeroAtom = new PageZeroAtom<A>(*this));
if ( fOptions.outputKind() == Options::kDynamicExecutable )
fWriterSynthesizedAtoms.push_back(new DsoHandleAtom<A>(*this));
fWriterSynthesizedAtoms.push_back(new MachHeaderAtom<A>(*this));
const unsigned int libCount = dynamicLibraries.size();
for (unsigned int i=0; i < libCount; ++i) {
ExecutableFile::DyLibUsed& dylibInfo = dynamicLibraries[i];
- if ( dylibInfo.indirect ) {
+ if ( dylibInfo.options.fBundleLoader ) {
+ fLibraryToOrdinal[dylibInfo.reader] = EXECUTABLE_ORDINAL;
+ }
+ else if ( dylibInfo.indirect ) {
// find ordinal of direct reader
if ( fOptions.nameSpace() == Options::kTwoLevelNameSpace ) {
bool found = false;
dylibInstallPath = dylibInfo.options.fInstallPathOverride;
for (unsigned int seenLib=0; seenLib < i; ++seenLib) {
ExecutableFile::DyLibUsed& seenDylibInfo = dynamicLibraries[seenLib];
- if ( !seenDylibInfo.indirect ) {
+ if ( !seenDylibInfo.indirect && !seenDylibInfo.options.fBundleLoader ) {
const char* seenDylibInstallPath = seenDylibInfo.reader->getInstallPath();
if ( seenDylibInfo.options.fInstallPathOverride != NULL )
seenDylibInstallPath = dylibInfo.options.fInstallPathOverride;
}
+// for ppc64, -mdynamic-no-pic only works in low 2GB, so we might need to split the zeropage into two segments
+template <>bool Writer<ppc64>::mightNeedPadSegment() { return (fOptions.zeroPageSize() >= 0x80000000ULL); }
+template <typename A> bool Writer<A>::mightNeedPadSegment() { return false; }
+
+
template <typename A>
ObjectFile::Atom* Writer<A>::getUndefinedProxyAtom(const char* name)
{
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 <typename A>
void Writer<A>::setExportNlist(const ObjectFile::Atom* atom, macho_nlist<P>* 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);
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;
}
{
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();
}
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;
}
template <typename A>
uint8_t Writer<A>::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;
}
}
+template <>
+uint32_t Writer<x86_64>::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<P> reloc1;
+ macho_relocation_info<P> 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<x86>::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref)
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);
//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());
relocIndex += this->addObjectRelocs(atom, ref);
}
}
- else {
+ else if ( ref->getKind() != A::kNoFixUp ) {
relocIndex += this->addObjectRelocs(atom, ref);
}
}
}
template <>
-bool Writer<ppc>::illegalRelocInFinalLinkedImage(uint8_t kind, bool slideable)
+bool Writer<ppc>::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref, bool slideable)
{
- switch ( kind ) {
+ switch ( ref.getKind() ) {
case ppc::kAbsLow16:
case ppc::kAbsLow14:
case ppc::kAbsHigh16:
template <>
-bool Writer<ppc64>::illegalRelocInFinalLinkedImage(uint8_t kind, bool slideable)
+bool Writer<ppc64>::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref, bool slideable)
{
- switch ( kind ) {
+ switch ( ref.getKind() ) {
case ppc::kAbsLow16:
case ppc::kAbsLow14:
case ppc::kAbsHigh16:
}
template <>
-bool Writer<x86>::illegalRelocInFinalLinkedImage(uint8_t kind, bool slideable)
-{
+bool Writer<x86>::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<x86_64>::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref, bool slideable)
+{
+ return false;
+}
template <typename A>
return kRelocNone;
}
+template <typename A>
+uint64_t Writer<A>::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<x86_64>::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<ppc64>::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 <typename A>
void Writer<A>::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();
}
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:
// 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();
case kRelocExternal:
{
macho_relocation_info<P> 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();
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());
}
}
::pwrite(fFileDescriptor, &x86Nop, 1, p);
}
+template <>
+void Writer<x86_64>::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 <typename A>
uint64_t Writer<A>::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);
}
}
}
delete [] buffer;
+ close(fFileDescriptor);
return end;
}
void Writer<x86>::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:
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());
}
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:
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;
}
}
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
(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);
}
}
+template <>
+void Writer<x86_64>::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<x86_64>::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<ppc>::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const
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());
return (kind == x86::kPCRel32 || kind == x86::kPCRel32WeakImport);
}
+template <>
+bool Writer<x86_64>::stubableReferenceKind(uint8_t kind)
+{
+ return (kind == x86_64::kBranchPCRel32 || kind == x86_64::kBranchPCRel32WeakImport);
+}
template <>
bool Writer<ppc>::weakImportReferenceKind(uint8_t kind)
return (kind == x86::kPCRel32WeakImport || kind == x86::kPointerWeakImport);
}
+template <>
+bool Writer<x86_64>::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 <>
return false;
}
+template <>
+bool Writer<x86_64>::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 <typename A>
+void Writer<A>::scanForAbsoluteReferences()
+{
+ // do nothing
+}
+
+// for ppc64 look for any -mdynamic-no-pic codegen
+template <>
+void Writer<ppc64>::scanForAbsoluteReferences()
+{
+ // only do this for main executable
+ if ( mightNeedPadSegment() && (fPageZeroAtom != NULL) ) {
+ for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) {
+ ObjectFile::Atom* atom = *it;
+ std::vector<ObjectFile::Reference*>& references = atom->getReferences();
+ for (std::vector<ObjectFile::Reference*>::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 <typename A>
{
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:
nlp = pos->second;
}
// alter reference to use non lazy pointer instead
- ref->setTarget(*nlp, 0);
+ ref->setTarget(*nlp, ref->getTargetOffset());
}
}
}
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;
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);
return false;
}
+template <>
+bool Writer<x86_64>::addBranchIslands()
+{
+ // x86 branches can reach entire 4G size of largest image
+ return false;
+}
template <>
inline uint8_t Writer<ppc>::branch24Reference()
// 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
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
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];
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);
}
}
}
}
+
+ // set up fFirstWritableSegment and fWritableSegmentPastFirst4GB
+ for (std::vector<SegmentInfo*>::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 <typename A>
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<SegmentInfo*>::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<class ObjectFile::Atom*>& atoms = lastSeg->fSections[i]->fAtoms;
const unsigned int atomCount = atoms.size();
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;
header.set_cpusubtype(CPU_SUBTYPE_I386_ALL);
}
+template <>
+void MachHeaderAtom<x86_64>::setHeaderInfo(macho_header<x86_64::P>& header) const
+{
+ header.set_magic(MH_MAGIC_64);
+ header.set_cputype(CPU_TYPE_X86_64);
+ header.set_cpusubtype(CPU_SUBTYPE_X86_64_ALL);
+}
template <typename A>
CustomStackAtom<A>::CustomStackAtom(Writer<A>& writer)
return true;
}
+template <>
+bool CustomStackAtom<x86_64>::stackGrowsDown()
+{
+ return true;
+}
template <typename A>
void SegmentLoadCommandsAtom<A>::computeSize()
}
fSize = size;
fCommandCount = segCount;
+ if ( fWriter.fPadSegmentInfo != NULL ) {
+ ++fCommandCount;
+ fSize += sizeof(macho_segment_command<P>);
+ }
}
template <>
return ((size+3) & (-4)); // 4-byte align all load commands for 32-bit mach-o
}
+template <>
+uint64_t LoadCommandAtom<x86_64>::alignedSize(uint64_t size)
+{
+ return ((size+7) & (-8)); // 8-byte align all load commands for 64-bit mach-o
+}
template <typename A>
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());
}
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<P>) + sectionsEmitted*sizeof(macho_section<P>)];
return this->alignedSize(16 + 16*4); // base size + i386_THREAD_STATE_COUNT * 4
}
+template <>
+uint64_t ThreadsLoadCommandsAtom<x86_64>::getSize() const
+{
+ return this->alignedSize(16 + x86_THREAD_STATE64_COUNT * 4);
+}
template <>
void ThreadsLoadCommandsAtom<ppc>::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 <>
}
+template <>
+void ThreadsLoadCommandsAtom<x86_64>::copyRawContent(uint8_t buffer[]) const
+{
+ uint64_t size = this->getSize();
+ uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint);
+ bzero(buffer, size);
+ macho_thread_command<x86_64::P>* cmd = (macho_thread_command<x86_64::P>*)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 <typename A>
// 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;
writer.fAllSynthesizedStubs.push_back(this);
}
+template <>
+StubAtom<x86_64>::StubAtom(Writer<x86_64>& writer, ObjectFile::Atom& target)
+ : WriterAtom<x86_64>(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target)
+{
+ writer.fAllSynthesizedStubs.push_back(this);
+
+ LazyPointerAtom<x86_64>* lp = new LazyPointerAtom<x86_64>(writer, target);
+ fReferences.push_back(new WriterReference<x86_64>(2, x86_64::kPCRel32, lp));
+}
template <typename A>
const char* StubAtom<A>::stubName(const char* name)
return 5;
}
+template <>
+uint64_t StubAtom<x86_64>::getSize() const
+{
+ return 6;
+}
template <>
uint8_t StubAtom<x86>::getAlignment() const
buffer[4] = 0xF4;
}
+template <>
+void StubAtom<x86_64>::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<x86_64>::getAlignment() const
+{
+ return 0;
+}
template <>
const char* StubAtom<ppc>::getSectionName() const
+template <>
+StubHelperAtom<x86_64>::StubHelperAtom(Writer<x86_64>& writer, ObjectFile::Atom& target, ObjectFile::Atom& lazyPointer)
+ : WriterAtom<x86_64>(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target)
+{
+ writer.fAllSynthesizedStubHelpers.push_back(this);
+
+ fReferences.push_back(new WriterReference<x86_64>(3, x86_64::kPCRel32, &lazyPointer));
+ fReferences.push_back(new WriterReference<x86_64>(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<x86_64>::getSize() const
+{
+ return 12;
+}
+
+template <>
+void StubHelperAtom<x86_64>::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 <typename A>
+const char* StubHelperAtom<A>::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<x86_64>::LazyPointerAtom(Writer<x86_64>& writer, ObjectFile::Atom& target)
+ : WriterAtom<x86_64>(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target)
+{
+ writer.fAllSynthesizedLazyPointers.push_back(this);
+
+ StubHelperAtom<x86_64>* helper = new StubHelperAtom<x86_64>(writer, target, *this);
+ fReferences.push_back(new WriterReference<x86_64>(0, x86_64::kPointer, helper));
+}
template <typename A>
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;
}
}
return mach_o::relocatable::Reader<ppc>::make(p, path, 0, options);
else if ( mach_o::relocatable::Reader<ppc64>::validFile(p) )
return mach_o::relocatable::Reader<ppc64>::make(p, path, 0, options);
+ else if ( mach_o::relocatable::Reader<x86_64>::validFile(p) )
+ return mach_o::relocatable::Reader<x86_64>::make(p, path, 0, options);
throwf("not a mach-o object file: %s", path);
}
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);
}
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;
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;
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<ObjectFile::Reference*>& getReferences() const = 0;
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;
};
: 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();
}
return fInitialUndefines;
}
+bool Options::printWhyLive(const char* symbolName)
+{
+ return ( fWhyLive.find(symbolName) != fWhyLive.end() );
+}
+
std::vector<const char*>& Options::traceSymbols()
{
return fTraceSymbols;
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;
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";
}
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);
}
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)
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 ) {
}
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]);
i += 2;
}
else if ( strcmp(arg, "-bundle_loader") == 0 ) {
- // FIX FIX
- ++i;
+ fBundleLoader = argv[++i];
+ if ( (fBundleLoader == NULL) || (fBundleLoader[0] == '-') )
+ throw "-bundle_loader missing <path>";
+ FileInfo info = findFile(fBundleLoader);
+ info.options.fBundleLoader = true;
+ fInputFiles.push_back(info);
}
else if ( strcmp(arg, "-private_bundle") == 0 ) {
// FIX FIX
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];
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
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()
}
}
}
+
+
//
// -syslibroot <path> is used for SDK support.
// The rule is that all search paths (both explicit and default) are
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()
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 ) {
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 )
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;
}
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;
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";
}
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;
};
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;
bool keepPrivateExterns(); // only for kObjectFile
bool interposable(); // only for kDynamicLibrary
bool hasExportRestrictList();
+ bool allGlobalsAreDeadStripRoots();
bool shouldExport(const char*);
bool ignoreOtherArchInputFiles();
bool forceCpuSubtypeAll();
uint64_t customStackAddr();
bool hasExecutableStack();
std::vector<const char*>& initialUndefines();
+ bool printWhyLive(const char* name);
uint32_t minimumHeaderPad();
std::vector<ExtraSection>& extraSections();
std::vector<SectionAlignment>& sectionAlignments();
bool warnStabs();
bool pauseAtEnd() { return fPause; }
bool printStatistics() { return fStatistics; }
+ bool printArchPrefix() { return fMessagesPrefixedWithArchitecture; }
+ bool makeTentativeDefinitionsReal() { return fMakeTentativeDefinitionsReal; }
private:
class CStringEquals
void addSectionAlignment(const char* segment, const char* section, const char* alignment);
CommonsMode parseCommonsTreatment(const char* mode);
Treatment parseTreatment(const char* treatment);
+ void reconfigureDefaults();
const char* fInitFunctionName;
const char* fDotOutputFile;
const char* fExecutablePath;
+ const char* fBundleLoader;
uint64_t fZeroPageSize;
uint64_t fStackSize;
uint64_t fStackAddr;
bool fPause;
bool fStatistics;
bool fPrintOptions;
+ bool fMakeTentativeDefinitionsReal;
std::vector<const char*> fInitialUndefines;
+ NameSet fWhyLive;
std::vector<const char*> fTraceSymbols;
unsigned long fLimitUndefinedSymbols;
std::vector<ExtraSection> fExtraSections;
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<ObjectFile::Reference*>& getReferences() const { return fgEmptyReferenceList; }
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;
#include <sys/sysctl.h>
#include <fcntl.h>
#include <errno.h>
+#include <limits.h>
#include <unistd.h>
#include <mach/mach_time.h>
#include <mach/vm_statistics.h>
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);
private:
+ struct WhyLiveBackChain
+ {
+ WhyLiveBackChain* previous;
+ const char* name;
+ };
+
ObjectFile::Reader* createReader(const Options::FileInfo&);
void addAtom(ObjectFile::Atom& atom);
void addAtoms(std::vector<class ObjectFile::Atom*>& 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();
ObjectFile::Atom* entryPoint();
ObjectFile::Atom* dyldHelper();
const char* assureFullPath(const char* path);
- void markLive(ObjectFile::Atom* atom, std::set<ObjectFile::Atom*>& liveAtoms);
- void collectStabs(ObjectFile::Reader* reader);
+ void markLive(ObjectFile::Atom& atom, Linker::WhyLiveBackChain* previous);
+ void collectStabs(ObjectFile::Reader* reader, std::map<class ObjectFile::Atom*, uint32_t>& atomOrdinals);
void synthesizeStabs(ObjectFile::Reader* reader);
void printStatistics();
void printTime(const char* msg, uint64_t partTime, uint64_t totalTime);
bool haveDirectLibrary(const char* path);
void logTraceInfo(const char* format, ...);
+
+
class SymbolTable
{
public:
std::vector<class ObjectFile::Reader*> fReadersThatHaveSuppliedAtoms;
std::vector<class ObjectFile::Atom*> fAllAtoms;
std::set<class ObjectFile::Atom*> fDeadAtoms;
+ std::set<ObjectFile::Atom*> fLiveAtoms;
+ std::set<ObjectFile::Atom*> fLiveRootAtoms;
std::vector<class ObjectFile::Reader::Stab> 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;
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<Options::FileInfo>& files = fOptions.getInputFiles();
::close(fd);
if ( amount >= (ssize_t)sizeof(buffer) ) {
if ( mach_o::relocatable::Reader<ppc>::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<ppc64>::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<x86>::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<x86_64>::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__
return CPU_TYPE_I386;
#elif __ppc64__
return CPU_TYPE_POWERPC64;
+#elif __x86_64__
+ return CPU_TYPE_X86_64;
#else
#error unknown default architecture
#endif
fOutputFile = writer;
}
+class InSet
+{
+public:
+ InSet(std::set<ObjectFile::Atom*>& deadAtoms) : fDeadAtoms(deadAtoms) {}
+
+ bool operator()(ObjectFile::Atom*& atom) const {
+ return ( fDeadAtoms.count(atom) != 0 );
+ }
+
+private:
+ std::set<ObjectFile::Atom*>& 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();
// 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<class ObjectFile::Reference*>& references = atom.getReferences();
- for (std::vector<ObjectFile::Reference*>::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<class ObjectFile::Reference*>& references = atom.getReferences();
+ for (std::vector<ObjectFile::Reference*>::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
this->addJustInTimeAtoms(name);
}
}
+}
+void Linker::checkUndefines()
+{
if ( fOptions.outputKind() != Options::kObjectFile ) {
// error out on any remaining undefines
bool doPrint = true;
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);
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());
- }
}
}
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());
}
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);
}
}
}
-class InSet
-{
-public:
- InSet(std::set<ObjectFile::Atom*>& deadAtoms) : fDeadAtoms(deadAtoms) {}
-
- bool operator()(ObjectFile::Atom*& atom) const {
- return ( fDeadAtoms.count(atom) != 0 );
- }
-
-private:
- std::set<ObjectFile::Atom*>& fDeadAtoms;
-};
// used to remove stabs associated with atoms that won't be in output file
class NotInSet
};
-class DeadStrippable
+class NotLive
{
public:
- DeadStrippable() {}
+ NotLive(std::set<ObjectFile::Atom*>& 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<ObjectFile::Atom*>& fLiveAtoms;
};
-void Linker::markLive(ObjectFile::Atom* atom, std::set<ObjectFile::Atom*>& 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<class ObjectFile::Reference*>& references = atom->getReferences();
+ std::vector<class ObjectFile::Reference*>& references = atom.getReferences();
for (std::vector<ObjectFile::Reference*>::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<ObjectFile::Atom*> 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<const char*>& initialUndefines = fOptions.initialUndefines();
+ for (std::vector<const char*>::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<ObjectFile::Atom*>::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<ObjectFile::Atom*>::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<ObjectFile::Atom*>::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()
{
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];
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);
}
}
}
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 ");
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;
//
// The stab strings are of the form:
// <name> ':' <type-code> <number-pari>
-// but the <name> contain a colon.
+// but the <name> contain a colon.
// For C++ <name> 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) )
//
}
}
-struct HeaderRange {
- std::vector<ObjectFile::Reader::Stab>::iterator begin;
+
+struct HeaderRange {
+ std::vector<ObjectFile::Reader::Stab>::iterator begin;
std::vector<ObjectFile::Reader::Stab>::iterator end;
int parentRangeIndex;
uint32_t sum;
static PathToSums sKnownBINCLs;
-void Linker::collectStabs(ObjectFile::Reader* reader)
+void Linker::collectStabs(ObjectFile::Reader* reader, std::map<class ObjectFile::Atom*, uint32_t>& atomOrdinals)
{
bool log = false;
bool minimal = ( fOptions.readerOptions().fDebugInfoStripping == ObjectFile::ReaderOptions::kDebugInfoMinimal );
std::vector<class ObjectFile::Reader::Stab>* 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<HeaderRange> 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<std::pair<ObjectFile::Atom*,ObjectFile::Atom*> > 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<class ObjectFile::Reader::Stab>::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) {
++count;
switch ( it->type ) {
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;
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<class ObjectFile::Atom*, uint32_t>::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:
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<ObjectFile::Atom*,ObjectFile::Atom*>(atomWithLowestOrdinal, atomWithHighestOrdinal));
+ }
+ // fall through
default:
if ( curRangeIndex != -1 ) {
if ( ! ranges[curRangeIndex].sumPrecomputed ) {
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<ObjectFile::Reader::Stab>::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) {
- if ( minimizeStab(*it) )
- fStabs.push_back(*it);
+ int soIndex = 0;
+ for(std::vector<ObjectFile::Reader::Stab>::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<HeaderRange>::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<HeaderRange>::iterator it=ranges.begin(); it != ranges.end(); ++it) {
if ( ! it->cannotEXCL ) {
}
}
}
-
+
// 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<ObjectFile::Reader::Stab>::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) {
switch ( it->type ) {
case N_BINCL:
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);
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);
+ }
}
}
}
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 ) {
void Linker::collectStabs()
{
if ( fOptions.readerOptions().fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone ) {
+
+ // make mapping from atoms to ordinal
+ std::map<class ObjectFile::Atom*, uint32_t> atomOrdinals;
+ uint32_t ordinal = 1;
+ for (std::vector<ObjectFile::Atom*>::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<class ObjectFile::Reader*>::iterator it=fReadersThatHaveSuppliedAtoms.begin();
// 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:
case CPU_TYPE_POWERPC:
if ( mach_o::relocatable::Reader<ppc>::validFile(p) )
return this->addObject(mach_o::relocatable::Reader<ppc>::make(p, info.path, info.modTime, fOptions.readerOptions()), info, len);
- else if ( mach_o::dylib::Reader<ppc>::validFile(p) )
- return this->addDylib(mach_o::dylib::Reader<ppc>::make(p, len, info.path, fOptions.readerOptions()), info, len);
+ else if ( mach_o::dylib::Reader<ppc>::validFile(p, info.options.fBundleLoader) )
+ return this->addDylib(mach_o::dylib::Reader<ppc>::make(p, len, info.path, info.options.fBundleLoader, fOptions.readerOptions()), info, len);
else if ( mach_o::archive::Reader<ppc>::validFile(p, len) )
return this->addArchive(mach_o::archive::Reader<ppc>::make(p, len, info.path, info.modTime, fOptions.readerOptions()), info, len);
break;
case CPU_TYPE_POWERPC64:
if ( mach_o::relocatable::Reader<ppc64>::validFile(p) )
return this->addObject(mach_o::relocatable::Reader<ppc64>::make(p, info.path, info.modTime, fOptions.readerOptions()), info, len);
- else if ( mach_o::dylib::Reader<ppc64>::validFile(p) )
- return this->addDylib(mach_o::dylib::Reader<ppc64>::make(p, len, info.path, fOptions.readerOptions()), info, len);
+ else if ( mach_o::dylib::Reader<ppc64>::validFile(p, info.options.fBundleLoader) )
+ return this->addDylib(mach_o::dylib::Reader<ppc64>::make(p, len, info.path, info.options.fBundleLoader, fOptions.readerOptions()), info, len);
else if ( mach_o::archive::Reader<ppc64>::validFile(p, len) )
return this->addArchive(mach_o::archive::Reader<ppc64>::make(p, len, info.path, info.modTime, fOptions.readerOptions()), info, len);
break;
case CPU_TYPE_I386:
if ( mach_o::relocatable::Reader<x86>::validFile(p) )
return this->addObject(mach_o::relocatable::Reader<x86>::make(p, info.path, info.modTime, fOptions.readerOptions()), info, len);
- else if ( mach_o::dylib::Reader<x86>::validFile(p) )
- return this->addDylib(mach_o::dylib::Reader<x86>::make(p, len, info.path, fOptions.readerOptions()), info, len);
+ else if ( mach_o::dylib::Reader<x86>::validFile(p, info.options.fBundleLoader) )
+ return this->addDylib(mach_o::dylib::Reader<x86>::make(p, len, info.path, info.options.fBundleLoader, fOptions.readerOptions()), info, len);
else if ( mach_o::archive::Reader<x86>::validFile(p, len) )
return this->addArchive(mach_o::archive::Reader<x86>::make(p, len, info.path, info.modTime, fOptions.readerOptions()), info, len);
break;
+ case CPU_TYPE_X86_64:
+ if ( mach_o::relocatable::Reader<x86_64>::validFile(p) )
+ return this->addObject(mach_o::relocatable::Reader<x86_64>::make(p, info.path, info.modTime, fOptions.readerOptions()), info, len);
+ else if ( mach_o::dylib::Reader<x86_64>::validFile(p, info.options.fBundleLoader) )
+ return this->addDylib(mach_o::dylib::Reader<x86_64>::make(p, len, info.path, info.options.fBundleLoader, fOptions.readerOptions()), info, len);
+ else if ( mach_o::archive::Reader<x86_64>::validFile(p, len) )
+ return this->addArchive(mach_o::archive::Reader<x86_64>::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";
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;
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;
// 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) );
}
else
clientNameLen = firstDot - clientName;
}
-
+
// Use clientName to check if this dylib is able to link against the allowable clients.
for (std::vector<const char*>::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;
case CPU_TYPE_I386:
this->setOutputFile(new mach_o::executable::Writer<x86>(path, fOptions, fDynamicLibraries));
break;
+ case CPU_TYPE_X86_64:
+ this->setOutputFile(new mach_o::executable::Writer<x86_64>(path, fOptions, fDynamicLibraries));
+ break;
default:
throw "unknown architecture";
}
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();
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;
}
#include <mach-o/loader.h>
#include <mach-o/fat.h>
#include <mach-o/stab.h>
+#include <mach-o/reloc.h>
+#include <mach-o/ppc/reloc.h>
+#include <mach-o/x86_64/reloc.h>
#include <vector>
void checkSection(const macho_segment_command<P>* segCmd, const macho_section<P>* sect);
uint8_t loadCommandSizeMask();
void checkIndirectSymbolTable();
+ void checkRelocations();
+ void checkExternalReloation(const macho_relocation_info<P>* reloc);
+ void checkLocalReloation(const macho_relocation_info<P>* reloc);
+ pint_t relocBase();
+ bool addressInWritableSegment(pint_t address);
const char* fPath;
const macho_header<P>* fHeader;
uint32_t fSymbolCount;
const uint32_t* fIndirectTable;
uint32_t fIndirectTableCount;
-
+ const macho_relocation_info<P>* fLocalRelocations;
+ uint32_t fLocalRelocationsCount;
+ const macho_relocation_info<P>* fExternalRelocations;
+ uint32_t fExternalRelocationsCount;
+ pint_t fRelocBase;
+ bool fWriteableSegmentWithAddrOver4G;
+ const macho_segment_command<P>* fFirstSegment;
+ const macho_segment_command<P>* fFirstWritableSegment;
};
return false;
}
+template <>
+bool MachOChecker<x86_64>::validFile(const uint8_t* fileContent)
+{
+ const macho_header<P>* header = (const macho_header<P>*)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<ppc>::loadCommandSizeMask() { return 0x03; }
template <> uint8_t MachOChecker<ppc64>::loadCommandSizeMask() { return 0x07; }
template <> uint8_t MachOChecker<x86>::loadCommandSizeMask() { return 0x03; }
+template <> uint8_t MachOChecker<x86_64>::loadCommandSizeMask() { return 0x07; }
template <typename A>
MachOChecker<A>::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) )
checkIndirectSymbolTable();
+ checkRelocations();
}
// 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<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
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<P>*)((char*)fHeader + dsymtab->locreloff());
+ if ( dsymtab->locreloff() < linkEditSegment->fileoff() )
+ throw "local relocations not in __LINKEDIT";
+ if ( (dsymtab->locreloff()+fLocalRelocationsCount*sizeof(macho_relocation_info<P>)) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) )
+ throw "local relocations not in __LINKEDIT";
+ }
+ fExternalRelocationsCount = dsymtab->nextrel();
+ if ( fExternalRelocationsCount != 0 ) {
+ fExternalRelocations = (const macho_relocation_info<P>*)((char*)fHeader + dsymtab->extreloff());
+ if ( dsymtab->extreloff() < linkEditSegment->fileoff() )
+ throw "local relocations not in __LINKEDIT";
+ if ( (dsymtab->extreloff()+fExternalRelocationsCount*sizeof(macho_relocation_info<P>)) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) )
+ throw "local relocations not in __LINKEDIT";
+ }
}
break;
}
if ( fStrings == NULL )
throw "missing symbol table";
-
+ fRelocBase = this->relocBase();
}
}
+
+template <>
+ppc::P::uint_t MachOChecker<ppc>::relocBase()
+{
+ if ( fHeader->flags() & MH_SPLIT_SEGS )
+ return fFirstWritableSegment->vmaddr();
+ else
+ return fFirstSegment->vmaddr();
+}
+
+template <>
+ppc64::P::uint_t MachOChecker<ppc64>::relocBase()
+{
+ if ( fWriteableSegmentWithAddrOver4G )
+ return fFirstWritableSegment->vmaddr();
+ else
+ return fFirstSegment->vmaddr();
+}
+
+template <>
+x86::P::uint_t MachOChecker<x86>::relocBase()
+{
+ if ( fHeader->flags() & MH_SPLIT_SEGS )
+ return fFirstWritableSegment->vmaddr();
+ else
+ return fFirstSegment->vmaddr();
+}
+
+template <>
+x86_64::P::uint_t MachOChecker<x86_64>::relocBase()
+{
+ // check for split-seg
+ return fFirstWritableSegment->vmaddr();
+}
+
+
+template <typename A>
+bool MachOChecker<A>::addressInWritableSegment(pint_t address)
+{
+ const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
+ const uint32_t cmd_count = fHeader->ncmds();
+ const macho_load_command<P>* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
+ const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd;
+ if ( (segCmd->initprot() & VM_PROT_WRITE) != 0 ) {
+ if ( (address >= segCmd->vmaddr()) && (address < segCmd->vmaddr()+segCmd->vmsize()) )
+ return true;
+ }
+ }
+ cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+ return false;
+}
+
+
+template <>
+void MachOChecker<ppc>::checkExternalReloation(const macho_relocation_info<P>* reloc)
+{
+ // FIX
+}
+
+template <>
+void MachOChecker<ppc64>::checkExternalReloation(const macho_relocation_info<P>* 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<x86>::checkExternalReloation(const macho_relocation_info<P>* reloc)
+{
+ // FIX
+}
+
+
+template <>
+void MachOChecker<x86_64>::checkExternalReloation(const macho_relocation_info<P>* 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<ppc>::checkLocalReloation(const macho_relocation_info<P>* reloc)
+{
+ if ( reloc->r_address() & R_SCATTERED ) {
+ // scattered
+ const macho_scattered_relocation_info<P>* sreloc = (const macho_scattered_relocation_info<P>*)reloc;
+ // FIX
+
+ }
+ else {
+ // FIX
+ if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) )
+ throw "local relocation address not in writable segment";
+ }
+}
+
+
+template <>
+void MachOChecker<ppc64>::checkLocalReloation(const macho_relocation_info<P>* 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<x86>::checkLocalReloation(const macho_relocation_info<P>* reloc)
+{
+ // FIX
+}
+
+template <>
+void MachOChecker<x86_64>::checkLocalReloation(const macho_relocation_info<P>* 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 <typename A>
+void MachOChecker<A>::checkRelocations()
+{
+ const macho_relocation_info<P>* const externRelocsEnd = &fExternalRelocations[fExternalRelocationsCount];
+ for (const macho_relocation_info<P>* reloc = fExternalRelocations; reloc < externRelocsEnd; ++reloc) {
+ this->checkExternalReloation(reloc);
+ }
+
+ const macho_relocation_info<P>* const localRelocsEnd = &fLocalRelocations[fLocalRelocationsCount];
+ for (const macho_relocation_info<P>* reloc = fLocalRelocations; reloc < localRelocsEnd; ++reloc) {
+ this->checkLocalReloation(reloc);
+ }
+}
+
+
static void check(const char* path)
{
struct stat stat_buf;
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) ) {
else
throw "in universal file, ppc64 slice does not contain ppc64 mach-o";
}
+ else if ( archs[i].cputype == CPU_TYPE_X86_64 ) {
+ if ( MachOChecker<x86_64>::validFile(p + archs[i].offset) )
+ MachOChecker<x86_64>::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";
}
else if ( MachOChecker<ppc64>::validFile(p) ) {
MachOChecker<ppc64>::make(p, length, path);
}
+ else if ( MachOChecker<x86_64>::validFile(p) ) {
+ MachOChecker<x86_64>::make(p, length, path);
+ }
else {
throw "not a known file type";
}
--- /dev/null
+/* -*- 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 <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <mach/mach.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <mach-o/loader.h>
+#include <mach-o/fat.h>
+#include <mach-o/reloc.h>
+#include <mach-o/ppc/reloc.h>
+#include <mach-o/x86_64/reloc.h>
+#include <vector>
+#include <set>
+
+#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 <typename A>
+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<P>* reloc);
+ pint_t* mappedAddressForVMAddress(uint32_t vmaddress);
+
+ const macho_header<P>* fHeader;
+ pint_t fOrignalVMRelocBaseAddress;
+ pint_t fSlide;
+ pint_t fRelocBase;
+ std::vector<vmmap> fVMMApping;
+};
+
+
+
+class MultiArchRebaser
+{
+public:
+ MultiArchRebaser(const char* path, bool writable=false);
+ ~MultiArchRebaser();
+
+ const std::vector<AbstractRebaser*>& getArchs() const { return fRebasers; }
+ void commit();
+
+private:
+ std::vector<AbstractRebaser*> 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<ppc>(&p[fileOffset]));
+ break;
+ case CPU_TYPE_POWERPC64:
+ fRebasers.push_back(new Rebaser<ppc64>(&p[fileOffset]));
+ break;
+ case CPU_TYPE_I386:
+ fRebasers.push_back(new Rebaser<x86>(&p[fileOffset]));
+ break;
+ case CPU_TYPE_X86_64:
+ fRebasers.push_back(new Rebaser<x86_64>(&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<ppc>(mh));
+ }
+ else if ( (OSSwapBigToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC64)) {
+ fRebasers.push_back(new Rebaser<ppc64>(mh));
+ }
+ else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_I386)) {
+ fRebasers.push_back(new Rebaser<x86>(mh));
+ }
+ else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_X86_64)) {
+ fRebasers.push_back(new Rebaser<x86_64>(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 <typename A>
+Rebaser<A>::Rebaser(const void* machHeader)
+ : fHeader((const macho_header<P>*)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<ppc>::getArchitecture() const { return CPU_TYPE_POWERPC; }
+template <> cpu_type_t Rebaser<ppc64>::getArchitecture() const { return CPU_TYPE_POWERPC64; }
+template <> cpu_type_t Rebaser<x86>::getArchitecture() const { return CPU_TYPE_I386; }
+template <> cpu_type_t Rebaser<x86_64>::getArchitecture() const { return CPU_TYPE_X86_64; }
+
+
+template <typename A>
+uint64_t Rebaser<A>::getBaseAddress() const
+{
+ uint64_t lowestSegmentAddress = LLONG_MAX;
+ const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
+ const uint32_t cmd_count = fHeader->ncmds();
+ const macho_load_command<P>* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
+ const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd;
+ if ( segCmd->vmaddr() < lowestSegmentAddress ) {
+ lowestSegmentAddress = segCmd->vmaddr();
+ }
+ }
+ cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+ return lowestSegmentAddress;
+}
+
+template <typename A>
+uint64_t Rebaser<A>::getVMSize() const
+{
+ const macho_segment_command<P>* highestSegmentCmd = NULL;
+ const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
+ const uint32_t cmd_count = fHeader->ncmds();
+ const macho_load_command<P>* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
+ const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd;
+ if ( (highestSegmentCmd == NULL) || (segCmd->vmaddr() > highestSegmentCmd->vmaddr()) ) {
+ highestSegmentCmd = segCmd;
+ }
+ }
+ cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+
+ return ((highestSegmentCmd->vmaddr() + highestSegmentCmd->vmsize() - this->getBaseAddress() + 4095) & (-4096));
+}
+
+
+template <typename A>
+void Rebaser<A>::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 <typename A>
+void Rebaser<A>::adjustLoadCommands()
+{
+ const macho_segment_command<P>* highestSegmentCmd = NULL;
+ const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
+ const uint32_t cmd_count = fHeader->ncmds();
+ const macho_load_command<P>* 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<P>* dylib = (macho_dylib_command<P>*)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<P>* dylib = (macho_dylib_command<P>*)cmd;
+ dylib->set_timestamp(2);
+ }
+ break;
+ case macho_routines_command<P>::CMD:
+ // update -init command
+ {
+ struct macho_routines_command<P>* routines = (struct macho_routines_command<P>*)cmd;
+ routines->set_init_address(routines->init_address() + fSlide);
+ }
+ break;
+ case macho_segment_command<P>::CMD:
+ // update segment commands
+ {
+ macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
+ seg->set_vmaddr(seg->vmaddr() + fSlide);
+ macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>));
+ macho_section<P>* const sectionsEnd = §ionsStart[seg->nsects()];
+ for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
+ sect->set_addr(sect->addr() + fSlide);
+ }
+ }
+ break;
+ }
+ cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+}
+
+
+template <typename A>
+void Rebaser<A>::buildSectionTable()
+{
+ // build vector of sections
+ const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
+ const uint32_t cmd_count = fHeader->ncmds();
+ const macho_load_command<P>* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
+ const macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
+ vmmap mapping;
+ mapping.vmaddr = seg->vmaddr();
+ mapping.vmsize = seg->vmsize();
+ mapping.fileoff = seg->fileoff();
+ fVMMApping.push_back(mapping);
+ }
+ cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+}
+
+
+template <typename A>
+void Rebaser<A>::adjustSymbolTable()
+{
+ const macho_dysymtab_command<P>* dysymtab = NULL;
+ macho_nlist<P>* symbolTable = NULL;
+
+ // get symbol table info
+ const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
+ const uint32_t cmd_count = fHeader->ncmds();
+ const macho_load_command<P>* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ switch (cmd->cmd()) {
+ case LC_SYMTAB:
+ {
+ const macho_symtab_command<P>* symtab = (macho_symtab_command<P>*)cmd;
+ symbolTable = (macho_nlist<P>*)(((uint8_t*)fHeader) + symtab->symoff());
+ }
+ break;
+ case LC_DYSYMTAB:
+ dysymtab = (macho_dysymtab_command<P>*)cmd;
+ break;
+ }
+ cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+
+ // walk all exports and slide their n_value
+ macho_nlist<P>* lastExport = &symbolTable[dysymtab->iextdefsym()+dysymtab->nextdefsym()];
+ for (macho_nlist<P>* 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<P>* lastLocal = &symbolTable[dysymtab->ilocalsym()+dysymtab->nlocalsym()];
+ for (macho_nlist<P>* entry = &symbolTable[dysymtab->ilocalsym()]; entry < lastLocal; ++entry) {
+ if ( entry->n_sect() != NO_SECT )
+ entry->set_n_value(entry->n_value() + fSlide);
+ }
+}
+
+template <typename A>
+void Rebaser<A>::adjustDATA()
+{
+ const macho_dysymtab_command<P>* dysymtab = NULL;
+
+ // get symbol table info
+ const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
+ const uint32_t cmd_count = fHeader->ncmds();
+ const macho_load_command<P>* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ switch (cmd->cmd()) {
+ case LC_DYSYMTAB:
+ dysymtab = (macho_dysymtab_command<P>*)cmd;
+ break;
+ }
+ cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+
+ // walk all local relocations and slide every pointer
+ const macho_relocation_info<P>* const relocsStart = (macho_relocation_info<P>*)(((uint8_t*)fHeader) + dysymtab->locreloff());
+ const macho_relocation_info<P>* const relocsEnd = &relocsStart[dysymtab->nlocrel()];
+ for (const macho_relocation_info<P>* 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<P>::CMD ) {
+ const macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
+ const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>));
+ const macho_section<P>* const sectionsEnd = §ionsStart[seg->nsects()];
+ const uint32_t* const indirectTable = (uint32_t*)(((uint8_t*)fHeader) + dysymtab->indirectsymoff());
+ for(const macho_section<P>* 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<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+
+}
+
+
+template <typename A>
+typename A::P::uint_t* Rebaser<A>::mappedAddressForVMAddress(uint32_t vmaddress)
+{
+ for(typename std::vector<vmmap>::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<x86_64>::doLocalRelocation(const macho_relocation_info<x86_64::P>* 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<ppc>::doLocalRelocation(const macho_relocation_info<P>* 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<P>* sreloc = (macho_scattered_relocation_info<P>*)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<x86>::doLocalRelocation(const macho_relocation_info<P>* 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<P>* sreloc = (macho_scattered_relocation_info<P>*)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 <typename A>
+void Rebaser<A>::doLocalRelocation(const macho_relocation_info<P>* 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 <typename A>
+void Rebaser<A>::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<ppc64>::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<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
+ const uint32_t cmd_count = fHeader->ncmds();
+ const macho_load_command<P>* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
+ const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)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<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+ // just use base address
+ fRelocBase = (pint_t)fHeader;
+ fOrignalVMRelocBaseAddress = this->getBaseAddress();
+}
+
+template <>
+void Rebaser<x86_64>::setRelocBase()
+{
+ // reloc addresses are always based from the start of the first writable segment
+ const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
+ const uint32_t cmd_count = fHeader->ncmds();
+ const macho_load_command<P>* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
+ const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd;
+ if ( segCmd->initprot() & VM_PROT_WRITE ) {
+ fRelocBase = segCmd->fileoff() + (pint_t)fHeader;
+ fOrignalVMRelocBaseAddress = segCmd->vmaddr();
+ return;
+ }
+ }
+ cmd = (const macho_load_command<P>*)(((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<archInfo> 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<cpu_type_t>& onlyArchs)
+{
+ const MultiArchRebaser mar(info.path);
+ const std::vector<AbstractRebaser*>& rebasers = mar.getArchs();
+ for(std::set<cpu_type_t>::iterator ait=onlyArchs.begin(); ait != onlyArchs.end(); ++ait) {
+ for(std::vector<AbstractRebaser*>::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<AbstractRebaser*>& rebasers = mar.getArchs();
+ for(std::vector<archInfo>::const_iterator fait=info.archs.begin(); fait != info.archs.end(); ++fait) {
+ for(std::vector<AbstractRebaser*>::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<fileInfo>& files)
+{
+ uint64_t totalSize = 0;
+ for(std::vector<fileInfo>::iterator fit=files.begin(); fit != files.end(); ++fit) {
+ fileInfo& fi = *fit;
+ for(std::vector<archInfo>::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<fileInfo>& 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 <arch>] files...\n");
+}
+
+
+int main(int argc, const char* argv[])
+{
+ std::vector<fileInfo> files;
+ std::set<cpu_type_t> 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<fileInfo>::iterator it=files.begin(); it != files.end(); ++it) {
+ setSizes(*it, onlyArchs);
+ }
+
+ // assign new base address for each arch
+ for(std::set<cpu_type_t>::iterator ait=onlyArchs.begin(); ait != onlyArchs.end(); ++ait) {
+ cpu_type_t arch = *ait;
+ uint64_t baseAddress = startAddress(arch, files, lowAddress, highAddress);
+ for(std::vector<fileInfo>::iterator fit=files.begin(); fit != files.end(); ++fit) {
+ fileInfo& fi = *fit;
+ for(std::vector<archInfo>::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<fileInfo>::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;
+}
+
+
+
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
# 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
--- /dev/null
+##
+# 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
+
+
--- /dev/null
+/* -*- 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;
+}
+
--- /dev/null
+/* -*- 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 <stdio.h>
+
+extern int bar();
+
+int foo()
+{
+ return bar();
+}
--- /dev/null
+/* -*- 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 <stdio.h>
+
+int main()
+{
+ return 0;
+}
+
--- /dev/null
+##
+# 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}
+
+
--- /dev/null
+
+
+// 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(); }
+
+
--- /dev/null
+/* -*- 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() { }
--- /dev/null
+##
+# 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
+
+
--- /dev/null
+int bar()
+{
+ return 10;
+}
--- /dev/null
+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
--- /dev/null
+int foo()
+{
+ return 10;
+}
--- /dev/null
+int main()
+{
+ return 0;
+}
--- /dev/null
+#!/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";
+ }
+}
+
+
-0000 SO CWD
+0000 SO CWD/
0000 SO hello.cxx
0001 OSO CWD/hello.o
0000 BNSYM
0000 FUN
0000 ENSYM
0000 SO
-0000 SO CWD
+0000 SO CWD/
0000 SO other.cxx
0001 OSO CWD/other.o
0000 BNSYM
--- /dev/null
+##
+# 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
+
+
--- /dev/null
+/* -*- 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 <stdio.h>
+
+int main()
+{
+ fprintf(stdout, "hello\n");
+}
--- /dev/null
+##
+# 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
+
+
--- /dev/null
+
+
+#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
+
+ .align 2
_bar:
nop
+ .align 2
.globl _xx
.globl __xx
_xx:
nop
+ .align 2
_ok:
nop
--- /dev/null
+##
+# 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}
--- /dev/null
+/* -*- 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 <Foundation/Foundation.h>
+
+
+@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
+
--- /dev/null
+##
+# 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
+
+
--- /dev/null
+#include <Foundation/Foundation.h>
+
+@interface Bar : NSObject
+
+-(void) blah;
+
+@end
+
+@implementation Bar
+
+-(void) blah {}
+
+@end
\ No newline at end of file
--- /dev/null
+
+int foo() { return 10; }
+
+void* foop = &foo;
+
+int glob = 5;
+
+int* globp = &glob;
+
+
+int big[3000];
+
+
+
\ No newline at end of file
+#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:
#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
.long _test_calls+16
.long _external
.long _external+16
-#elif __ppc64__
+#elif __ppc64__ || __x86_64__
.quad _test_calls
.quad _test_calls+16
.quad _external
${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:
--- /dev/null
+##
+# 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
+
+
--- /dev/null
+
+// a tentative definition
+int a;