-2008-05-06 Nick Kledzik <kledzik@apple.com>
+----- Tagged ld64-95.2.12
- <rdar://problem/5905889> ARM ld should take W bit off of maxprot for __TEXT segment
- * src/MachOWriterExecutable.hpp: for iPhone always set maxprot to be initprot in all segments
+2009-07-02 Nick Kledzik <kledzik@apple.com>
+ <rdar://problem/6836647> creation of __unwind_info section can fail if hundreds of functions cannot be compact encoded
-2008-05-06 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/5914343> encryptable images may not be signable
- * src/MachOWriterExecutable.hpp: use minimum header padding when aligning __text section
-
+----- Tagged ld64-95.2.11
------ Tagged ld64-85
+2009-06-19 Nick Kledzik <kledzik@apple.com>
-2008-04-29 Nick Kledzik <kledzik@apple.com>
+ <rdar://problem/6960981> Link Time Optimization errors out when targeting < 10.6
- * ld64.xcodeproj/project.pbxproj: <llvm-c/lto.h> is moving from /usr/local/include to /Developer/usr/local/include
+----- Tagged ld64-95.2.10
-2008-04-29 Nick Kledzik <kledzik@apple.com>
+2009-04-02 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/5829579> ld doesn't honor "rightmost" -syslibroot argument
- * src/Options.cpp: if last -syslibroot is /, then ignore all syslibroots
+ <rdar://problem/6805002> corrupt metaclass entry in dynamic library
+ * src/ld/ld.cpp: change Section constructor to copy segment and section names
-2008-04-29 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/5866582> GLRendererFloat has bad __eh_frame section caused by mixing llvm-gcc and gcc object files
- * src/MachOReaderRelocatable.hpp: make all atoms in __eh_frame section have 1-byte alignment
- * src/MachOWriterExecutable.hpp: make __eh_frame section have pointer sized alignment
+----- Tagged ld64-95.2.9
+2009-04-02 Nick Kledzik <kledzik@apple.com>
-2008-04-17 Nick Kledzik <kledzik@apple.com>
+ <rdar://problem/6744267> Update ld64 for new triples introduced in 6654669 to support ARM LLVM
+ * src/ld/LTOReader.hpp: change "arm-" to "arm" so matching works for new triples
- * src/MachOReaderRelocatable.hpp: better cpu subtype support
+----- Tagged ld64-95.2.8
-2008-04-14 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/5733759> ld64 has bad ARM branch island check
- * src/MachOWriterExecutable.hpp: in addBranchIslands() don't force large arm programs to fail
-
+2009-03-24 Nick Kledzik <kledzik@apple.com>
-2008-04-10 Nick Kledzik <kledzik@apple.com>
+ <rdar://problem/6713931> anonymous functions have the compact unwind info computed wrong
+ * ld/MachOReaderRelocatable.hpp: use new compact unwind function in AnonymousAtom
- * src/MachOWriterExecutable.hpp: fix stubs used with lazy dylibs
-
------ Tagged ld64-84.4
+----- Tagged ld64-95.2.7
-2008-04-10 Nick Kledzik <kledzik@apple.com>
+2009-03-11 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/5847206> SPEC2000/eon built with -mdynamic-no-pic won't run
- * src/Architectures.hpp: added arm::kReadOnlyPointer
- * src/MachOReaderRelocatable.hpp: generate arm::kReadOnlyPointer
- * src/MachOWriterExecutable.hpp: use arm::kReadOnlyPointer
- * src/machochecker.cpp: allow MH_PIE bit
- * unit-tests/test-cases/switch-jump-table: added test cases
+ <rdar://problem/6670421> AddressBook incorrectly gets _objc_msgSend from WebKit
+ * src/ld/MachOReaderDylib.hpp: fix processIndirectLibraries() to not force a private re-export of a dylib
+ that is already explictly or implicitly linked.
+ * unit-tests/test-cases/re-export-optimizations-indirect: add test case
------ Tagged ld64-84.3
+2009-03-10 Nick Kledzik <kledzik@apple.com>
-2008-04-09 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/5852023> -undefined dynamic_lookup busted
- * src/ld.cpp: don't create proxy atom when scanning for dylib duplicates
- * unit-tests/test-cases/tentative-and-archive: use -undefined dynamic_lookup
+ <rdar://problem/6665853> dyld weak linking optimization leaves some symbols unbound
+ * src/ld/MachOWriterExecutable.hpp: be sure to create bind entry for a reference
+ to a symbol in a dylib that is a weak definition
+ * unit-tests/test-cases/coalesce_weak_def_in_dylib: add test case
------ Tagged ld64-84.2
+2009-03-10 Nick Kledzik <kledzik@apple.com>
-2008-04-04 Nick Kledzik <kledzik@apple.com>
+ <rdar://problem/6666004> many OS i386 OS dylibs still have __IMPORT segment
+ * ld/MachOReaderRelocatable.hpp: moved where __IMPORT/__pointer is changed to __DATA/__nl_symbol_ptr
+ * unit-tests/test-cases/stripped-indirect-symbol-table: updated to test for this problem
- * src/ld.cpp: don't add .eh symbols to symbol table in -r mode
- * unit-tests/test-cases/eh-coalescing-r: update to test out of order coalescing
+
+----- Tagged ld64-95.2.6
+
+2009-02-27 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6633530> ld might set MH_WEAK_DEFINES when it should not
+ * src/ld/MachOWriterExecutable.hpp: only consider atoms in fRegularDefAtomsThatOverrideADylibsWeakDef
+ that will be exported when computing MH_WEAK_DEFINES
+ * unit-tests/test-cases/operator-new: updated to reproduce issue
------ Tagged ld64-84.1
+----- Tagged ld64-95.2.5
-2008-03-28 Nick Kledzik <kledzik@apple.com>
+2009-02-24 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/5720961> ld should prefer architecture-specific variant over generic in fat object file
- * src/Options.cpp: fully process -arch arguments into fArchitecture and fSubArchitecture
- * src/ld.cpp: when -arch with a subtype is used, try to find the exact subtype from fat files
- * unit-tests/test-cases/cpu-sub-types-preference: added test cases for arm and ppc
+ <rdar://problem/6605499> x86_64 obj-c runtime confused when static lib is stripped
+ * src/ld/MachOWriterExecutable.hpp: in setLocalNlist() don't use 'l' labels for x86_64 strings
+ * unit-tests/test-cases/objc-literal-pointers-strip: added test case
------ Tagged ld64-84
+----- Tagged ld64-95.2.4
-2008-03-28 Nick Kledzik <kledzik@apple.com>
+2009-02-23 Nick Kledzik <kledzik@apple.com>
- * src/LTOReader.hpp: don't print lto version, if lto is unavailable
+ * src/ld/MachOReaderRelocatable.hpp: ignore ARM_THUMB_32BIT_BRANCH relocs
+
+
+2009-02-18 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6583555> Writer<A>::symbolIndex() uses a linear search and does not scale
+ * src/ld/MachOWriterExecutable.hpp: build a std::map so symbolIndex() scales better
-2008-03-26 Nick Kledzik <kledzik@apple.com>
+2009-02-18 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/5575399> Add LD_WARN_COMMONS to BigBear builds
- * src/Options.cpp: Add support for LD_WARN_FILE which copies all warnings to a side file
+ <rdar://problem/6312070> Use new compact encodings that handle all register permutations
+ * src/ld/Architectures.hpp: add kSectionOffset24
+ * src/ld/ObjectFile.h: add getFDE()
+ * src/ld/MachOReaderRelocatable.hpp: use new libunwind functions to get new compact encoding
+ * src/ld/MachOWriterExecutable.hpp: use new compact encoding which includes offset in dwarf if needed
+ * src/other/unwinddump.cpp: update unwinddump output to display register save set
-2008-03-26 Nick Kledzik <kledzik@apple.com>
+2009-02-16 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/5797713> Need encryption tag in mach-o file
- <rdar://problem/5811920> linker should adjust arm final linked images so __text is never on the same page as the load commands
- * src/MachOFileAbstraction.hpp: add support for encryption_info_command
- * src/Options.cpp: add support for LD_NO_ENCRYPT and -no_encryption
- * src/MachOWriterExecutable.hpp: add EncryptionLoadCommandsAtom
- * src/machochecker.cpp: validate LC_ENCRYPTION_INFO
+ <rdar://problem/6511619> runtime error with bundle for 10.5 that has weak external symols
+ * src/ld/ld.cpp: fix hybrid (10.5) compressed linkedit info for data pointing to weak definitions
-2008-03-25 Nick Kledzik <kledzik@apple.com>
+2009-02-15 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/5712533> ld64 does not recognize LLVM bitcode archive files
- * src/MachOReaderArchive.hpp: renamed to src/ArchiveReader.hpp
- * src/ArchiveReader.hpp: sniff each member and instantiate correct reader
- * src/ld.cpp: rename mach_o::archive::Reader to archive::Reader
- * ld64.xcodeproj/project.pbxproj: rename MachOReaderArchive.hpp to ArchiveReader.hpp
- * unit-tests/test-cases/llvm-integration: added test case
+ <rdar://problem/6583757> i386 relocation error with negative offsets from local labels
+ * src/ld/MachOReaderRelocatable.hpp: handle when base addr of scattered relocation does not point to a label
+ * unit-tests/test-cases/relocs-neg-from-local: add test case
+
+2009-02-12 Nick Kledzik <kledzik@apple.com>
-2008-03-25 Nick Kledzik <kledzik@apple.com>
+ <rdar://problem/6578360> -dead_strip inhibits weak coalescing in no_dead_strip section
+ * src/ld/ld.cpp: remove atoms coalesced away from fLiveRootAtoms
+ * unit-tests/test-cases/dead_strip-weak-coalesce: added test case
- <rdar://problem/5771658> ld64 should switch to new libLTO.dylib interface
- <rdar://problem/5675690> Produce llvm bc file in 'ld -r' mode if all .o files are llvm bc
- * src/LTOReader.hpp: rewrite from LLVMReader.hpp to use new lto_* C interface
- * unit-tests/test-cases/llvm-integration: update and comment
- * ld64.xcodeproj/project.pbxproj: update to lazy load libLTO.dylib
- * src/ld.cpp: rework and simplify Linker::optimize()
- * src/ObjectDump.cpp: Add -nm option
+
+2009-02-12 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6581809> x86_64 weak_import broken for initialized data
+ * src/ld/MachOReaderRelocatable.hpp: use isWeakImportSymbol() in Reader<x86_64>::addRelocReference()
+ * src/other/dyldinfo.cpp: update to display weak_import attribute
+ * unit-tests/test-cases/weak_import: updated test case
-2008-03-25 Nick Kledzik <kledzik@apple.com>
+2009-02-06 Nick Kledzik <kledzik@apple.com>
- * src/MachOReaderRelocatable.cpp: Fix some .objc_class_name_ off by one problem
- * src/MachOWriterExecutable.cpp: Fix some .objc_class_name_ off by one problem
+ <rdar://problem/6541812> ld parsing of __eh_frame unwind information is slow
+ * src/ld/MachOReaderRelocatable.hpp: build a std::map of all __eh_frame relocations for x86_64
-2008-03-24 Nick Kledzik <kledzik@apple.com>
+----- Tagged ld64-95.2.3
- <rdar://problem/5814613> Xcode 3.1 breaks linkage of libgcj.9.dylib from gcc 4.3.0
- * src/MachOWriterExecutable.cpp: Make sure all ivars in Writer are initialized.
+2009-02-04 Nick Kledzik <kledzik@apple.com>
+ <rdar://problem/6545406> ld: warning: can't add line info to anonymous symbol
+ * src/ld/MachOReaderRelocatable.hpp: don't warn about line info in dyld stubs
-2008-03-21 Nick Kledzik <kledzik@apple.com>
- * src/Options.cpp: warn if -seg1addr value is not page aligned
-
+----- Tagged ld64-95.2.2
-2008-03-21 Nick Kledzik <kledzik@apple.com>
+2009-02-02 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/5806437> Move ARM support outside of __OPEN_SOURCE__
- * src/ld.cpp: remove __OPEN_SOURCE__ around arm support
- * src/LLVMReader.hpp: remove __OPEN_SOURCE__ around arm support
- * src/MachOReaderDylib.hpp: remove __OPEN_SOURCE__ around arm support
- * src/ObjectFile.h: remove __OPEN_SOURCE__ around arm support
- * src/MachOReaderRelocatable.hpp: remove __OPEN_SOURCE__ around arm support
- * src/OpaqueSection.hpp: Cover arm support inside __OPEN_SOURCE__ macro check
- * src/MachOWriterExecutable.hpp: remove __OPEN_SOURCE__ around arm support
- * src/ObjectDump.cpp: remove __OPEN_SOURCE__ around arm support
- * ld64.xcodeproj/project.pbxproj: remove ARM_SUPPORT from config.h
-
+ <rdar://problem/6548268> ld -r does not preserve the N_NO_DEAD_STRIP bit
+ * src/ld/MachOWriterExecutable.hpp: set N_NO_DEAD_STRIP based on dontDeadStrip()
+ * unit-tests/test-cases/dead_strip-r_symbol_desc: added test case
------ Tagged ld64-83.2
+
+----- Tagged ld64-95.2.1
+
+2009-01-29 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6535736> ld coalesces C strings in different segments
+ * src/ld/MachOReaderRelocatable.hpp: only do standard coalescing on __cstring section if is in __TEXT segment
+ * unit-tests/test-cases/cstring-alt-segment: add test case
-2008-03-15 Nick Kledzik <kledzik@apple.com>
+
+2009-01-29 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6532377> gcc DejaGnu failure: building longcall/dylib library
+ * src/ld/MachOWriterExecutable.hpp: if no __DATA sections insert non-lazy pointers at end of __TEXT segment
+ * unit-tests/test-cases/no-data-bundle: added test case
+
+
+----- Tagged ld64-95.2
+
+2009-01-06 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6476760> strip -S fails with "new trie is larger than original"
+ * src/other/PruneTrie.cpp: don't align trie more than original trie was aligned
- <rdar://problem/5801620> ld64-83 removes OBJC_CLASS_$ symbols from projects, causes catastrophic results
- * src/Options.cpp: restore "case CPU_TYPE_ARM" in switch statement for .objc_class symbols in .exp files
- * unit-tests/test-cases/objc-exported_symbols_list: added test case
+
+----- Tagged ld64-95.1
+
+2008-12-21 Nick Kledzik <kledzik@apple.com>
+
+ * src/ld/MachOWriterExecutable.hpp: in new linkedit format, make sure only exported symbols
+ make it into weak binding info
------ Tagged ld64-83.1
+----- Tagged ld64-95
-2008-03-14 Nick Kledzik <kledzik@apple.com>
+2008-12-18 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/5800466> -iphone_version_min ==> -iphoneos_version_min
- * src/Options.cpp: support -iphoneos_version_min as well
+ * src/ld/Options.cpp: move check for fSharedRegionEligible until fPrebind has stabilized
------ Tagged ld64-83
-
-2008-03-10 Nick Kledzik <kledzik@apple.com>
+2008-12-18 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/5791543> ld needs to strip iphone_version_min option if invoking ld_classic
- * src/Options.cpp: suppress -iphone_version_min from being passed to ld_classic
+ <rdar://problem/6305021> Generate new compressed LINKEDIT when targeting 10.6
+ * src/ld/Options.cpp: turn on compressed LINKEDIT by default
-2008-03-04 Nick Kledzik <kledzik@apple.com>
+----- Tagged ld64-94.1
- <rdar://problem/4171253> ADOBE XCODE: Linker option to lazy load frameworks (cause dyld is too slow)
- * src/MachOWriterExecutable.hpp: create lazy stubs and LC_LAZY_LOAD_DYLIB for lazy load dylibs
- * src/Options.cpp: support -lazy-l, -lazy_library, and -lazy_framework
- * src/MachOFileAbstraction.hpp: add LC_LAZY_LOAD_DYLIB and S_LAZY_DYLIB_SYMBOL_POINTERS until in cctools
- * src/MachOReaderDylib.hpp: add isLazyLoadedDylib()
- * src/ld.cpp: pass lazy helper atom to writer
- * doc/man/man1/ld.1: document new options
- * unit-tests/test-cases/lazy-dylib-objc: add test case
- * unit-tests/test-cases/lazy-dylib: add test case
-
+2008-12-16 Nick Kledzik <kledzik@apple.com>
------ Tagged ld64-82.7
+ * src/ld/Options.cpp: Fix -F handling in buildSearchPaths()
-2008-03-07 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/5787149> duplicate symbol literal-pointer@__OBJC@__message_refs@...
- * src/MachOReaderRelocatable.hpp: AnonymousAtom from S_LITERAL_POINTERS section should be weak
- * unit-tests/test-cases/objc-selector-coalescing: added test case
+----- Tagged ld64-94
+
+2008-12-15 Nick Kledzik <kledzik@apple.com>
+ * doc/man/man1/ld.1: document new options
------ Tagged ld64-82.6
-2008-03-04 Nick Kledzik <kledzik@apple.com>
+2008-12-15 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/5779681> ld crashes building XsanFS for Snow Leopard Builds
- * src/ld.cpp: add bool dylibsOnly parameter to addJustInTimeAtoms()
- * unit-tests/test-cases/tentative-and-archive: added test case
+ <rdar://problem/6134468> linker should enforce all .o files have same sub-type, and ignore sub-type of dylibs
+ * doc/man/man1/ld.1: update man page about -allow_sub_type_mismatches
+ * src/ld/ld.cpp: call validFile() with new arguments
+ * src/ld/MachOReaderRelocatable.hpp: add new arguments to validFile()
+ * src/ld/Options.cpp: Support LD_ALLOW_CPU_SUBTYPE_MISMATCHES and -allow_sub_type_mismatches
-2008-03-04 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/5775822> ld64 should not force building with gcc 4.0
- * ld64.xcodeproj/project.pbxproj: change rules to use "system" compiler instead of 4.0
-
+2008-12-15 Nick Kledzik <kledzik@apple.com>
-2008-02-29 Nick Kledzik <kledzik@apple.com>
+ <rdar://problem/6438270> -syslibroot should skip standard search paths not in the SDK
+ * src/ld/Options.cpp: in buildSearchPaths() if an SDK is specified don't add
+ standard search paths not in the SDK.
- <rdar://problem/5774730> Simulator frameworks are being build split-seg and not prebound
- * src/Options.cpp: only splitseg if prebound
+2008-12-15 Nick Kledzik <kledzik@apple.com>
-2008-02-29 Nick Kledzik <kledzik@apple.com>
+ <rdar://problem/6406609> ld: remove "can't make compact unwind encoding" warning
+ * src/ld/ObjectFile.h: add fWarnCompactUnwind
+ * src/ld/Options.cpp: -warn_compact_unwind --> fWarnCompactUnwind
+ * src/ld/MachOReaderRelocatable.hpp: test fWarnCompactUnwind before warning
- <rdar://problem/5774231> Linker should not make GSYM debug note for .objc_category_* symbols
- * src/ld.cpp: suppress GSYM debug notes for absolute symbols
- * unit-tests/test-cases/objc-category-debug-notes: added test case
+2008-12-15 Nick Kledzik <kledzik@apple.com>
-2008-02-29 Nick Kledzik <kledzik@apple.com>
+ <rdar://problem/6442926> Add dtrace usdt support for arm to ld64
+ * src/ld/MachOWriterExecutable.hpp: handle arm::kDtraceIsEnabledSite
+ * unit-tests/test-cases/dtrace-static-probes: use is-enabled in test case
- <rdar://problem/5768970> non-ASCII CFString support is broken
- * src/MachOReaderRelocatable.hpp: only name and coalesce cfstring constants if they use a __cstring
- * unit-tests/test-cases/cfstring-utf16: add test case
+----- Tagged ld64-93
-2008-02-25 Nick Kledzik <kledzik@apple.com>
+2008-12-11 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/5366363> ld -r -x
- * doc/man/man1/ld.1: update man page to explain -r -x produces auto-stripped labels
+ * src/ld/ObjectFile.h: add fIPhoneVersionMin to track min iPhoneOS version
+ * src/ld/Options.cpp: use fIPhoneVersionMin
+
+2008-12-11 Nick Kledzik <kledzik@apple.com>
------ Tagged ld64-82.5
+ <rdar://problem/6431277> non-lazy pointer to non-global tentative definition encoded wrong
+ * src/ld/MachOWriterExecutable.hpp: don't use INDIRECT_SYMBOL_LOCAL for tentative definitions
+ * unit-tests/test-cases/non-lazy-r: updated test case
-2008-02-12 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/5738023> x86_64: -stack_size failure when large __bss is used
- * src/ld.cpp: only move section already in __DATA segment to new __huge section
- * unit-tests/test-cases/stack_size_no_addr: updated test case to add large bss section
+2008-12-11 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6437667> kernel fails to boot when ld64 used for intermediate ld -r step
+ * src/ld/MachOWriterExecutable.hpp: in -r mode when generating a scattered sect-diff reloc for
+ i386/arm, special case when from target is not the atom
+ the relocation is in.
+ * unit-tests/test-cases/relocs-asm: update test case
------ Tagged ld64-82.4
+2008-12-11 Nick Kledzik <kledzik@apple.com>
-2008-02-06 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/5726215> comdat warnings with ld -r of C++ .o files
- * unit-tests/test-cases/eh-coalescing-r: added test case
- * src/ld.cpp: in ld -r mode don't warn about if .eh symbols are not static
-
+ * src/ld/ld.cpp: handle new __program_vars section
+ * src/ld/MachOWriterExecutable.hpp: handle inserting synthesized sections when there is no __dyld section
-2008-02-06 Devang Patel <dpatel@apple.com>
- <rdar://problem/5724990> LTO of Bom framework with -dead_strip causes ld(1) crash
- * src/LLVMReader.hpp: Check fAtom while determining LLVMReference target binding.
- * unit-tests/test-cases/llvm-integration/Makefile: Add new test case.
- * unit-tests/test-cases/llvm-integration/a15.c: New.
- * unit-tests/test-cases/llvm-integration/b15.c: New.
- * unit-tests/test-cases/llvm-integration/c15.c: New.
-
-2008-02-05 Nick Kledzik <kledzik@apple.com>
+2008-12-11 Nick Kledzik <kledzik@apple.com>
- * src/ld.cpp: fix for -arch ppc -mdynamic-no-pic -pie so PPC_RELOC_HA16 reloc is used
+ * src/ld/MachOReaderRelocatable.hpp: Fix getDescription() to work when direct reference is to anonymous atom
+
+
+2008-12-10 Nick Kledzik <kledzik@apple.com>
+
+ * src/ld/Options.cpp: enable LD_FORCE_NO_PREBIND to be used with arm
+
+
+2008-12-10 Nick Kledzik <kledzik@apple.com>
------ Tagged ld64-82.3
+ <rdar://problem/6258169> Developer tool to print the new compressed LINKEDIT information
+ * src/other/dyldinfo.cpp: fix typo in usage()
-2008-02-04 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/5721186> ld doesn't seem to understand $ld$add$os... and $ld$hide$os... for 10.6 moves
- * src/ObjectFile.h: add 10.6
- * src/Options.cpp: add 10.6 support
- * src/MachOReaderDylib.hpp: recognize $os10.6$
-
+2008-12-05 Nick Kledzik <kledzik@apple.com>
------ Tagged ld64-82.2
+ <rdar://problem/6308882> SnowLeopard kernel should compile warning free
+ * src/ld/MachOReaderRelocatable.hpp: correct parse two global labels at end of section and make one an alias
+ * unit-tests/test-cases/end-label: update test case
-2008-01-30 Devang Patel <dpatel@apple.com>
- <rdar://problem/5714833> Can't build 64-bit Intel binaries with LTO
- <rdar://problem/5714787> ld64 fails to build with llvm-gcc-4.2
- * src/LLVMReader.hpp: Fix character count typo in strncmp call.
- Use const char * to initialize temp. string.
- * ld64.xcodeproj/project.pbxproj: use $(DEVELOPER_DIR) in header search construction
- instead of hard coding /Developer.
-
------ Tagged ld64-82.1
+2008-12-04 Nick Kledzik <kledzik@apple.com>
-2008-01-23 Nick Kledzik <kledzik@apple.com>
+ <rdar://problem/6342245> Better warning than "PPC_RELOC_JBSR should not be using an external relocation"
+ * src/ld/MachOReaderRelocatable.hpp: issue warning with .o path if it was compiled with -mlong-branch
- * src/MachOReaderRelocatable.hpp: don't bus error if S_LITERAL_POINTERS is missing relocs
+
+2008-12-04 Nick Kledzik <kledzik@apple.com>
+ <rdar://problem/6408832> linker should not map __pointers -> __nl_symbol_ptr unless actually making new LINKEDIT
+ * src/ld/ObjectFile.h: add fMakeCompressedDyldInfo for readers to see
+ * src/ld/Options.cpp: set fMakeCompressedDyldInfo for readers to see
+ * src/ld/MachOReaderRelocatable.hpp: check fMakeCompressedDyldInfo
-2008-01-22 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/5695496> ld uses 32-bits in some places to hold addresses when parsing 64-bit mach-o files
- * src/MachOReaderRelocatable.hpp: use AddrToAtomMap type that switch address to 64-bits for 64-bit archs
- * src/MachOWriterExecutable.hpp: verify BR14 does not overflow for external relocs
- * unit-tests/test-cases/relocs-c: update test case to slide addresses to verify x86_64 .o files
+2008-12-02 Nick Kledzik <kledzik@apple.com>
+ * src/ld/debugline.c: fix error handling in line_open()
------ Tagged ld64-82
-
-2008-01-18 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/5694612> Bad grammar used in ld warning: cannot exported hidden symbol
- * src/ld.cpp: fix typo in warning string
-
+2008-11-26 Nick Kledzik <kledzik@apple.com>
-2008-01-16 Nick Kledzik <kledzik@apple.com>
+ <rdar://problem/6401277> vtable with thumb entries broke after ld -r
+ * src/ld/MachOReaderRelocatable.hpp: if target of reloc is thumb, mask thumb bit off addend
+ * unit-tests/test-cases/thumb-pointer: added test case
- <rdar://problem/5565074> Bundle Loader does not work anymore when loader is a bundle
- <rdar://problem/5590203> ld warns of incorrect architecture when linking a bundle to a bundle
- * src/MachOReaderDylib.hpp: support linking against bundles via -bundle_loader. Clean up error messages
- * unit-tests/test-cases/bundle_loader: update test case
-
-
-2008-01-16 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/5366363> ld -r -x creates debug notes (stabs) when it should not with -x (keep only global symbols)
- * src/Options.cpp: in reconfigureDefaults() if -r and -x then -S
-
-2008-01-16 Nick Kledzik <kledzik@apple.com>
+2008-11-26 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/5566068> if ld crashes while writing output file, it should delete the half written file
- * src/MachOWriterExecutable.hpp: wrap open/write/close in try block and add signal handlers all to delete
- output file on failure.
+ * src/ld/Option.cpp: Fix how crashreporterBuffer is created to not miss some arguments
-2008-01-16 Devang Patel <dpatel@apple.com>
+2008-11-24 Nick Kledzik <kledzik@apple.com>
- * src/LLVMReader.hpp: Use __gnu_cxx::hash_map instead of hash supported by LLVM.
+ <rdar://problem/6398605> Security.framework has some duplicate FDEs for some functions
+ * src/ld/ld.cpp: remove fDeadAtoms from fLiveAtoms when there are weak atoms overriden by late loads
+ * unit-tests/test-cases/dead_strip-archive-eh: added test case
-2008-01-16 Nick Kledzik <kledzik@apple.com>
+----- Tagged ld64-92
- <rdar://problem/5593537> GC-supported library can't be linked into GC-required executable
- * src/ld.cpp: loosen constraint that all objc code must be compiled with same GC settings and
- allow gc-compatible code to be linked into anything.
- * unit-tests/test-cases/objc-gc-checks: update test case
+2008-11-21 Nick Kledzik <kledzik@apple.com>
+ * src/ld/MachOReaderDylib.hpp: if export_size is zero, no need to parse trie
+ * src/abstraction/MachOTrie.hpp: gracefully handle empty trie
+
-2008-01-15 Nick Kledzik <kledzik@apple.com>
+2008-11-21 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/5687976> no debug notes for custom named data
- * src/ld.cpp: in synthesizeDebugNotes() check getSymbolTableInclusion() instead of for leading underscore
- * unit-tests/test-cases/dwarf-debug-notes: update test case
+ <rdar://problem/6257854> strip(1) support for new compressed LINKEDIT information
+ * ld64.xcodeproj/project.pbxproj: build and install new libprunetrie.a
+ * src/other/prune_trie.h: added
+ * src/other/PruneTrie.cpp: implements prune_trie()
------ Tagged ld64-81.5
-2008-01-14 Devang Patel <dpatel@apple.com>
+2008-11-21 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/5683813> llvm-gcc-4.2 fails to build Sqlite 3.5.4 with -O4
- * src/LLVMReader.hpp: Resolve proxy references. Collect new unbounded references
- after optimization.
- * src/ld.cpp: Resolve additional unbounded references after optimization.
+ * src/ld/ld.cpp: if an export file is used and all weak symbols are masked, don't set WEAK_DEFINES
+ * unit-tests/test-cases/weak-def-flag: added test case
+
+
+2008-11-20 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6305021> Generate new compressed LINKEDIT when targeting 10.6
+ * src/ld/MachOWriterExecutable.hpp: support generating new compressed format
+ * src/ld/MachOReaderRelocatable.hpp: new compress format implies non-lazy pointers in __DATA for i386
+ * src/ld/MachOReaderDylib.hpp: support linking aginst new format
+ * src/ld/Options.cpp: suppport -exported_symbols_order and -no_compact_linkedit
+ * src/ld/ld.cpp: track which atoms have weak counter parts in dylibs
+ * src/other/dyldinfo.cpp: added tool to display new LINKEDIT format
+ * ld64.xcodeproj/project.pbxproj: add dyldinfo tool
+ * unit-tests/*: lots of fixes to work with new format
+
+
+2008-11-20 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6389316> ld64 should preserve N_WEAK_REF when linking MH_KEXT_BUNDLEs
+ * src/ld/MachOWriterExecutable.hpp: set up fWeakImportMap in synthesizeKextGOT()
+
+
+2008-11-19 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6342406> VideoToolbox.framework has bad __TEXT.__eh_frame info
+ * src/ld/Options.cpp: add -no_eh_labels option for use with -r
+ * src/ld/MachOWriterExecutable.hpp: generate correct x86_64 labeless relocs in -r mode
+ * src/ld/MachOReaderRelocatable.hpp: now ignore all labels and relocations in
+ __TEXT/__eh_frame section and rely on getCFIs() from libunwind
+ * unit-tests/test-cases/eh-coalescing-no-labels: add test case
+
+
+2008-11-19 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6378110> LTO doesn't like dtrace symbols
+ * src/ld/LTOReader.hpp: ignore __dtrace_probe undefines in bitcode files
-2008-01-14 Nick Kledzik <kledzik@apple.com>
+2008-11-14 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/5655246> PPC Leopard (Xcode 3.0) linker ld gets "Bus error" sometimes
- * src/MachOReaderRelocatable.hpp: use same code as x86 to parse ppc and arm sect-diff relocs
- * src/MachOWriterExecutable.hpp: use same code as x86 to write ppc and arm sect-diff relocs
+ * src/abstraction/MachOFileAbstraction.hpp: fix to work with 10.5 headers
+
+----- Tagged ld64-91
-2008-01-11 Nick Kledzik <kledzik@apple.com>
+2008-11-07 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/5637618> PPC Leopard (Xcode 3.0) linker ld reports "unknown scattered relocation type 4"
- * src/MachOReaderRelocatable.hpp: add PPC_RELOC_HI16 to scattered reloc parsing
- * unit-tests/test-cases/relocs-asm/relocs-asm.s: added tests for scattered hi/lo instructions
+ Remove COMPACT_UNWIND_SUPPORT conditionalizing
-2008-01-11 Nick Kledzik <kledzik@apple.com>
+2008-11-06 Nick Kledzik <kledzik@apple.com>
- * doc/man/man1/ld.1: add doc for -no_implicit_dylibs, -read_only_stubs, -slow_stubs, -interposable_list
+ Reorganize source layout. ld sources are now in "ld",
+ and other tools are in "other".
+
+
+2008-11-05 Nick Kledzik <kledzik@apple.com>
+
+ * ld64.xcodeproj/project.pbxproj: start installing unwinddump tool
+ * src/UnwindDump.cpp: support -arch option
+ * doc/man/man1/unwinddump.1: create man page
+
+
+2008-11-05 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6337329> linker should put cpusubtype in n_sect field of nlist entry for N_OSO debug note entries
+ * src/ld.cpp: in synthesizeDebugNotes() set other field of OSO to be subtype
+
+
+2008-11-05 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/3738966> Need a linker option to load all objects from one library
+ * src/Options.cpp: support -force_load option
+ * src/ArchiveReader.hpp: Add fForceLoad ivar
+ * doc/man/man1/ld.1: update man page with -force_load option
+ * unit-tests/test-cases/archive-force-load: add test case
+
+
+2008-11-05 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6308882> Dtrace Probe Warnings: SnowLeopard kernel should compile warning free
+ * src/ld.cpp: don't generate GSYM stabs for old style __dtrace_probe
+ * src/MachOReaderRelocatable.hpp: fix test for deciding if a symbol is an alias
+
+2008-11-04 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6294378> ADOBE: XCODE: ld: duplicate typeinfo in executable
+ * src/ld.cpp: in dead-strip mode, record overriden symbols and later rebind all uses
+ * unit-tests/test-cases/dead_strip-archive-weak: add test case
-2008-01-11 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/4800212> ld64(1) man page uses ambiguous term "suffix"
- * doc/man/man1/ld.1: make meaning of "suffix" more explicit
+2008-11-03 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6254202> support increased branch range in Thumb-2
+ * src/MachOReaderRelocatable.hpp: handle full branch range in addRelocReference()
+ * unit-tests/test-cases/branch-distance: added test case
+2008-10-31 Devang Patel <dpatel@apple.com>
-2008-01-11 Nick Kledzik <kledzik@apple.com>
+ <rdar://problem/5725712> Sqlite 3.5.4 built with lvm-gcc-4.2 -O4 fails regression test
+ * src/LTOReader.hpp: Use real atom scope when real atom is available.
+ Preserve globals while optimizing an executable.
- <rdar://problem/5633081> Obj-C Symbols in Leopard Can't Be Weak Linked
- * src/MachOWriterExecutable.hpp: set weak and lazy attributes on dummy .objc_class_name undefines
- to dylibs to support Mac OS X 10.3.x dyld
-
+2008-10-30 Nick Kledzik <kledzik@apple.com>
-2008-01-11 Nick Kledzik <kledzik@apple.com>
+ * src/MachOReaderRelocatable.hpp: support all encodings in getEncodedP()
- <rdar://problem/5669751> Unknown error with linker (dyld: unknown external relocation type)
- * src/ld.cpp: fix crash when SO stabs are not balanced
+
+----- Tagged ld64-90
+
+2008-10-30 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6327584> icc has dwarf unwind info that is different than gcc
+ * src/MachOReaderRelocatable.hpp: support more encodings in getEncodedP()
+
+
+2008-10-23 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6088653> build ld64 for x86_64
+ * ld64.xcodeproj/project.pbxproj: add X86_64 to valid archs
-2008-01-11 Devang Patel <dpatel@apple.com>
+2008-10-23 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/5667433> LTO does not work if expected output is a dynamic library
- * src/LLVMReader.hpp: Supply arguments describing output kind to optimizer. Communicate
- visibility info.
+ * ld64.xcodeproj/project.pbxproj: use generated @$(DERIVED_FILE_DIR)/linker_opts for extra
+ linker options. This allows linker to be built if LTO headers and libs are missing.
-2000-01-10 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/5680988> __cls_refs section is losing S_LITERAL_POINTERS section type
- * src/MachOWriterExecutable.hpp: special case __cls_refs section
- * unit-tests/test-cases/objc-literal-pointers: add test case
+2008-10-23 Nick Kledzik <kledzik@apple.com>
+ <rdar://problem/6273617> Linker warning not shown in the Xcode build log
+ * src/Options.cpp: add colon to format string in warning()
-2008-01-03 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/5667688> wrong EH information might be used
- Created new kGroupSubordinate reference kind to model group comdat. The "signature" atom
- has kGroupSubordinate references to the other atoms in the group. If the signature atom
- is coalesced away, the linker follows kGroupSubordinate references and throws away the
- other members of the group.
- * unit-tests/test-cases/eh-coalescing: added test case
- * src/ld.cpp: added markDead() and use propagate to subordinates
- * src/Architectures.hpp: added kGroupSubordinate
- * src/MachOReaderRelocatable.hpp: add kGroupSubordinate reference from a function to its .eh atom
- and if used, from .eh atom to its LSDA atom.
- * src/MachOWriterExecutable.hpp: handle kGroupSubordinate like kNoFixUp
+----- Tagged ld64-89.3
------ Tagged ld64-81.4.1
+2008-10-24 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6317985> ld64-89 broke TOT OpenGL libProgrammability x86_64 build
+ * src/MachOReaderRelocatable.hpp: add cast in getEncodedP()
+
-2007-12-19 Devang Patel <dpatel@apple.com>
+----- Tagged ld64-89.2
- * src/LLVMReader.hpp: Add LLVM_LTO_VERSION #ifdef check.
+2008-10-23 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6312895> SnowLeopard: Libsystem built with ld64-89.1 causes crashes
+ * src/MachOReaderRelocatable.hpp: when FDE information causes __text atom to be split, make the
+ atoms follow-on pairs.
+
+
+----- Tagged ld64-89.1
+
+2008-10-22 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: for x86_64 __eh_frame force direct references
-2007-12-19 Devang Patel <dpatel@apple.com>
- * src/LLVMReader.hpp: Add fOptimizer NULL check before calling printVersion().
+2008-10-21 Nick Kledzik <kledzik@apple.com>
+
+ * src/ObjectDump.cpp: Use getContentType() to see if content type is a cstring
-2007-12-19 Devang Patel <dpatel@apple.com>
- <rdar://problem/5655956> print LLVM LTO version number in verbose mode
- * src/LLVMReader.hpp: Add printLLVMVersion() to print llvm version string in verbose mode.
- * src/Options.cpp: Use printLLVMVersion() in verbose mode.
+----- Tagged ld64-89
-2007-12-19 Devang Patel <dpatel@apple.com>
+2008-10-21 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/5655956> print LLVM LTO version number in verbose mode
- * src/Options.h: Add verbose() method to check fVerbose flag.
- * src/LLVMReader.hpp: Print LLVM version string in verbose mode.
+ <rdar://problem/6253171> 10A180 with QT-1119 roots: iTunes and QuickTime cannot play back purchased videos
+ <rdar://problem/5950453> linker should not need .eh labels
+ * src/MachOWriterExecutable.hpp: use kCFIType to set section attributes
+ * src/MachOReaderRelocatable.hpp: use libunwind's CFITuple to parse __eh_frame content
+ * src/ld.cpp: Add adjustScope() phase instead of demoting scope within symboltable.add()
+ * unit-tests/test-cases/eh-stripped-symbols: added test case
+
+
+----- Tagged ld64-88.1
+
+2008-10-16 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: Fix uses of COMPACT_UNWIND_SUPPORT
+ * src/MachOWriterExecutable.hpp: Fix uses of COMPACT_UNWIND_SUPPORT
+
+
+2008-09-30 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6255983> OBJC2: Reorder __DATA,__objc_* sections by writedness
+ * src/ld.cpp: change sorting order of Sections
+
+
+2008-09-29 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6159479> Executable produced by XCode 3.2 on 10.6 crashes on 10.3.9
+ * src/MachOWriterExecutable.hpp: set objc_module_info_addr field of module table
------ Tagged ld64-81.4
-2007-12-18 Devang Patel <dpatel@apple.com>
+----- Tagged ld64-88
- * src/LLVMReader.hpp: Invalidate input architecture when optimizer is not available.
+2008-09-25 Nick Kledzik <kledzik@apple.com>
------ Tagged ld64-81.3
+ <rdar://problem/5935708> kexts need to be built as MH_BUNDLE mach-o files
+ * src/ld.cpp: use getUndefinedProxyAtom() with kKextBundle
+ * src/MachOFileAbstraction.hpp: add MH_KEXT_BUNDLE
+ * src/Options.cpp: support -kext for all architectures
+ * src/MachOWriterExecutable.hpp: support kKextBundle to make a bundle like kext
+ * unit-tests/test-cases/kext-basic: added test case
+
-2007-12-17 Nick Kledzik <kledzik@apple.com>
+2008-09-25 Nick Kledzik <kledzik@apple.com>
- * ld64.xcodeproj/project.pbxproj: remove extraneous header search paths
+ <rdar://problem/6052546> ld invoking wrong ld_classic
+ * src/Options.cpp: first look for ld_classic relative to ld itself
-2007-12-17 Devang Patel <dpatel@apple.com>
+2008-09-25 Nick Kledzik <kledzik@apple.com>
- * src/LLVMReader.hpp: Do not throw exception if LLVMReader is not able to
- dlopen LTO library. Instead just flag input file as an invalid LLVM bitcode file.
+ <rdar://problem/5855588> ld fails to link references from 32 bit code into 64 bit code
+ <rdar://problem/5955200> Desired 32-bit absolute relocation
+ * src/Architectures.hpp: add x86_64::kPointer32
+ * src/MachOReaderRelocatable.hpp: support X86_64_RELOC_UNSIGNED with length=2
+ * src/MachOWriterExecutable.hpp: support x86_64::kPointer32
+ * unit-tests/test-cases/relocs-asm/relocs-asm.s: added 32-bit pointer tests
+
+2008-09-25 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6075323> Should be able to mark dylibs as auto-dead-dylib-strip
+ * src/Options.h: add fMarkDeadStrippableDylib
+ * src/MachOReaderDylib.hpp: check MH_DEAD_STRIPPABLE_DYLIB
+ * src/ObjectFile.h: add deadStrippable()
+ * src/MachOFileAbstraction.hpp: add MH_DEAD_STRIPPABLE_DYLIB
+ * src/Options.cpp: support -mark_dead_strippable_dylib
+ * src/MachOWriterExecutable.hpp: test reader->deadStrippable(), set MH_DEAD_STRIPPABLE_DYLIB
+ * doc/man/man1/ld.1: update man page
+ * unit-tests/test-cases/dead_strippable_dylib: added test case
+
-2007-12-14 Nick Kledzik <kledzik@apple.com>
+2008-09-25 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/5645908> gcc DejaGnu failure: gcc.dg/20020312-2.c (test for excess errors) (-fstack-protector-all)
- * src/MachOWriterExecutable.hpp: fix Writer<x86>::generatesExternalTextReloc() to allow text relocs
- * unit-tests/test-cases/read-only-relocs: updated test case to link a dynamic main executable compiled with -static
+ <rdar://problem/6197601> ER: Add -seg_page_size option
+ * src/Options.cpp: add -seg_page_size option
+ * src/MachOWriterExecutable.hpp: use new page size info when laying out segments
+ * doc/man/man1/ld.1: update man page
+
+2008-09-24 Nick Kledzik <kledzik@apple.com>
-2007-12-14 Devang Patel <dpatel@apple.com>
+ <rdar://problem/5798786> -arch_errors_fatal not working
+ * src/ld.cpp: check fOptions.errorOnOtherArchFiles()
+ * src/Options.cpp: turn -arch_errors_fatal into fOptions.errorOnOtherArchFiles()
- <rdar://problem/5648438> Enable Link Time Optimization in Opal
- * src/LLVMReader.hpp: Locate LLVMlto.dylib relative to ld location in Developer folder.
- * ld64.xcodeproj/project.pbxproj: Add {DEVELOPER_DIR}/usr/include in header search path.
- * unit-tests/run-all-unit-tests: Set DYLD_FALLBACK_LIBRARY_PATH to find LLVMlto.dylib during unit testing.
- * unit-tests/testcases/llvm-integration/Makefile: Point LLVMGCC and LLVMGXX to llvm-gcc-4.2 in Developer folder during unit testing.
-
-
-2007-12-13 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/5645446> SWB: failures due to ld: pointer in read-only segment not allowed in slidable image, used in ...
- * src/MachOReaderRelocatable.hpp: in Reader<x86>::addRelocReference() handle weak pc-rel 32-bit vanilla relocs properly
+2008-09-24 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6161215> CrashTracer: [USER] 1 crash in ld at ld: 0x5ce02
+ * src/ld.cpp: abort if resolve() finds an unresolved reference, rather than allow a future crash
------ Tagged ld64-81.2
+2008-09-24 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6157989> linker crashes linking X86-64 with -fwritable-strings
+ * src/MachOReaderRelocatable.hpp: handle unbound cfstring references
+ * unit-tests/test-cases/cfstring-coalesce: update test case
+
+
+2008-09-24 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6213035> ld64: bl out of range (-17147704 max is +/-16M) on ppc
+ * src/MachOWriterExecutable.hpp: tweak branch island regions to be every 14MB instead of 15MB
+
+
+2008-09-24 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5907981> -filelist fails with comma in path
+ * src/Options.cpp: in loadFileList() first try without special comma meaning
+ * unit-tests/test-cases/filelist/Makefile: update test case
+
+
+2008-09-23 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6203068> nop not used when aligning functions in -r mode
+ * src/MachOWriterExecutable.hpp: change check for when to pad with nops to not test segment's name
+
+
+2008-09-23 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6238329> "-pie can only be used when linking a main executable" should be a warning, not an error
+ * src/Options.cpp: make -pie on a dylib or bundle be a warning instead of an error
+
+
+2008-09-23 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: add warning if dwarf cannot be encoded as compact unwind
+
+
+2008-09-18 Nick Kledzik <kledzik@apple.com>
+
+ * src/LTOReader.hpp: re-enable use of lto_codegen_debug_options()
+
+
+2008-09-16 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6219054> ld does not always set S_CSTRING_LITERALS on __TEXT,__cstring
+ * src/MachOReaderRelocatable.hpp: add getContentType() to SymbolAtom
+ * src/MachOWriterExecutable.hpp: for x86_64 don't override named cstrings with LC* name
+
+
+2008-09-10 Nick Kledzik <kledzik@apple.com>
+
+ * Options.cpp: add __crashreporter_info__ to communicate command line to crash reporter
+ * ld64.xcodeproj/project.pbxproj: leave local symbols in ld to provide better crash reports
+
+
+2008-09-08 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6126637> 161569 GCC 4.2 - breakpoints no longer work for a large number of functions
+ * src/MachOReaderRelocatable.hpp: support DW_FORM_strp out-of-line strings when parsing line table
+
+
+2008-09-02 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: fix compact unwind personality for dyld and -slow_stubs
+
+
+2008-08-29 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6186838> -weak_library no longer forces uses to be weak_import
+ * src/MachOWriterExecutable.hpp: use fWeakImport on dylib to force proxy atoms into fWeakImportMap
+ * unit-tests/test-cases/weak_import-force: added test case
+
+
+2008-08-29 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6061558> linker should order __DATA segment to reduce dyld dirtied pages
+ * src/Options.cpp: add fOrderData and support -no_data_order
+ * src/ld.cpp: modify tweakLayout() to sort atoms with relocations to start of __data section
+
+
+2008-08-27 Nick Kledzik <kledzik@apple.com>
+
+ * src/Options.cpp: back out <rdar://problem/6174493>
+
+
+----- Tagged ld64-87.5
+
+2008-08-26 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6174493> some projects show _Unwind_Resume coming from libSystem.B.dylib
+ * src/Options.cpp: swap any early symlinks to libSystem with libgcc_s
+
+
+----- Tagged ld64-87.4
+
+2008-08-25 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6174493> some projects show _Unwind_Resume coming from libSystem.B.dylib
+ * src/Options.cpp: swap any early libSystem with libgcc_s
+
+
+2008-08-15 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5898326> Unable to build ppc debug builds (linker out of range error)
+ * src/MachOWriterExecutable.hpp: in addPPCBranchIslands() look ahead so large atoms don't push out branch islands
+
+
+----- Tagged ld64-87.3.1
+
+2008-09-08 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6204202> i386 dylibs have incorrect personality pointers when put in dyld shared cache
+ * src/MachOWriterExecutable.hpp: in addCrossSegmentRef() handle kImageOffset32 to __IMPORT segment
+
+
+----- Tagged ld64-87.3
+
+2008-08-09 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6125381> work around compiler gcc_except_table alignment
+ * src/ObjectFile.h: change getLSDA() to return a reference instead of an atom
+ * src/MachOReaderRelocatable.hpp: special case __eh_frame 64-bit pointer diff relocations
+ * src/MachOWriterExecutable.hpp: track lsda offset when creating __unwind_info section
+ * src/UnwindDump.cpp: log when LDSA content does not start with 0xFF
+
+----- Tagged ld64-87.2
+
+2008-08-07 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6131559> 10A141: libuwind falls back to dwarf and makes whole system super slow
+ * src/MachOWriterExecutable.hpp: Fix sign extension bug with x86_64::kPointerDiff24
+ * src/UnwindDump.cpp: warn about mangled LSDA entries when dumping unwind section
+
+
+----- Tagged ld64-87.1
+
+2008-08-03 Nick Kledzik <kledzik@apple.com>
+
+ * src/LTOReader.hpp: Don't use lto_codegen_debug_options until newer libLTO.dylib is available
+
+
+----- Tagged ld64-87
+
+2008-07-21 Nick Kledzik <kledzik@apple.com>
+
+ * src/Options.cpp: Always set fAutoOrderInitializers=false for dyld
+
+
+2008-07-21 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOWriterExecutable.hpp: fix when regular vs compressed __unwind_info pages are generated
+ * src/UnwindDump.cpp: fix function name decoding in regular pages
+
+
+2008-07-21 Nick Kledzik <kledzik@apple.com>
+
+ * ld64.xcodeproj/project.pbxproj: don't allow ld to build for x86_64 until libdtrace.dylib is available
+
+
+2008-07-18 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: don't crash if debug_line section has no line table
+
+
+2008-07-18 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5628149> Duplicate probe firings in Security.framework
+ * src/LTOReader.hpp: optimize() now returns atoms optimized away
+ * src/ObjectFile.h: optimize() should return if it did anything
+ * src/ArchiveReader.hpp: pass through optimize() result
+ * src/ld.cpp: rework dtrace probe processing as a new pass to prevent double counting
+
+
+2008-07-15 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6061904> automatically order initializers to start of __TEXT
+ * src/Options.cpp: add -no_order_inits option
+ * src/MachOReaderRelocatable.hpp: merge __StaticInit into __text
+ * src/ObjectFile.h: add fAutoOrderInitializers
+ * src/ld.cpp: sort initializer to start of __text and terminators to end
+ * doc/man/man1/ld.1: add doc about -no_order_inits
+ * unit-tests/test-cases/init-order: add test case
+
+2008-07-15 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6073697> Only add LC_SEGMENT_SPLIT_INFO to dylibs that might be in the shared cache
+ * src/MachOWriterExecutable.hpp: re-layout load commands after split-seg data computed
+ * src/Options.cpp: non-public install name will disable split-seg load command
+
+
+2008-07-14 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6069861> ld -r for x86_64 is changing visibility of cstring constants
+ * src/MachOWriterExecutable.hpp: force x86_64 cstring labels to be local in -r mode
+ * unit-tests/test-cases/cstring-label: added test case
+
+
+2008-07-11 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6070190> ld not adding updating LC_SEGMENT_SPLIT_INFO with __unwind_info section
+ * src/MachOWriterExecutable.hpp: run createSplitSegContent() after __unwind_info section is created
+
+2008-07-10 Nick Kledzik <kledzik@apple.com>
+
+ * src/LTOReader.hpp: improve missing symbol error message
+
+
+2008-07-09 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6061558> linker should order __DATA segment to reduce dyld dirtied pages
+ * src/ld.cpp: first phase, order sections
+
+
+2008-07-08 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: remove "coal" sections when creating a final linked image
+
+
+2008-07-08 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6054476> ld: add support for mllvm LTO options
+ * src/Options.cpp: support -mllvm option
+ * src/LTOReader.hpp: call lto_codegen_debug_options() with -mllvm options
+ * src/ld.cpp: pass llvmOptions to optimize()
+ * src/Options.h: add fLLVMOptions
+ * src/ArchiveReader.hpp: add llvmOptions parameter to optimize()
+ * src/ObjectFile.h: add llvmOptions parameter to optimize()
+ * unit-tests/test-cases/lto-llvm-options: add test case
+
+
+2008-07-07 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6054296> Linker fails with: 24-bit pointer diff out of range in unwind info in unwind info from...
+ * src/MachOWriterExecutable.hpp: fix when to fallback to uncompressed unwind info
+
+
+2008-07-03 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6001472> ld crash with gcc-4.0 code that uses a zero sized array
+ * src/MachOReaderRelocatable.hpp: handle zero size atom in a zero sized section
+
+
+2008-07-03 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6016809> ld crashes when bad ppc relocs are found
+ * src/MachOReaderRelocatable.hpp: change all missing PAIR warnings to errors
+
+
+2008-07-02 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5881324> when linking a kext the static linker should leave a pad in the headers to allow code signing
+ * src/MachOWriterExecutable.hpp: add padding for load commands in object files
+ * unit-tests/test-cases/code-signed-object-file: added test case
+
+
+2008-07-02 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6048484> LC_SEGMENT_64 filesize incorrect for MH_OBJECT filetype
+ * src/MachOWriterExecutable.hpp: correctly set segment size info in object files
+ * unit-tests/test-cases/no-object-symbols: add test case
+
+
+2008-06-26 Nick Kledzik <kledzik@apple.com>
+
+ * ld64.xcodeproj/project.pbxproj: enable ld and rebase targets to build for x86_64
+ * src/rebase.cpp: remove unused fRelocBase field that was not 64-bit clean
+ * src/MachOReaderRelocatable.hpp: fix getEncodedP() to be 64-bit clean
+
+
+----- Tagged ld64-86.3
+
+2008-06-17 Nick Kledzik <kledzik@apple.com>
+
+ * src/ld.cpp: fix loadUndefines() to double check undefine symbol was not already loaded
+
+
+----- Tagged ld64-86.2
+
+2008-06-14 Nick Kledzik <kledzik@apple.com>
+
+ * srd/ld.cpp: Add NULL check in getTentativesNames()
+
+
+----- Tagged ld64-86.1
+
+2008-06-06 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOWriterExecutable.hpp: fix header padding calculation for dyld
+
+
+----- Tagged ld64-86
+
+2008-06-04 Nick Kledzik <kledzik@apple.com>
+
+ * src/LTOReader.hpp: if lto_codegen_add_module() fails, add explanation to error message
+
+
+2008-06-04 Nick Kledzik <kledzik@apple.com>
+
+ * src/ObjectFile.h: add deadAtoms parameter to optimize()
+ * src/ld.cpp: ditto
+ * src/ArchiveReader.hpp: ditto
+ * src/MachOReaderRelocatable.hpp: handle llvm use of 0x1B pointer encodings in CIEs
+ * src/LTOReader.hpp: make sure libLTO.dylib knows about any llvm symbol coalesced away
+ * unit-tests/test-cases/lto-weak-native-override: add test case
+
+
+2008-06-04 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5935600> LTO : 176.gcc and 177.mesa build failure at -O4
+ * src/LTOReader.hpp: make sure internal is returned by getAtoms()
+ * unit-tests/test-cases/lto-archive-dylib: update test case
+
+
+2008-06-03 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5894163> fix for 5613343 need to search for definitions for common symbols is broken
+ * src/ld.cpp: modify loadUndefines() to check for undefines in all files and tentative definitions but only in archives
+ * src/machochecker.cpp: check for undefine symbols and external symbols with same name
+ * unit-tests/test-cases/tentative-and-archive: update test case
+
+
+2008-06-03 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5935237> linker produces wrong result for 16-bit call relocations
+ * src/MachOReaderRelocatable.hpp: properly parse i386 scattered relocs for word sized pc-rel vanilla
+ * src/MachOWriterExecutable.hpp: propery compute displacement for x86::kPCRel16
+ * unit-tests/test-cases/relocs-asm: update test case with callw instructions
+
+
+2008-06-03 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5905900> Building kext x86_64 with unexported symbols file causes linking problems
+ * src/MachOWriterExecutable.hpp: better check when creating undefined proxy atoms
+ * unit-tests/test-cases/unexported_symbols_list-r: added test case
+
+
+2008-06-02 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5659338> S_CSTRING_LITERALS section type not preserved in executable
+ * src/ObjectFile.h: added ContentType
+ * src/MachOReaderRelocatable.hpp: set ContentType for anonymous string literals
+ * src/MachOWriterExecutable.hpp: set S_CSTRING_LITERALS if ContentType is kCStringType
+ * unit-tests/test-cases/cstring-custom-section: added test case
+
+
+2008-06-02 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5980194> linker should produce __unwind_info section in final linked images
+ * src/ld.cpp: sort __unwind_info then __eh_frame section to end of __TEXT
+ * src/Architectures.hpp: add kImageOffset32 and kPointerDiff24
+ * src/ObjectFile.h: add compact unwind info support
+ * src/MachOReaderRelocatable.hpp: add compact unwind info support
+ * src/MachOFileAbstraction.hpp: add C++ wrappers for unwind section layout
+ * src/UnwindDump.cpp: new tool for dumping __unwind_info section
+ * src/MachOWriterExecutable.hpp: create __unwind_info section when needed
+ * src/ObjectDump.cpp: print unwind info
+
+
+2008-06-02 Nick Kledzik <kledzik@apple.com>
+
+ * unit-tests/test-cases/llvm-integration: split out some test cases
+ * unit-tests/test-cases/lto-preload-pie: added
+ * unit-tests/test-cases/lto-archive-dylib: added
+
+
+2008-05-30 Nick Kledzik <kledzik@apple.com>
+
+ * unit-tests: fixes to build all tests with with gcc-4.2 on SnowLeopard
+
+
+2008-05-30 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5536348> support -preload option to generate MH_PRELOAD binaries compatible with mtoc(1) and EFI
+ * src/ld.cpp: add entryPoint parameter to optimize()
+ * src/ArchiveReader.hpp: ditto
+ * src/ObjectFile.h: ditto
+ * src/LTOReader.hpp: use entryPoint parameter to optimize()
+ * src/Options.h: add kPreload and segment alignment
+ * src/Options.cpp: support -preload and -segalign
+ * src/MachOWriterExecutable.hpp: support kPreload and non-page aligned segments
+
+
+2008-05-30 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4431008> ld should warn if passed -r and also dylibs
+ * src/ld.cpp: check for spurious dylibs in Linker::addDylib()
+
+
+----- Tagged ld64-85.6
+
+2008-11-01 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6254202> support increased branch range in Thumb-2
+ * src/MachOWriterExecutable.hpp: in fixUpReferenceFinal() support new longer branch range
+
+
+2008-11-01 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6104368> ld warning: unknown option to -iphoneos_version_min, not 1.x or 2.x
+ * src/Options.cpp: In setIPhoneVersionMin() support 3.x
+
+
+----- Tagged ld64-85.5
+
+2008-09-17 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6162415> vtable pointers can be missing thumb bit
+ * src/MachOWriterExecutable.hpp: Writer<arm>::fixUpReferenceFinal() OR in the 1 bit if the target
+ of a arm::kReadOnlyPointer is thumb.
+
+
+----- Tagged ld64-85.4
+
+2008-08-11 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6138961> ld should ignore LD_PREBIND when processing a static archive
+ * src/MachOWriterExecutable.hpp: in setImportNlist() never use N_PBUD for object files
+
+----- Tagged ld64-85.3
+
+2008-07-14 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6060912> Prebinding busted in DTSB
+ * src/Options.cpp: check for libstdc++.6.0.[49] in seg_addr_table
+
+
+----- Tagged ld64-85.2
+
+2008-05-06 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5905889> ARM ld should take W bit off of maxprot for __TEXT segment
+ * src/MachOWriterExecutable.hpp: for iPhone always set maxprot to be initprot in all segments
+
+
+2008-05-06 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5914343> encryptable images may not be signable
+ * src/MachOWriterExecutable.hpp: use minimum header padding when aligning __text section
+
+
+----- Tagged ld64-85 (Xcode 3.1)
+
+2008-04-29 Nick Kledzik <kledzik@apple.com>
+
+ * ld64.xcodeproj/project.pbxproj: <llvm-c/lto.h> is moving from /usr/local/include to /Developer/usr/local/include
+
+
+2008-04-29 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5829579> ld doesn't honor "rightmost" -syslibroot argument
+ * src/Options.cpp: if last -syslibroot is /, then ignore all syslibroots
+
+
+2008-04-29 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5866582> GLRendererFloat has bad __eh_frame section caused by mixing llvm-gcc and gcc object files
+ * src/MachOReaderRelocatable.hpp: make all atoms in __eh_frame section have 1-byte alignment
+ * src/MachOWriterExecutable.hpp: make __eh_frame section have pointer sized alignment
+
+
+2008-04-17 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: better cpu subtype support
+
+
+2008-04-14 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5733759> ld64 has bad ARM branch island check
+ * src/MachOWriterExecutable.hpp: in addBranchIslands() don't force large arm programs to fail
+
+
+2008-04-10 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOWriterExecutable.hpp: fix stubs used with lazy dylibs
+
+
+----- Tagged ld64-84.4
+
+2008-04-10 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5847206> SPEC2000/eon built with -mdynamic-no-pic won't run
+ * src/Architectures.hpp: added arm::kReadOnlyPointer
+ * src/MachOReaderRelocatable.hpp: generate arm::kReadOnlyPointer
+ * src/MachOWriterExecutable.hpp: use arm::kReadOnlyPointer
+ * src/machochecker.cpp: allow MH_PIE bit
+ * unit-tests/test-cases/switch-jump-table: added test cases
+
+
+----- Tagged ld64-84.3
+
+2008-04-09 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5852023> -undefined dynamic_lookup busted
+ * src/ld.cpp: don't create proxy atom when scanning for dylib duplicates
+ * unit-tests/test-cases/tentative-and-archive: use -undefined dynamic_lookup
+
+
+----- Tagged ld64-84.2
+
+2008-04-04 Nick Kledzik <kledzik@apple.com>
+
+ * src/ld.cpp: don't add .eh symbols to symbol table in -r mode
+ * unit-tests/test-cases/eh-coalescing-r: update to test out of order coalescing
+
+
+----- Tagged ld64-84.1
+
+2008-03-28 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5720961> ld should prefer architecture-specific variant over generic in fat object file
+ * src/Options.cpp: fully process -arch arguments into fArchitecture and fSubArchitecture
+ * src/ld.cpp: when -arch with a subtype is used, try to find the exact subtype from fat files
+ * unit-tests/test-cases/cpu-sub-types-preference: added test cases for arm and ppc
+
+
+----- Tagged ld64-84
+
+2008-03-28 Nick Kledzik <kledzik@apple.com>
+
+ * src/LTOReader.hpp: don't print lto version, if lto is unavailable
+
+
+2008-03-26 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5575399> Add LD_WARN_COMMONS to BigBear builds
+ * src/Options.cpp: Add support for LD_WARN_FILE which copies all warnings to a side file
+
+
+2008-03-26 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5797713> Need encryption tag in mach-o file
+ <rdar://problem/5811920> linker should adjust arm final linked images so __text is never on the same page as the load commands
+ * src/MachOFileAbstraction.hpp: add support for encryption_info_command
+ * src/Options.cpp: add support for LD_NO_ENCRYPT and -no_encryption
+ * src/MachOWriterExecutable.hpp: add EncryptionLoadCommandsAtom
+ * src/machochecker.cpp: validate LC_ENCRYPTION_INFO
+
+
+2008-03-25 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5712533> ld64 does not recognize LLVM bitcode archive files
+ * src/MachOReaderArchive.hpp: renamed to src/ArchiveReader.hpp
+ * src/ArchiveReader.hpp: sniff each member and instantiate correct reader
+ * src/ld.cpp: rename mach_o::archive::Reader to archive::Reader
+ * ld64.xcodeproj/project.pbxproj: rename MachOReaderArchive.hpp to ArchiveReader.hpp
+ * unit-tests/test-cases/llvm-integration: added test case
+
+
+2008-03-25 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5771658> ld64 should switch to new libLTO.dylib interface
+ <rdar://problem/5675690> Produce llvm bc file in 'ld -r' mode if all .o files are llvm bc
+ * src/LTOReader.hpp: rewrite from LLVMReader.hpp to use new lto_* C interface
+ * unit-tests/test-cases/llvm-integration: update and comment
+ * ld64.xcodeproj/project.pbxproj: update to lazy load libLTO.dylib
+ * src/ld.cpp: rework and simplify Linker::optimize()
+ * src/ObjectDump.cpp: Add -nm option
+
+
+2008-03-25 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.cpp: Fix some .objc_class_name_ off by one problem
+ * src/MachOWriterExecutable.cpp: Fix some .objc_class_name_ off by one problem
+
+
+2008-03-24 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5814613> Xcode 3.1 breaks linkage of libgcj.9.dylib from gcc 4.3.0
+ * src/MachOWriterExecutable.cpp: Make sure all ivars in Writer are initialized.
+
+
+2008-03-21 Nick Kledzik <kledzik@apple.com>
+
+ * src/Options.cpp: warn if -seg1addr value is not page aligned
+
+
+2008-03-21 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5806437> Move ARM support outside of __OPEN_SOURCE__
+ * src/ld.cpp: remove __OPEN_SOURCE__ around arm support
+ * src/LLVMReader.hpp: remove __OPEN_SOURCE__ around arm support
+ * src/MachOReaderDylib.hpp: remove __OPEN_SOURCE__ around arm support
+ * src/ObjectFile.h: remove __OPEN_SOURCE__ around arm support
+ * src/MachOReaderRelocatable.hpp: remove __OPEN_SOURCE__ around arm support
+ * src/OpaqueSection.hpp: Cover arm support inside __OPEN_SOURCE__ macro check
+ * src/MachOWriterExecutable.hpp: remove __OPEN_SOURCE__ around arm support
+ * src/ObjectDump.cpp: remove __OPEN_SOURCE__ around arm support
+ * ld64.xcodeproj/project.pbxproj: remove ARM_SUPPORT from config.h
+
+
+----- Tagged ld64-83.2
+
+2008-03-15 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5801620> ld64-83 removes OBJC_CLASS_$ symbols from projects, causes catastrophic results
+ * src/Options.cpp: restore "case CPU_TYPE_ARM" in switch statement for .objc_class symbols in .exp files
+ * unit-tests/test-cases/objc-exported_symbols_list: added test case
+
+
+----- Tagged ld64-83.1
+
+2008-03-14 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5800466> -iphone_version_min ==> -iphoneos_version_min
+ * src/Options.cpp: support -iphoneos_version_min as well
+
+
+----- Tagged ld64-83
+
+2008-03-10 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5791543> ld needs to strip iphone_version_min option if invoking ld_classic
+ * src/Options.cpp: suppress -iphone_version_min from being passed to ld_classic
+
+
+2008-03-04 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4171253> ADOBE XCODE: Linker option to lazy load frameworks (cause dyld is too slow)
+ * src/MachOWriterExecutable.hpp: create lazy stubs and LC_LAZY_LOAD_DYLIB for lazy load dylibs
+ * src/Options.cpp: support -lazy-l, -lazy_library, and -lazy_framework
+ * src/MachOFileAbstraction.hpp: add LC_LAZY_LOAD_DYLIB and S_LAZY_DYLIB_SYMBOL_POINTERS until in cctools
+ * src/MachOReaderDylib.hpp: add isLazyLoadedDylib()
+ * src/ld.cpp: pass lazy helper atom to writer
+ * doc/man/man1/ld.1: document new options
+ * unit-tests/test-cases/lazy-dylib-objc: add test case
+ * unit-tests/test-cases/lazy-dylib: add test case
+
+
+----- Tagged ld64-82.7
+
+2008-03-07 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5787149> duplicate symbol literal-pointer@__OBJC@__message_refs@...
+ * src/MachOReaderRelocatable.hpp: AnonymousAtom from S_LITERAL_POINTERS section should be weak
+ * unit-tests/test-cases/objc-selector-coalescing: added test case
+
+
+----- Tagged ld64-82.6
+
+2008-03-04 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5779681> ld crashes building XsanFS for Snow Leopard Builds
+ * src/ld.cpp: add bool dylibsOnly parameter to addJustInTimeAtoms()
+ * unit-tests/test-cases/tentative-and-archive: added test case
+
+2008-03-04 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5775822> ld64 should not force building with gcc 4.0
+ * ld64.xcodeproj/project.pbxproj: change rules to use "system" compiler instead of 4.0
+
+
+2008-02-29 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5774730> Simulator frameworks are being build split-seg and not prebound
+ * src/Options.cpp: only splitseg if prebound
+
+
+2008-02-29 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5774231> Linker should not make GSYM debug note for .objc_category_* symbols
+ * src/ld.cpp: suppress GSYM debug notes for absolute symbols
+ * unit-tests/test-cases/objc-category-debug-notes: added test case
+
+
+2008-02-29 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5768970> non-ASCII CFString support is broken
+ * src/MachOReaderRelocatable.hpp: only name and coalesce cfstring constants if they use a __cstring
+ * unit-tests/test-cases/cfstring-utf16: add test case
+
+
+2008-02-25 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5366363> ld -r -x
+ * doc/man/man1/ld.1: update man page to explain -r -x produces auto-stripped labels
+
+
+----- Tagged ld64-82.5
+
+2008-02-12 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5738023> x86_64: -stack_size failure when large __bss is used
+ * src/ld.cpp: only move section already in __DATA segment to new __huge section
+ * unit-tests/test-cases/stack_size_no_addr: updated test case to add large bss section
+
+
+----- Tagged ld64-82.4
+
+2008-02-06 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5726215> comdat warnings with ld -r of C++ .o files
+ * unit-tests/test-cases/eh-coalescing-r: added test case
+ * src/ld.cpp: in ld -r mode don't warn about if .eh symbols are not static
+
+
+2008-02-06 Devang Patel <dpatel@apple.com>
+
+ <rdar://problem/5724990> LTO of Bom framework with -dead_strip causes ld(1) crash
+ * src/LLVMReader.hpp: Check fAtom while determining LLVMReference target binding.
+ * unit-tests/test-cases/llvm-integration/Makefile: Add new test case.
+ * unit-tests/test-cases/llvm-integration/a15.c: New.
+ * unit-tests/test-cases/llvm-integration/b15.c: New.
+ * unit-tests/test-cases/llvm-integration/c15.c: New.
+
+2008-02-05 Nick Kledzik <kledzik@apple.com>
+
+ * src/ld.cpp: fix for -arch ppc -mdynamic-no-pic -pie so PPC_RELOC_HA16 reloc is used
+
+----- Tagged ld64-82.3
+
+2008-02-04 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5721186> ld doesn't seem to understand $ld$add$os... and $ld$hide$os... for 10.6 moves
+ * src/ObjectFile.h: add 10.6
+ * src/Options.cpp: add 10.6 support
+ * src/MachOReaderDylib.hpp: recognize $os10.6$
+
+
+----- Tagged ld64-82.2
+
+2008-01-30 Devang Patel <dpatel@apple.com>
+
+ <rdar://problem/5714833> Can't build 64-bit Intel binaries with LTO
+ <rdar://problem/5714787> ld64 fails to build with llvm-gcc-4.2
+ * src/LLVMReader.hpp: Fix character count typo in strncmp call.
+ Use const char * to initialize temp. string.
+ * ld64.xcodeproj/project.pbxproj: use $(DEVELOPER_DIR) in header search construction
+ instead of hard coding /Developer.
+
+----- Tagged ld64-82.1
+
+2008-01-23 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: don't bus error if S_LITERAL_POINTERS is missing relocs
+
+
+2008-01-22 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5695496> ld uses 32-bits in some places to hold addresses when parsing 64-bit mach-o files
+ * src/MachOReaderRelocatable.hpp: use AddrToAtomMap type that switch address to 64-bits for 64-bit archs
+ * src/MachOWriterExecutable.hpp: verify BR14 does not overflow for external relocs
+ * unit-tests/test-cases/relocs-c: update test case to slide addresses to verify x86_64 .o files
+
+
+----- Tagged ld64-82
+
+2008-01-18 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5694612> Bad grammar used in ld warning: cannot exported hidden symbol
+ * src/ld.cpp: fix typo in warning string
+
+
+2008-01-16 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5565074> Bundle Loader does not work anymore when loader is a bundle
+ <rdar://problem/5590203> ld warns of incorrect architecture when linking a bundle to a bundle
+ * src/MachOReaderDylib.hpp: support linking against bundles via -bundle_loader. Clean up error messages
+ * unit-tests/test-cases/bundle_loader: update test case
+
+
+2008-01-16 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5366363> ld -r -x creates debug notes (stabs) when it should not with -x (keep only global symbols)
+ * src/Options.cpp: in reconfigureDefaults() if -r and -x then -S
+
+
+2008-01-16 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5566068> if ld crashes while writing output file, it should delete the half written file
+ * src/MachOWriterExecutable.hpp: wrap open/write/close in try block and add signal handlers all to delete
+ output file on failure.
+
+
+2008-01-16 Devang Patel <dpatel@apple.com>
+
+ * src/LLVMReader.hpp: Use __gnu_cxx::hash_map instead of hash supported by LLVM.
+
+
+2008-01-16 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5593537> GC-supported library can't be linked into GC-required executable
+ * src/ld.cpp: loosen constraint that all objc code must be compiled with same GC settings and
+ allow gc-compatible code to be linked into anything.
+ * unit-tests/test-cases/objc-gc-checks: update test case
+
+
+2008-01-15 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5687976> no debug notes for custom named data
+ * src/ld.cpp: in synthesizeDebugNotes() check getSymbolTableInclusion() instead of for leading underscore
+ * unit-tests/test-cases/dwarf-debug-notes: update test case
+
+----- Tagged ld64-81.5
+
+2008-01-14 Devang Patel <dpatel@apple.com>
+
+ <rdar://problem/5683813> llvm-gcc-4.2 fails to build Sqlite 3.5.4 with -O4
+ * src/LLVMReader.hpp: Resolve proxy references. Collect new unbounded references
+ after optimization.
+ * src/ld.cpp: Resolve additional unbounded references after optimization.
+
+
+2008-01-14 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5655246> PPC Leopard (Xcode 3.0) linker ld gets "Bus error" sometimes
+ * src/MachOReaderRelocatable.hpp: use same code as x86 to parse ppc and arm sect-diff relocs
+ * src/MachOWriterExecutable.hpp: use same code as x86 to write ppc and arm sect-diff relocs
+
+
+2008-01-11 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5637618> PPC Leopard (Xcode 3.0) linker ld reports "unknown scattered relocation type 4"
+ * src/MachOReaderRelocatable.hpp: add PPC_RELOC_HI16 to scattered reloc parsing
+ * unit-tests/test-cases/relocs-asm/relocs-asm.s: added tests for scattered hi/lo instructions
+
+
+2008-01-11 Nick Kledzik <kledzik@apple.com>
+
+ * doc/man/man1/ld.1: add doc for -no_implicit_dylibs, -read_only_stubs, -slow_stubs, -interposable_list
+
+
+2008-01-11 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4800212> ld64(1) man page uses ambiguous term "suffix"
+ * doc/man/man1/ld.1: make meaning of "suffix" more explicit
+
+
+2008-01-11 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5633081> Obj-C Symbols in Leopard Can't Be Weak Linked
+ * src/MachOWriterExecutable.hpp: set weak and lazy attributes on dummy .objc_class_name undefines
+ to dylibs to support Mac OS X 10.3.x dyld
+
+
+2008-01-11 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5669751> Unknown error with linker (dyld: unknown external relocation type)
+ * src/ld.cpp: fix crash when SO stabs are not balanced
+
+
+2008-01-11 Devang Patel <dpatel@apple.com>
+
+ <rdar://problem/5667433> LTO does not work if expected output is a dynamic library
+ * src/LLVMReader.hpp: Supply arguments describing output kind to optimizer. Communicate
+ visibility info.
+
+2000-01-10 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5680988> __cls_refs section is losing S_LITERAL_POINTERS section type
+ * src/MachOWriterExecutable.hpp: special case __cls_refs section
+ * unit-tests/test-cases/objc-literal-pointers: add test case
+
+
+2008-01-03 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5667688> wrong EH information might be used
+ Created new kGroupSubordinate reference kind to model group comdat. The "signature" atom
+ has kGroupSubordinate references to the other atoms in the group. If the signature atom
+ is coalesced away, the linker follows kGroupSubordinate references and throws away the
+ other members of the group.
+ * unit-tests/test-cases/eh-coalescing: added test case
+ * src/ld.cpp: added markDead() and use propagate to subordinates
+ * src/Architectures.hpp: added kGroupSubordinate
+ * src/MachOReaderRelocatable.hpp: add kGroupSubordinate reference from a function to its .eh atom
+ and if used, from .eh atom to its LSDA atom.
+ * src/MachOWriterExecutable.hpp: handle kGroupSubordinate like kNoFixUp
+
+----- Tagged ld64-81.4.1
+
+2007-12-19 Devang Patel <dpatel@apple.com>
+
+ * src/LLVMReader.hpp: Add LLVM_LTO_VERSION #ifdef check.
+
+2007-12-19 Devang Patel <dpatel@apple.com>
+
+ * src/LLVMReader.hpp: Add fOptimizer NULL check before calling printVersion().
+
+2007-12-19 Devang Patel <dpatel@apple.com>
+
+ <rdar://problem/5655956> print LLVM LTO version number in verbose mode
+ * src/LLVMReader.hpp: Add printLLVMVersion() to print llvm version string in verbose mode.
+ * src/Options.cpp: Use printLLVMVersion() in verbose mode.
+
+2007-12-19 Devang Patel <dpatel@apple.com>
+
+ <rdar://problem/5655956> print LLVM LTO version number in verbose mode
+ * src/Options.h: Add verbose() method to check fVerbose flag.
+ * src/LLVMReader.hpp: Print LLVM version string in verbose mode.
+
+----- Tagged ld64-81.4
+
+2007-12-18 Devang Patel <dpatel@apple.com>
+
+ * src/LLVMReader.hpp: Invalidate input architecture when optimizer is not available.
+
+----- Tagged ld64-81.3
+
+2007-12-17 Nick Kledzik <kledzik@apple.com>
+
+ * ld64.xcodeproj/project.pbxproj: remove extraneous header search paths
+
+
+2007-12-17 Devang Patel <dpatel@apple.com>
+
+ * src/LLVMReader.hpp: Do not throw exception if LLVMReader is not able to
+ dlopen LTO library. Instead just flag input file as an invalid LLVM bitcode file.
+
+
+2007-12-14 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5645908> gcc DejaGnu failure: gcc.dg/20020312-2.c (test for excess errors) (-fstack-protector-all)
+ * src/MachOWriterExecutable.hpp: fix Writer<x86>::generatesExternalTextReloc() to allow text relocs
+ * unit-tests/test-cases/read-only-relocs: updated test case to link a dynamic main executable compiled with -static
+
+
+2007-12-14 Devang Patel <dpatel@apple.com>
+
+ <rdar://problem/5648438> Enable Link Time Optimization in Opal
+ * src/LLVMReader.hpp: Locate LLVMlto.dylib relative to ld location in Developer folder.
+ * ld64.xcodeproj/project.pbxproj: Add {DEVELOPER_DIR}/usr/include in header search path.
+ * unit-tests/run-all-unit-tests: Set DYLD_FALLBACK_LIBRARY_PATH to find LLVMlto.dylib during unit testing.
+ * unit-tests/testcases/llvm-integration/Makefile: Point LLVMGCC and LLVMGXX to llvm-gcc-4.2 in Developer folder during unit testing.
+
+
+2007-12-13 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5645446> SWB: failures due to ld: pointer in read-only segment not allowed in slidable image, used in ...
+ * src/MachOReaderRelocatable.hpp: in Reader<x86>::addRelocReference() handle weak pc-rel 32-bit vanilla relocs properly
+
+----- Tagged ld64-81.2
+
+
+
+2007-12-07 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5615298> support 8-bit relocations for i386
+ * src/Architectures.hpp: add kPCRel8
+ * src/MachOReaderRelocatable.hpp: support 8-bit pc-rel relocations for intel
+ * src/MachOWriterExecutable.hpp: support 8-bit pc-rel relocations for intel
+ * unit-tests/test-cases/relocs-asm: add test cases
+
+
+----- Tagged ld64-81.1
+
+2007-12-06 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderDylib.hpp: rework cycle detection to remove some false positives
+
+
+2007-12-05 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5628149> Duplicate probe firings in Security.framework
+ * src/ld.cpp: check dtrace probe sites are not in fDeadAtoms before using
+ * unit-tests/test-cases/dtrace-static-probes-coalescing: add test case
+
+
+2007-12-05 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: fix CFString coalescing to work with -fwritable-strings
+ * unit-tests/test-cases/cfstring-coalesce: add -fwritable-strings to test case
+
+
+----- Tagged ld64-81
+
+2007-11-15 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4196067> ld64 should support runtime text relocations
+ * src/MachOWriterExecutable.hpp: add generatesLocalTextReloc() and generatesExternalTextReloc()
+ * src/Options.cpp: process -read_only_relocs option
+ * src/Options.h: add allowTextRelocs() and warnAboutTextRelocs()
+ * src/MachOReaderRelocatable.hpp: add hasLongBranchStubs()
+ * src/machochecker.cpp: allow relocs in read only segments, if section flags are set
+ * unit-tests/test-cases/read-only-relocs: update test case
+
+
+2007-11-08 Devang Patel <dpatel@apple.com>
+
+ * ld64.xcodeproj/project.pbxproj: add new build phase "build configure.h" for
+ ld target.
+ * src/ld.cpp: Include "configure.h"
+
+
+----- Tagged ld64-80.11
+
+2008-02-12 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5741312> Wrong section name for objc info for ARM when OBJC2 is used
+ * src/MachOWriterExecutable.hpp: switch segment/section name for ARM objc2 image info
+
+----- Tagged ld64-80.10
+
+2008-02-11 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5733578> ld64 does not support -aspen_version_min 2.0
+ * src/Options.cpp: allow 2.x for -aspen_version_min
+
+
+2008-02-11 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5733575> ld_classic: unknown flag: -aspen_version_min
+ * src/Options.cpp: change -aspen_version_min x.x to -macosx_version_min 10.5 when invoking ld_classic
+
+
+----- Tagged ld64-80.9
+
+2008-01-29 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5713054> -iphone_version_min ==> -aspen_version_min
+ * src/Options.cpp: support -aspen_version_min
+
+
+----- Tagged ld64-80.8
+
+2008-01-10 Nick Kledzik <kledzik@apple.com>
+
+ * src/Options.cpp: support transition to new objc ABI for ARM by allowing old .objc_class_name_*
+ style names in export files and map them to new _OBJC_CLASS_$_ style names.
+
+
+----- Tagged ld64-80.7
+
+2008-01-02 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5572168> BigBear5A18 isn't fully prebound
+ * src/Options.cpp: make fNeedsModuleTable true for arm
+
+----- Tagged ld64-80.6
+
+2007-11-30 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5620976> -iphone_version_min
+ * src/Options.cpp: handle -iphone_version_min option
+
+
+----- Tagged ld64-80.5
+
+2007-11-26 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5601142> need to special case some dylibs in seg_addr_table
+ * src/Options.cpp: retry seg_add_table lookup for a couple of unusual dylibs
+
+
+----- Tagged ld64-80.4
+
+2007-11-06 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: fix parsing of external and scattered thumb branch22 relocs
+ * unit-tests/test-cases/thumb-blx: add test case to keep blx issues from coming back
+
+----- Tagged ld64-80.3
+
+2007-11-03 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: remove recalc of dstAddr which could cause thumb branches to be +2
+ * src/MachOWriterExecutable.hpp: remove incorrect test for relocateableExternal
+
+----- Tagged ld64-80.2
+
+2007-11-01 Nick Kledzik <kledzik@apple.com>
+
+ * src/ld.cpp: hack my own prototype for log2() until math.h is cleaned up
+
+
+----- Tagged ld64-80.1
+
+2007-11-01 Nick Kledzik <kledzik@apple.com>
+
+ * ld64.xcodeproj/project.pbxproj: add HEADER_SEARCH_PATHS for cross builds
+ * src/ld.cpp: temporarily disable LLVM_SUPPORT
+ * src/MachOWriterExecutable.hpp: Don't use CC_MD5() directly
+
+
+2007-10-26 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5556038> Cannot build with libm_static.a statically linked
+ * src/MachOWriterExecutable.hpp: Fix makesExternalRelocatableReference() for -r -d case
+ * unit-tests/test-cases/tentative-to-real-hidden: add test case
+
+
+----- Tagged ld64-80
+
+2007-10-24 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4222696> linker should probably warn about trying to export a hidden symbol
+ * src/ld.cpp: if using -exported_symbols_list check each hidden atom as it is added to symbol table
+ * src/Options.h,.cpp: add hasExportMaskList()
+ * unit-tests/test-cases/exported_symbols_list-hidden: added test case
+
+
+2007-10-24 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOWriterExecutable.hpp: keep old style dtrace probes externel for kernel builds
+
+
+2007-10-23 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4556199> unify error and warning messages
+ <rdar://problem/5546450> -w should suppress warnings
+ * src/ld.cpp: use warning() function
+ * src/Options.h: remove emitWarnings()
+ * src/MachOReaderDylib.hpp: use warning() function
+ * src/MachOReaderRelocatable.hpp: use warning() function
+ * src/Options.cpp: use and implement warning()
+ * src/MachOWriterExecutable.hpp: use warning() function
+ * unit-tests/test-cases/visibility-warning: verify -w suppresses warnings
+
+
+2007-10-23 Devang Patel <dpatel@apple.com>
+
+ * src/ld.cpp: Cover arm support inside __OPEN_SOURCE__ macro check.
+ * src/LLVMReader.hpp: Cover arm support inside __OPEN_SOURCE__ macro check.
+ * src/MachOReaderDylib.hpp: Cover arm support inside __OPEN_SOURCE__ macro check.
+ * src/ObjectFile.h: Cover arm support inside __OPEN_SOURCE__ macro check.
+ * src/MachOReaderRelocatable.hpp: Cover arm support inside __OPEN_SOURCE__ macro check.
+ * src/OpaqueSection.hpp: Cover arm support inside __OPEN_SOURCE__ macro check
+ * src/MachOWriterExecutable.hpp: Cover arm support inside __OPEN_SOURCE__ macro check.
+ * src/ObjectDump.cpp: Cover arm support inside __OPEN_SOURCE__ macro check.
+
+
+2007-10-22 Nick Kledzik <kledzik@apple.com>
+
+ * src/Options.cpp: add support for LD_DEAD_STRIP and LD_WARN_COMMONS
+ * src/MachOReaderRelocatable.hpp: fix problem with -dead_strip of ObjC literal pointers
+
+
+2007-10-22 Nick Kledzik <kledzik@apple.com>
+
+ * src/Options.cpp: have -static arm code link with ld_classic (for now)
+
+
+2007-10-22 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5396826> Recognize all arm architectures
+ * src/MachOReaderRelocatable.hpp: add support for all ARM sub-types
+ * unit-tests/test-cases/cpu-sub-types: add test cases for all combinations of ARM sub-types
+
+
+2007-10-19 Nick Kledzik <kledzik@apple.com>
+
+ * src/*: merge in arm support
+ * unit-tests/test-cases/*: fix to work for arm and thumb
+
+----- Tagged ld64-79
+
+2007-10-16 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOWriterExecutable.hpp: if -r mode, always set custom alignment (SET_COMM_ALIGN) on common symbols
+ * unit-tests/test-cases/visibility-warning-dylib-v-archive/Makefile: fix warning
+ * unit-tests/test-cases/static-executable/Makefile: fix spurious failure
+
+
+2007-10-16 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOWriterExecutable.hpp: fix edge case in branch island generation
+
+
+2007-10-12 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5323449> Add option to create old, slow stubs for i386
+ * src/ObjectFile.h/.cpp: support -read_only_stubs
+ * src/MachOWriterExecutable.hpp: enhance StubAtom<x86> to support old style __symbol_stub/__la_symbol_ptr stubs
+ * unit-tests/test-cases/slow-x86-stubs: add test case
+
+
+2007-10-12 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5427952> ld64's re-export cycle detection logic prevents use of X11 libGL on Leopard
+ * src/Options.cpp: in findFileUsingPaths() don't search for embedded dylibs
+ * unit-tests/test-cases/indirect-path-search/Makefile: added case for a dylib embedded in a framework
+
+
+2007-10-11 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5451987> add option to disable implicit load commands for indirectly used public dylibs
+ * src/Options.cpp: add support for -no_implicit_dylibs
+ * src/ObjectFile.h: add fImplicitlyLinkPublicDylibs
+ * src/MachOReaderDylib.hpp: test fImplicitlyLinkPublicDylibs before hoisting an implicitly linked dylib
+ * unit-tests/test-cases/implicit_dylib: add test case
+
+
+2007-10-11 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5254413> -interposable_list
+ * src/Options.h/cpp: Add fInterposeList and fInterposeMode to support -interposable_list
+ * src/MachOWriterExecutable.hpp: pass symbol name to fOptions.interposable()
+ * unit-tests/test-cases/interposable_list: add test case
+
+
+2007-10-10 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5529626> If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB
+ * src/MachOWriterExecutable.hpp: automatically use LC_LOAD_WEAK_DYLIB if all symbols used from a dylib are weak_import
+ * unit-tests/test-cases/weak_dylib: added test case
+
+
+2007-10-10 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5504954> linker does not error when dylib ordinal exceeds 250
+ * src/MachOWriterExecutable.hpp: error out if ordinals exceed max allowed
+
+
+2007-10-10 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4067110> overriding 'operator new' or 'operator delete' fails if no weak symbols are present
+ * src/ld.cpp: at end of checkUndefines() search dylibs for weak versions of any global external symbols
+ * src/ObjectFile.h: add hasWeakExternals() method to Reader
+ * src/MachOReaderDylib.hpp: implement hasWeakExternals() method in Reader
+ * src/ExecutableFile.h: add overridesDylibWeakDefines parameter to write()
+ * src/MachOWriterExecutable.hpp: use overridesDylibWeakDefines parameter to write()
+ * unit-tests/test-cases/operator-new: add test case
+
+
+2007-10-05 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5048861> No warning about tentative definition conflicting with dylib definition
+ <rdar://problem/5132652> .comm variables in shared library, worked with XCode 2.4.1, broken with XCode 3?
+ * src/ld.cpp: at end of checkUndefines() verify if any remaining commons conflict with dylibs
+ * doc/man/man1/ld.1: document -commons and -warn_commons options
+ * unit-tests/test-cases/tentative-and-dylib: added test case
+
+
+2007-10-05 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5346331> NS/CFString constants are not dead strippable
+ * src/MachOReaderRelocatable.hpp: break up __cfstring section into one atom per cfstring, make them coalesable
+ * unit-tests/test-cases/cfstring-coalesce: added test case
+
+
+2007-10-05 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5524973> Dead stripping + exported symbols list using wildcards doesn't seem to do the right thing
+ * src/Options.cpp/h: add hasWildCardExportRestrictList()
+ * src/ld.cpp: if dead stripping code and have wildcard exports, add all global atoms matching wildcards as roots
+ * unit-tests/test-cases/exported-symbols-wildcards-dead_strip: added test case
+
+
+2007-10-04 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5433882> ld shouldn't search /Network/Library/Frameworks by default
+ * src/Options.cpp: remove /Network/Library/Frameworks/ from default search path
+ * doc/man/man1/ld.1: document the change
+
+
+2007-10-04 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5341567> all binaries should get LD_UUID load commands, not just those with DWARF symbols
+ * src/ld.cpp: default fCreateUUID to be true for non object file output types
+ * unit-tests/test-cases/no-uuid/Makefile: update test case to match new rules
+
+
+----- Tagged ld64-78
+
+2007-09-27 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5476313> range check load commands
+ * src/MachOReaderDylib.hpp: check that load commands all fit in load command size from header
+ * src/MachOReaderRelocatable.hpp: check that load commands all fit in load command size from header
+
+
+2007-09-27 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5433355> Xc8M2540a: ld64 crashes when linking Pascal program
+ * src/ld.cpp: fix findAtomAndOffset() to handle where there are no function atoms
+
+
+2007-09-27 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5241179> ADOBE Xcode 3: ld -dead_strip does not work with -init from an archive
+ * src/ld.cpp: add bool parameter to entryPoint() so -init atom not looked for too soon
+ * unit-tests/test-cases/dead_strip-init-archive: added test case
+
+
+2007-09-26 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5459546> Spurious link warnings for inline members of C++ template classes
+ * src/ld.cpp: check definition kinds before warning about visibility mismatches
+ * unit-tests/test-cases/visibility-warning: added test case
+
+
+2007-09-26 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5394172> an empty .o file with zero load commands will crash linker
+ * src/MachOReaderRelocatable.hpp: have Reader constructor return early of no load commands
+ * unit-tests/test-cases/empty-object: added test case
+
+
+2007-09-26 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5453384> 9a527: ppc64 branch islands fail with 4GB pagezeo
+ * src/MachOWriterExecutable.hpp: start range calculations at start of __text not at zero.
+
+
+----- Tagged ld64-77 (Xcode 3.0)
+
+2007-07-23 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5351380> Kernel is linked with some global symbols unsorted
+ * src/MachOWriterExecutable.hpp: Add NListNameSorter to allow global atoms and extra labels to be sorted
+
+
+2007-07-20 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5254468> Can't do objc_msgSendSuper dispatches after loading a Fix&Continue bundle
+ * src/MachOWriterExecutable.hpp: when calculating what kind of reloc to use, never use an
+ external reloc to reference 32-bit ObjC symbols.
+
+
+2007-07-20 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5349847> Runtime crash with ICC math library on Leopard
+ * src/MachOReaderRelocatable.hpp: detect if section starts with a symbol that is not
+ aligned to section and correct it.
+
+
+----- Tagged ld64-76
+
+2007-06-29 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5303718> export hiding does not work for frameworks
+ * src/MachOReaderDylib.hpp: fix checks in isPublicLocation()
+ * unit-tests/test-cases/symbol-moving: update to test frameworks as well as dylibs
+
+
+2007-06-27 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5299907> linker should use undefines from flat dylibs when linking a main flat
+ * src/ObjectFile.h: added fLinkingMainExecutable
+ * src/Options.cpp: set up fLinkingMainExecutable
+ * src/MachOReaderDylib.hpp: when linking a main executable for flat namespace, the reader for
+ any loaded flat namespace dylib will have a new atoms that has references to all undefined
+ symbols in the dylib
+ * unit-tests/test-cases/flat-indirect-undefines: added test case
+ * doc/man/man1/ld.1: update man page to describe when dylib undefines are used
+
+
+2007-06-27 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5277857> OpenGL.framework and X11 both have a libGL.dylib which can cause ld to segfault if both are found
+ * src/MachOReaderDylib.hpp: add assertNoReExportCycles() method
+ * unit-tests/test-cases/dylib-re-export-cycle: added test case
+
+
+2007-06-27 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5286462> ld64 has slightly different warning message formats than the old ld
+ * src/ld.cpp: standardize all warning messages to start with "ld: warning"
+ * src/MachOWriterExecutable.hpp: ditto
+ * src/MachOReaderRelocatable.hpp: ditto
+ * src/MachOReaderDylib.hpp:ditto
+
+
+2007-06-26 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5297034> -dead_strip can cause duplicate external commons
+ * src/ld.cpp: don't use discarded coalesced global atoms as dead strip roots
+ * src/machochecker.cpp: error if duplicate external symbols
+ * unit-tests/test-cases/commons-coalesced-dead_strip: added test case
+
+
+2007-06-26 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4135857> update man page that linker does not search indirect libraries with two-level namespace
+ * doc/man/man1/ld.1: add new "Indirect dynamic libraries" section to man page
+
+
+2007-06-26 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5285473> Xc9A466: Exports file cannot use Mac line ends
+ * src/Options.cpp: check for \r or \n when parsing .exp files
+ * unit-tests/test-cases/exported_symbols_list-eol: added test case
+
+
+----- Tagged ld64-75
+
+2007-05-31 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4607755> Simplier, generalized way to re-export dylibs: LC_REEXPORT_DYLIB
+ * src/MachOWriterExecutable.hpp: Use LC_REEXPORT_DYLIB when targetting 10.5
+
+
+----- Tagged ld64-74.5
+
+2007-05-31 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5241902> set OSO timestamp to zero for when building in buildit
+ * src/ld.cpp: check for RC_RELEASE and if exists set all OSO timestamps to zero
+
+
+2007-05-30 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5224676> BUILD_STABS now causes ld of xnu to bus error
+ * src/ld.cpp: Change || to && in collectStabs()
+
+
+----- Tagged ld64-74.4
+
+2007-05-18 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5211667> static probes don't work with libraries in dyld shared cache
+ * src/OpaqueSection.hpp: the __TEXT segment is executable
+
+
+----- Tagged ld64-74.3
+
+2007-05-16 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5201463> ppc: linker adds stubs to cstring references
+ * src/MachOWriterExecutable.hpp: update ppc stubableReference() to only allow high/low references
+ to be stubed if they reference a symbol in some other dylib.
+ * unit-tests/test-cases/stub-generation: added test case
+
+
+2007-05-16 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5008421> ppc64: need to make LOCAL indirect symbol table entry for now local symbol
+ * src/MachOWriterExecutable.hpp: factored local tests into indirectSymbolIsLocal()
+ * unit-tests/test-cases/non-lazy-r: added test case
+
+
+2007-05-15 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5198807> ld64 drops fix&continue bit in __OBJC, __image_info.
+ * src/MachOReaderRelocatable.hpp: implement objcReplacementClasses()
+
+
+2007-05-15 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5066152> support __image_info in __DATA segment for 64-bits
+ * src/MachOReaderRelocatable.hpp: use strncmp() for __objc_imageinfo since it is 16 bytes long
+ * src/MachOWriterExecutable.hpp: specialize segment/section names for synthesized objc image info section
+
+
+2007-05-15 Nick Kledzik <kledzik@apple.com>
+
+ * unit-tests/include/common.makefile: set COMPILER_PATH so harness works with latest compiler
+
+
+----- Tagged ld64-74.2
+
+2007-05-11 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5195447> ld64-74.1 breaks libstdc++ DejaGnu test (G5 only)
+ * src/MachOWriterExecutable.hpp: don't stub a reference if the target offset is non-zero
+
+
+----- Tagged ld64-74.1
+
+2007-05-09 Nick Kledzik <kledzik@apple.com>
+
+ * src/Options.h: add emitWarnings()
+ * src/Options.cpp: wire up -w to emitWarnings()
+
+
+2007-05-09 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5137285> ld64 won't link wine (regression from Tiger)
+ * src/Architectures.hpp: add x86::kPointerDiff16 and x86::kPCRel16
+ * src/MachOReaderRelocatable.hpp: add support to parse new relocs
+ * src/MachOWriterExecutable.hpp: add support fo new relocs
+
+
+2007-05-08 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5182537> need way for ld and dyld to see different exported symbols in a dylib
+ * src/MachOReaderDylib.hpp: update parse and use $ld$ symbols
+ * src/Options.h: move VersionMin to ReaderOptions
+ * src/ObjectFile.h: move VersionMin to ReaderOptions
+ * src/Options.cpp: move VersionMin to ReaderOptions
+ * src/MachOWriterExecutable.hpp: move VersionMin to ReaderOptions
+ * unit-tests/test-cases/symbol-moving: added test case
+
+
+2007-05-03 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5181105> typo in error message for linking -pie
+ * src/MachOWriterExecutable.hpp: fix typo in error messages
+
+
+----- Tagged ld64-74
+
+2007-05-03 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5171880> ld64 can't find @executable _path relative dylibs from our umbrella frameworks
+ <rdar://problem/4019497> ld64 should handle linking against dylibs that have @loader_path based dylib load commands
+ * src/ObjectFile.h: add from parameter to findDylib()
+ * src/MachOReaderDylib.hpp: supply from parameter to findDylib()
+ * src/ld.cpp: use from parameter for @loader_path substitution in findDylib()
+ * unit-tests/test-cases/re-export-relative-paths: added test case
+
+
+2007-05-02 Nick Kledzik <kledzik@apple.com>
+
+ * src/ObjectFile.h: add fLogObjectFiles and fLogAllFiles
+ * src/Options.cpp: hook up -t to fLogAllFiles and -whatsloaded to fLogObjectFiles
+ * src/MachOReaderDylib.hpp: log if fLogAllFiles
+ * src/MachOReaderRelocatable.hpp: log if fLogObjectFiles or fLogAllFiles
+ * src/MachOReaderArchive.hpp: log if fLogAllFiles
+ * doc/man/man1/ld.1: update man page
+
+
+2007-05-02 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5177848> typo in message, frameowrk
+ * src/Options.cpp: fix typo
+
+
+2007-05-01 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4977301> "ld" man page is missing the description for many options
+ * doc/man/man1/ld.1: add documentation on all obsolete options
+
+
+2007-05-01 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5113424> ld doesn't handle -mlong-branch .o files that have had local symbols stripped
+ <rdar://problem/4965359> warning about dwarf line info with -mlong-branch
+ * src/MachOReaderRelocatable.hpp: don't lop -mlong-branch stubs off end of functions
+ * src/MachOWriterExecutable.hpp: allow code references besides BR24 to be stubable
+
+
+2007-04-30 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5065659> unable to link VTK because __textcoal_nt too large
+ * src/MachOReaderRelocatable.hpp: when doing a final link map __textcoal_nt to __text
+
+
+2007-04-30 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5062685> ld does not report error when -r is used and exported symbols are not defined.
+ <rdar://problem/4637139> ld leaves global common symbols not in exported symbols list.
+ * src/ld.cpp: stop special casing -r mode in checkUndefines()
+ * src/MachOWriterExecutable.hpp: don't create proxy atom in -r mode if it is supposed to be exported.
+ mark tentative definitions are private extern in -r mode even without -keep_private_externs
+ * unit-tests/test-cases/exported_symbols_list-r: added test case
+
+
+2007-04-27 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5137732> ld should keep looking when it finds a weak definition in a dylib
+ * src/ld.cpp: modified addJustInTimeAtoms() to keep looking when a weak defintion is found
+ * unit-tests/test-cases/weak-def-ordinal: added test case
+
+
+2007-04-27 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5166572> better error message for indirect dylibs missing required architecture
+ * src/ld.cpp: when loading indirect dylib add path to error messages
+
+
+2007-04-25 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5109373> the i386 slice of dyld does not need __IMPORT segment
+ * src/ObjectFile.h: add fForDyld
+ * src/Options.cpp: set up fForDyld
+ * src/MachOReaderRelocatable.hpp: if fForDyld, change __IMPORT segment to __DATA
+ * src/MachOWriterExecutable.hpp: recognize __DATA/__pointers in dyld as a non-lazy section
+
+
+2007-04-24 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5008421> ppc64: need to make LOCAL indirect symbol table entry for now local symbol
+ * src/MachOWriterExecutable.hpp: use INDIRECT_SYMBOL_LOCAL for any non-global symbol
+ * unit-tests/test-cases/strip_local: update test case
+
+
+2007-04-24 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5150407> ld64 -sectorder and -order_file files don't accept white space following the :
+ * src/Options.cpp: prune white space after colon and before symbol name
+ * unit-tests/test-cases/order_file: update test case to have a space after the colon
+
+
+2007-04-24 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5055233> ld64 corrupts debug symbol table entries, nm doesn't print them
+ * src/MachOWriterExecutable.hpp: properly set ilocalsym in module table
+
+
+2007-04-24 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5066152> support __image_info in __DATA segment for 64-bits
+ * src/MachOReaderRelocatable.hpp: look for new objc info section name too
+
+
+2007-04-24 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOWriterExecutable.hpp: fix -non_global_symbols_strip_list to work with -r
+ * unit-tests/test-cases/local-symbol-partial-stripping: update test case
+
+
+
+----- Tagged ld64-73.7
+
+2007-05-10 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5194804> can't use dtrace static probes in x86_64 dylib
+ * src/MachOWriterExecutable.hpp: x86_64:kPointerDiff32 is ok in shared region
+ * unit-tests/test-cases/dtrace-static-probes: update to build dylib too
+
+
+2007-05-09 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5191610> 9A430: using -dead_strip with static dtrace probes causes ld to crash
+ * src/ld.cpp: fix markLive() to look at right name in dtrace probe refernce
+ * unit-tests/test-cases/dtrace-static-probes: added -dead_strip case
+
+
+----- Tagged ld64-73.6
+
+2007-04-17 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5140897> Add options to do partial stripping of local symbols
+ * src/MachOWriterExecutable.hpp: use fOptions.keepLocalSymbol()
+ * src/Options.cpp: implement -non_global_symbols_no_strip_list and -non_global_symbols_strip_list
+ * src/Options.h: replace stripLocalSymbols() with localSymbolHandling() and keepLocalSymbol()
+ * doc/man/man1/ld.1: document -non_global_symbols_no_strip_list and -non_global_symbols_strip_list
+ * unit-tests/test-cases/local-symbol-partial-stripping: added test case
+
+
+----- Tagged ld64-73.5
+
+2007-04-17 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5129379> ld64-73.3 XBS logging incorrectly reporting "direct" dynamic libraries
+ * src/ld.cpp: restore direct vs indirect library for LD_TRACE_DYLIBS logging
+
+
+2007-04-16 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5067034> data initialized to a weak imported symbol is missing relocation
+ * src/MachOWriterExecutable.hpp: check for A::kPointerWeakImport in buildExecutableFixups()
+ * unit-tests/test-cases/weak_import: updated test case to catch this problem
+
+
+2007-04-13 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5071047> Support -U
+ * src/MachOWriterExecutable.hpp: create proxies for -U symbols
+ * src/Options.cpp: process -U
+ * src/Options.h: add allowedUndefined() and someAllowedUndefines()
+ * src/ld.cpp: create proxies for -U symbols
+ * doc/man/man1/ld.1: document -U and -undefined options
+ * unit-tests/test-cases/undefined-dynamic-lookup: added test case
+
+
+----- Tagged ld64-73.4
+
+2007-04-12 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5125280> ld changes needed to support read-only DOF
+ * src/Options.cpp: remove -read_only_dof
+ * src/Options.h: remove fReadOnlyDOFs
+ * src/ld.cpp: only generate read-only DOF sections
+
+
+----- Tagged ld64-73.3.1
+
+2007-04-13 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5130496> -framework vecLib -framework Accelerate causes bad ordinals
+ * src/MachOWriterExecutable.hpp: fix bug optimizeDylibReferences() when there are two readers with same install name
+
+
+----- Tagged ld64-73.3
+
+2007-04-03 Nick Kledzik <kledzik@apple.com>
+
+ * src/ld.cpp: read-only-dofs should use 32-bit offsets for x86_64
+ * src/MachOReaderDylib.hpp: if "public" re-export is not marked implict, still mark it as re-exported
+
+
+2007-04-02 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5105971> if replacement file for -dylib_file is missing, warn instead of error
+ * src/ld.cpp: a try/catch to turn -dylib_file error into a warning.
+ * unit-tests/test-cases/dylib_file-missing: add test case
+ * doc/man/man1/ld.1: update man page about -dead_strip_dylibs
+
+
+----- Tagged ld64-73.2
+
+2007-03-31 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5102873> ld64-73: atom sorting error with duplicate zero sized bss symbols
+ * src/MachOReaderRelocatable.hpp: suppress warning on sorting zero size zero fill atoms
+
+2007-03-31 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5102845> ld64-73 fails anything linking with -lm
+ * src/ld.cpp: when processing dylbs that are sylinks ensure that fDylibMap contains all paths
+ * src/MachOWriterExecutable.hpp: when dead stripping dylibs and renumbering ordinals make sure
+ aliases dylib get renumbered too
+ * unit-tests/test-cases/dylib-aliases: added
+
+
+----- Tagged ld64-73.1
+
+2007-03-30 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOWriterExecutable.hpp: back out use of LC_REEXPORT_DYLIB until rdar://problem/5009909 is in build fleet
+
+
+----- Tagged ld64-73
+
+2007-03-30 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4175790> ER: -dead_strip_dylibs
+ <rdar://problem/3904828> linker should add implicit load commands for indirectly used public dylibs
+ * src/ObjectFile.h: change dylib reader interface to implictly/explicitlyLinked
+ * src/ld.cpp: use new dylib reader interface
+ * src/Options.h: add deadStripDylibs()
+ * src/Options.cpp: support -dead_strip_dylibs
+ * src/MachOReaderDylib.hpp: use new dylib reader interface
+ * src/MachOWriterExecutable.hpp: remove dylib load commands for unused dylibs and alter ordinals
+ * unit-tests/test-cases/re-export-optimizations: added
+ * unit-tests/test-cases/dead_strip_dylibs: added
+
+
+2007-03-30 Nick Kledzik <kledzik@apple.com>
+
+ * src/Options.cpp: enable -lfoo to search for libfoo.so as well as libfoo.dylib,
+ remove seg addr table hack for transitioning to new linker
+
+2007-03-30 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5073800> ADOBE XCODE3: Linker is slow with large C++ .o files
+ * src/MachOReaderRelocatable.hpp: the compiler generates stubs to weak functions in the
+ same translation unit. Don't treat those like the spurios stubs to static functions.
+
+
+2007-03-29 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4739044> ld64 should link mach_kernel during xnu builds to support dtrace
+ * src/MachOReaderRelocatable.hpp: To handle duplicate labels properly, rework how atoms sizes are set
+ by iterating through sorted fAtoms rather than fAddrToAtom, . Change default alignment of commons
+ to be the natural alignment of the size rounded up to the closest power of two and max it at 12.
+ Build atoms in reverse symbol table order so that global atoms are constructed before locals.
+ This assures that if there is a global and local label at the same location, the global label
+ will become the atom's name and the local will be an alias. Properly handle a label
+ at the end of a section. Handle R_ABS in relocations. Handle sect-diff relocs with addends.
+ Don't auto-strip 'l' symbols in static executables (mach_kernel).
+ * src/OpaqueSection.hpp: opaque_section now has an ordinal
+ * src/ld.cpp: opaque_section now requires an ordinal
+ * src/ObjectFile.h: add ReaderOptions.fForStatic
+ * src/Options.cpp: set fForStatic when building a static executable
+ * src/MachOWriterExecutable.hpp: add from atom to StubAtom<ppc>. Properly write out i386
+ sect-diff relocs with addends. properly write out ppc PICbase relocs where pic base
+ is not in the atom.
+
+
+2007-03-27 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5085863> Typo in ld man page (-exported_symbols_list)
+ * doc/man/man1/ld.1: fix typo
+
+
+2007-03-26 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4727750> consider generating LC_UUID from a checksum of the file
+ * src/Options.h: change emitUUID() to getUUIDMode()
+ * src/Options.cpp: support -random_uuid
+ * src/MachOWriterExecutable.hpp: set uuid to be md5 hash of entire output file
+
+
+2007-03-24 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOWriterExecutable.hpp: restructure writeAtoms() to copy all atoms in memory if possible
+
+
+2007-03-24 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5082603> ld -r of stripped .o file can incorrectly merge non-lazy pointers
+ * src/MachOWriterExecutable.hpp: when generating a .o file, non-lazy pointer with target offsets should be
+ encoded as LOCAL in the indirect symbol table
+ * unit-tests/test-cases/stripped-indirect-symbol-table: added test case
+
+
+2007-03-23 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5084564> SWB: ld64-72 errors building with gcc-4.2
+ * src/MachOReaderDylib.hpp: add curly brackets in switch cases
+ * src/MachOWriterExecutable.hpp: rearrange classes so there are no template specialization forward references
+
+
+2007-03-23 Nick Kledzik <kledzik@apple.com>
+
+ * src/ld.cpp: fix -print_statistics when using -dead_strip
+
+
+2007-03-23 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: generate better names for non-lazy pointers to the interior of atoms
+
+
+2007-03-16 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOWriterExecutable.hpp: speed up ld -r a little by reversing relocs en mas
+
+
+2007-03-16 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4975277> ld Bus Error on missing command line arguments
+ * src/Options.cpp: check next argv[] is not NULL
+
+
+2007-03-16 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4832049> need to be able to order symbols in anonymous namespaces
+ * src/ld.cpp: add logic to do fuzzy matching of symbols with anonymous namespace usage
+ * unit-tests/test-cases/order_file-ans: added test case
+
+
+2007-03-16 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5042552> headerpad_max_install_names deprecated for 64-bit
+ * src/ld.cpp: make sure dylib load command order matches command line order
+ * src/Options.h: add maxMminimumHeaderPad()
+ * src/Options.cpp: add maxMminimumHeaderPad() set by -headerpad_max_install_names
+ * src/src/MachOWriterExecutable.hpp: check maxMminimumHeaderPad()
+ * doc/man/man1/ld.1: update man page about -headerpad_max_install_names
+
+
+2007-03-16 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4592484> Linker returns success although exported symbols are undefined.
+ * src/ld.cpp: turn missing symbols back into an error
+
+
+2007-03-16 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4019497> ld64 should handle linking against dylibs that have @loader_path based dylib load commands
+ * unit-tests/test-cases/loader_path: added test case
+
+
+2007-03-16 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/3904828> linker should add implicit load commands for indirectly used public dylibs
+ <rdar://problem/4142277> Indirect libraries should be found using -F and -L options
+ <rdar://problem/4607755> Simplier, generalized way to re-export dylibs: LC_REEXPORT_DYLIB
+ * src/ld.cpp: reworked all dylib processing. Readers can now add the dylib list.
+ * src/Options.h: add findFileUsingPaths()
+ * src/MachOReaderDylib.hpp: look in re-exported children instead of requring linker to do that
+ * src/ObjectFile.h: add processIndirectLibraries(), remove getDependentLibraryPaths()
+ * src/machochecker.cpp: support LC_REEXPORT_DYLIB
+ * src/ExecutableFile.h: simplify DyLibUsed
+ * src/Options.cpp: add findFileUsingPaths(). add new re-export options
+ * src/MachOWriterExecutable.hpp: Use LC_REEXPORT_DYLIB when targetting 10.5
+ * doc/man/man1/ld.1: updated with new re-export options
+ * unit-tests/test-cases/indirect-path-search: added tests that -F and -L work with indirect dylibs
+ * unit-tests/test-cases/re-export-cases: added tests for all combinations of re-exporting
+
+
+2007-03-14 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4982400> sort external relocations to optimize dyld performance
+ * src/MachOWriterExecutable.hpp: added ExternalRelocSorter
+ * src/machochecker.cpp: verify external relocations are grouped by symbol number
+ * unit-tests/test-cases/external-reloc-sorting: added test case
+
+
+----- Tagged ld64-72
+
+2007-03-06 Nick Kledzik <kledzik@apple.com>
+
+ * src/Options.cpp: ignore .objc_category_name_* symbols in .exp files
+
+
+2007-03-06 Nick Kledzik <kledzik@apple.com>
+
+ * src/Options.cpp: stop special casing mach_kernel and instead requre kernel to be built with -new_linker
+
+
+2007-03-06 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5044253> ld64-72 (experimental) is causing DejaGnu test failures
+ * src/MachOWriterExecutable.hpp: add optimizableGOTReferenceKind() to track GOT uses that cannot be optimized
+
+
+2007-03-06 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5026135> minimum header padding should be 32 to allow code signing
+ * src/Options.cpp: initialize fMinimumHeaderPad to 32
+ * src/MachOWriterExecutable.hpp: better calculation of header padding
+
+
+2007-03-06 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5033206> Linker crashes with -flat_namespace against two-level dylibs that might have re-exports
+ * src/ld.cpp: flat namespace should not allow NULL indirect readers
+
+
+2007-03-06 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: don't error on S_COALESCED sections with anonymous atoms
+ * src/MachOWriterExecutable.hpp: set MH_PIE bit when linking -pie
+ * ld64.xcodeproj/project.pbxproj: don't echo environment when running unit test
+
+
+2007-03-01 Nick Kledzik <kledzik@apple.com>
+
+ * doc/man/man1/ld.1: Add descriptions to all "rarely used options"
+
+
+2007-03-01 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4971033> Remove support for Essential Symbols: Warn about use of -Sp option; remove man page entry
+ * src/Options.cpp: make -Sp obsolete
+ * doc/man/man1/ld.1: make -Sp obsolete
+
+
+2007-03-01 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5040314> Support -pie
+ * src/Options.h: Add positionIndependentExecutable()
+ * src/Options.cpp: Support -pie option to set positionIndependentExecutable()
+ * src/MachOWriterExecutable: if -pie is used, add extra local relocations and error if any
+ absolute addressing is used
+
+
+2007-03-01 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4739044> ld64 should link mach_kernel during xnu builds to support dtrace
+ * src/ld.cpp: Ensure segments are laid out in discovery order. Add support for kAbsoluteSymbol.
+ Warn when merging symbols of different visiblity. Warn when a tentative definition
+ is replaced by one a real definition with a smaller size. Lay out __common section
+ so that ones built with -fno-commons come before regular commons.
+ * src/ObjectFile.h: remove SegmentOffset ivar and getter/setters
+ * src/machochecker.cpp: allow images with no r/w segments
+ * src/MachOReaderRelocatable: Add AbsoluteAtom. Sort tentative definitions by name instead of by size
+ Add support for custom commons alignment.
+ * src/Options.cpp: Fix spurious -sectalign warnings. Don't use ld_classic when linking mach_kernel
+ * src/MachOWriterExecutable.hpp: Support kAbsoluteSymbol atoms. In -r mode, set custom alignment
+ for commons if alignment is not its size. Support global __dtrace_probe labels.
+ * src/ObjectDump.cpp: add support for kAbsoluteSymbol atoms.
+ * unit-tests/test-cases/commons-alignment: Added test case for custom commons alignment
+ * unit-tests/test-cases/absolute-symbol: Added test case for basic absolute symbols
+ * unit-tests/test-cases/segment-order: Added test case that segments lay out in discovery order
+ * unit-tests/test-cases/commons-order: Added test case that commons lay out correctly
+ * unit-tests/test-cases/end-label: Added test case that a label used to mark the end of a section does not
+ get associcated with the next section.
+
+
+2007-02-23 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/3965017> gcc-5005: DejaGnu failures due to -frepo
+ * src/ld.cpp: Add quotes to referenced from name to make collect2 and -frepo happy
+
+
+2007-02-22 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOWriterExecutable.hpp: rework how padding after load commands is calculated
+
+
+2007-02-21 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOWriterExecutable.hpp: extend special case of __mh_execute_header to static executables too
+
+
+2007-02-21 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/3882294> gcc link map option ( "-M" ) should be redirectable to file
+ * doc/man/man1/ld.1: added -map option description
+ * src/Options.h: added generatedMapPath()
+ * src/Options.cpp: set up generatedMapPath() if -map option is used
+ * src/MachOWriterExecutable.hpp: add writeMap() method to generate map file
+
+
+2007-02-19 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4557734> Implement GOT Load elimination optimization
+ * src/ld.cpp: track size of all atoms and if > 2GB sort large zero-fill atoms to end
+ * src/MachOWriterExecutable.hpp: If image size < 2GB, only generate GOT entries if value must be
+ updatable by dyld. If > 2GB, only eliminate GOT entries to non-zero-fill atoms. Any use
+ of an eliminated GOT entry has its code changed from MOVQ _foo@GOT(%rip) to LEAQ _foo(%rip).
+ * unit-tests/test-cases/large-data: added
+ * unit-tests/test-cases/got-elimination: added
+
+
+----- Tagged ld64-71.2
+
+2007-02-13 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4995303> new ld ignores -segprot option
+ * src/Options.h: expose customSegmentProtections()
+ * src/Options.cpp: parse -segprot option and populate customSegmentProtections()
+ * src/MachOWriterExecutable.hpp: use customSegmentProtections()
+
+
+2007-02-13 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4988068> i386 -stack_addr doesn't work
+ * src/MachOWriterExecutable.hpp: use correct offset into thread state record
+
+
+----- Tagged ld64-71.1
+
+2007-02-07 Nick Kledzik <kledzik@apple.com>
+
+ * src/ld.cpp: sort __OBJC2 segment to be next to __OBJC segment
+
+
+2007-02-07 Nick Kledzik <kledzik@apple.com>
+
+ * src/Options.cpp: change missing -seg_addr_table from an error to a warning
+
+
+2007-02-06 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4977311> Leopard 9A357: -dylib_file broken?
+ * src/MachOWriterExecutable.hpp: remove use of fInstallPathOverride
+ * src/Options.cpp: wire up -dylib_file option
+ * src/Options.h: remove fInstallPathOverride. add fDylibOverrides
+ * src/ld.cpp: check dylibOverrides() for indirect libraries
+ * unit-tests/test-cases/dylib_file: add test case
+
+
+2007-02-05 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderDylib.hpp: don't warn about zero size __image_info sections
+
+
+2007-02-04 Rick Balocca <rbalocca@apple.com>
+ Enable the failing cases for missing command line arguments
+
+2007-02-04 Rick Balocca <rbalocca@apple.com>
+ Make sure that all .o's are checked by ObjectDump
+ and all macho are checked by machochecker
+
+2007-02-04 Rick Balocca <rbalocca@apple.com>
+ Fix an endian problem with machochecker
+ Fix blank-stubs Makefile
+
+----- Tagged ld64-71
+
+2007-02-02 Rick Balocca <rbalocca@apple.com>
+ blank-stubs test case: handle the case of a native ppc compile--this
+ sets the subtype, which must be passed to lipo
+
+2007-02-01 Rick Balocca <rbalocca@apple.com>
+ make cpu-sub-types test more robust
+
+2007-02-01 Rick Balocca <rbalocca@apple.com>
+ auto-arch tests were resulting in a false FAILs
+
+2007-02-01 Rick Balocca <rbalocca@apple.com>
+ test cpu-sub-types was resulting in a false FAIL
+
+2007-02-01 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4969335> STD:VSC: c99 -o writes to file that does not have write permission
+ * src/MachOWriterExecutable.hpp: check file is writable before using it
+
+2007-02-01 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4965743> debug map (N_OSO) timestamps for object files in ranlib archive are incorrect
+ * src/MachOReaderArchive.hpp: parse modTime for .o files out of archive header
+
+2007-01-31 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4967535> 9A354: ld -all_load does *NOT* produce the same dSYM as *.o or -u
+ * src/ld.cpp: when using -all_load don't assume that all atoms have same reader
+ * unit-tests/test-cases/dwarf-archive-all_load: added
+
+----- Tagged ld64-70.1
+
+2007-01-31 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOWriterExecutable.hpp: in addObjectRelocs_powerpc() mask scattered r_address to 16-bits
+
+----- Tagged ld64-70
+
+
+2007-01-30 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4810668> linker should verify GC consistency of modules being linked into library
+ <rdar://problem/4474195> Support cpu-sub-types for ppc
+ * src/ObjectFile.h: Add getObjCConstraint() and getCpuConstraint()
+ * src/MachOReaderRelocatable.hpp: don't make atom for __image_info section, instead parse constaints
+ * src/MachOReaderDylib.hpp: look at __image_info content to get constaints
+ * src/ld.cpp: add updateContraints() and checkObjc()
+ * src/MachOWriterExecutable.hpp: add ObjCInfoAtom to sythesize __image_info content
+
+
+2007-01-28 Nick Kledzik <kledzik@apple.com>
+
+ src/*: remove ObjectFile::requiresFollowOnAtom() method
+
+
+2007-01-28 Nick Kledzik <kledzik@apple.com>
+
+ src/ld.cpp: enable LLVM_SUPPORT by default
+ src/LLVMReader.hpp: don't use absolute paths for llvm headers and libraries
+
+
+2007-01-26 Rick Balocca <rbalocca@apple.com>
+ * src/ObjectDump.cpp: The usage() message was incorrect.
+
+
+2007-01-25 Rick Balocca <rbalocca@apple.com>
+ * unit-tests/test-cases/zero-fill3: It was reporting FAIL on ld64 error return.
+ It should have been checking for non-error return.
+
+
+2007-01-24 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4952297> x86 fast stubs should not cross 64-byte boundries
+ * src/MachOWriterExecutable.hpp: for x86, 64-byte align __jump_table section
+ and make 64-btye crossing stubs be empty entries with indirect symbol table
+ entry of INDIRECT_SYMBOL_ABS
+
+
+2007-01-19 Nick Kledzik <kledzik@apple.com>
+
+ * src/Options.h: add readOnlyx86Stubs()
+ * src/Options.cpp: support -read_only_stubs
+ * src/MachOWriterExecutable.hpp: make __IMPORT segment not writable if -read_only_stubs is used
+
+
+2007-01-16 Eric Christopher <echristo@apple.com>
+
+ <rdar://problem/4856341> ld64 --help isn't recognized
+ * src/Options.cpp (Options::parse): Support --help and -help.
+
+
+2007-01-15 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOFileAbstraction.hpp: add range checking on macho_scattered_relocation_info::set_r_address()
+
+
+2007-01-14 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4514409> Support wildcards in contents of -exported_symbols_list
+ * src/Options.h: add SetWithWildcards class
+ * src/Options.cpp: add -exported_symbol and -unexported_symbol and use SetWithWildcards
+ * doc/man/man1/ld.1: add -exported_symbol and wildcard explanation
+ * unit-tests/test-cases/exported-symbols-wildcards: added test case
+
+
+2007-01-10 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4868270> [U]SDT probes should use C calling convention
+ * src/Options.cpp: Add -read_only_dof
+ * src/ld.cpp: create __dof section(s) based on probe and isenabled sites
+ * src/MachOReaderRelocatable.hpp: parse new sdt 2.0 probes encoded in .o files
+ * src/MachOWriterExecutable.hpp: handle regenerating dtrace probes into .o files
+ * unit-tests/test-cases/dtrace-static-probes: added test case
+
+
+----- Tagged ld64-69.8
+
+2007-01-30 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4964508> Support LD_FORCE_NO_SEG_ADDR_TABLE
+ * src/Options.cpp: Support LD_FORCE_NO_SEG_ADDR_TABLE
+
+
+----- Tagged ld64-69.7
+
+2007-01-25 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4949507> Leopard9A351: CFM Apps Are Broken because CFM glue is missing
+ * src/MachOReaderRelocatable.hpp: check S_ATTR_NO_DEAD_STRIP in dontDeadStrip()
+
+
+----- Tagged ld64-69.6
+
+2007-01-24 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4947347> LD_TRACE_ARCHIVES should only print out when a .o is actually used from an archive
+ * src/ld.cpp: create and use logArchive()
+
+
+----- Tagged ld64-69.5
+
+2007-01-22 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4946075> 9A350: can't link ppc programs with ld_classic
+ * src/Options.cpp: Remove support for LD_NO_CLASSIC_LINKER. Add support for -classic_linker
+
+
+----- Tagged ld64-69.4
+
+2007-01-17 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4925645> QTComponents does not link with ld64
+ * src/MachOReaderRelocatable.hpp: handle N_RSYM and N_PSYM stabs
+
+
+----- Tagged ld64-69.3
+
+2007-01-03 Nick Kledzik <kledzik@apple.com>
+
+ * src/Options.cpp: If the same dylib is specified twice and the second is specified weak, make it weak
+
+
+----- Tagged ld64-69.2
+
+2006-12-18 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4889729> -dead_strip without -exported_symbols_list should not strip global functions from archives
+ * src/ld.cpp: when adding a .o file from an archive, add all its global symbols to live roots
+ * unit-tests/test-cases/dead_strip-archive: added
+
+
+2006-12-18 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4889409> flat_namespace main executables do not need to indirect interior references
+ * src/MachOWriterExecutable.hpp: don't indirect references to global symbols in main executables
+ * unit-tests/test-cases/flat-main: updated to test for indirection
+ * unit-tests/test-cases/flat-dylib: added
+
+
+----- Tagged ld64-69.1
+
+2006-12-15 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4886721> -flat_namespace does not work with -mdynamic-no-pic
+ * src/MachOWriterExecutable.hpp: rework checking for use of ppc absolute addressing to allow them as long as
+ the target is within the same linkage unit.
+
+
+2006-12-15 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4886652> -ObjC should only load .o with .objc_ symbols
+ * src/Options.cpp: remove warning from -ObjC and have it instead set fLoadAllObjcObjectsFromArchives
+ * src/MachOReaderArchive.hpp: when -ObjC is used, preload all .o files from archives that contain .objc_ symbols
+
+
+----- Tagged ld64-69
+
+2006-12-13 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4879913> prebound interior pointers must be non-zero
+ * src/MachOWriterExecutable.hpp: in fixUpReference_powerpc() set lazy pointers bound to with the dylib to
+ their target value. Properly set REFERENCE_FLAG_UNDEFINED_* flags in reference table and n_desc
+
+
+2006-12-09 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4868738> ld64 fails to detect error that ld_classic does
+ * src/MachOWriterExecutable.hpp: check for absolute reloc to an external symbol
+ * src/MachOReaderRelocatable.hpp: ignore -mlong-branch stubs in .o files
+
+
+2006-12-09 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4874209> symbols with REFERENCED_DYNAMICALLY should never be stripped
+ * src/MachOWriterExecutable.hpp: update Writer<A>::shouldExport() to check for kSymbolTableInAndNeverStrip
+ * unit-tests/test-cases/main-stripped: add test that dynamically referenced symbol cannot be stripped
+
+
+2006-12-08 Nick Kledzik <kledzik@apple.com>
+
+ * unit-tests/test-cases/allowable-client: add variant test cases (e.g. CoreServices_profile)
+ * src/ld.cpp: allow frameworks with variant install names (e.g. CoreServices_profile) to be private clients
+
+
+2006-12-08 Nick Kledzik <kledzik@apple.com>
+
+ * doc/man/man1/ld.1: rewrite man page
+ * src/Options.h: add warnObsolete()
+ * src/Options.cpp: use warnObsolete() on many options. Make nonWeak the weak-mis-match default.
+ Make -ObjC mean -all_load.
+
+----- Tagged ld64-68.3
+
+2006-12-05 Nick Kledzik <kledzik@apple.com>
+
+ * src/ld.cpp: allow umbrella frameworks to have variant install names (e.g. CoreServices_profile) and still link
+
+
+----- Tagged ld64-68.2
+
+2006-12-05 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOWriterExecutable.cpp: Use N_PBUD in the symbol table for undefined symbols in prebound dylibs
+
+
+----- Tagged ld64-68.1
+
+2006-12-01 Nick Kledzik <kledzik@apple.com>
+
+ * src/Options.cpp: always generate module tables for 32-bit architectures so that ld_classic
+ can link against them
+
+
+----- Tagged ld64-68
+
+2006-12-01 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4858299> seg_addr_table needs matching fuzziness
+ * src/Options.cpp: special case a how a dozen dylib are looked up in the seg_addr_table
+
+
+2006-12-01 Nick Kledzik <kledzik@apple.com>
+
+ * src/Options.cpp: have all -static links for 32-bit archs roll over to ld_classic unless
+ LD_NO_CLASSIC_LINKER_STATIC is set.
+ * unit-tests/bin/make-recursive.pl: set LD_NO_CLASSIC_LINKER_STATIC for unit tests
+
+
+2006-11-29 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4855542> ld64-67: QTComponents fails to build
+ * src/MachOReaderRelocatable.hpp: don't error out when a local non-lazy pointer does not point to a symbol
+ * unit-tests/test-cases/strip_local: added test case
+
+
+2006-11-28 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4433496> Need a way to mark libraries usable by dynamic linker but unusable by static linker
+ * src/Options.cpp: allow -client_name to be used with main executables
+ * src/ld.cpp: generalize -allowable_client. Any dylib can now restrict who can link against it. As a convention
+ linking with -allowable_client '!' will mean no one can statically link with the dylib. It can still be loaded
+ dynamically, or by any existing clients, but no new clients can link with it.
+ * unit-tests/test-cases/allowable-client/Makefile: enable previously commented out test cases. Add test cases
+ of a dylib that allows no clients and just one client
+
+2006-11-27 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4795615> -final_output should be used if -install_name not used
+ * src/Options.cpp: fall back to using -final_output for install name
+
+
+----- Tagged ld64-67
+
+2006-11-17 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOWriterExecutable.hpp: support __IMPORT segment being slide independently of __DATA segment in shared cache
+
+
+2006-11-16 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4838262> 9a303: ld -filelist Bus Error
+ * src/Options.cpp: add check that -filelist is followed by an argument
+
+
+2006-11-16 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOWriterExecutable.hpp: when building split-seg dylibs, LINKEDIT goes in read-only side
+
+
+2006-11-15 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOWriterExecutable.hpp: set proper attributes for __eh_frame in ld -r mode
+ * unit-tests/test-cases/eh_frame: added test case
+
+
+2006-11-10 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: redirect references to static weak stubs to the real target
+
+
+2006-11-09 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOWriterExecutable.hpp: r_address is offset from first LC_SEGMENT vmaddr - not from segment with lowest address
+
+
+----- Tagged ld64-66.1
+
+2006-11-09 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOWriterExecutable.hpp: initialize fModuleInfoAtom to zero
+
+
+2006-11-08 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4821985> FSF GCC's libjava doesn't link with Ochre ld64
+ * src/MachOReaderRelocatable.hpp: ignore debug_line section if debug_info section is missing or empty
+
+----- Tagged ld64-66
+
+2006-11-07 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4824368> SWB: d64-65 does not built usage split-seg dylibs
+ * src/MachOWriterExecutable.hpp: when prebinding split-seg correctly set r_address fields and on
+ disk values for external relocations
+ * unit-tests/test-cases/prebound-split-seg: added test case
+
+
+2006-11-03 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderDylib.hpp: don't report dependent libraries if MH_NO_REEXPORTED_DYLIBS bit is set
+ * src/MachOWriterExecutable.hpp: set MH_NO_REEXPORTED_DYLIBS bit if dylib does not logically re-export any other dylibs
+ * unit-tests/test-cases/re-export-flag: added test case
+ * src/machochecker.cpp: validate use of MH_NO_REEXPORTED_DYLIBS
+
+
+2006-11-02 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4814565> Mysterious messages from ld64 with MACOSX_DEPLOYMENT_TARGET = 10.5
+ * src/MachOWriterExecutable.hpp: kPointerWeakImport is a valid reference type to cross segments
+
+
+2006-11-02 Nick Kledzik <kledzik@apple.com>
+
+ * src/Options.cpp,h: Add support for -rpath
+ * src/MachOFileAbstraction.hpp: add macho_rpath_command
+ * src/MachOWriterExecutable.hpp: add RPathLoadCommandsAtom to create LC_RPATH for each -rpath
+
+
+----- Tagged ld64-65
+
+2006-10-30 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4729162> x86_64 default stack_addr is wrong
+ * src/Options.cpp: change default 64-bit stack location when using -stack_size
+
+
+2006-10-30 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4474316> dylibs need modules for 10.3 and for ld_classic in Salt
+ * src/MachOWriterExecutable.hpp: add ModuleInfoLinkEditAtom to create module table stuff
+ * src/Options.cpp,h: Add needsModuleTable()
+ * src/MachOFileAbstraction.hpp: Add macho_dylib_module, macho_dylib_reference, and macho_dylib_table_of_contents
+
+
+2006-10-27 Nick Kledzik <kledzik@apple.com>
+
+ * unit-tests/test-cases/no-uuid/Makefile: add -gstabs+ to be compatible with latest compiler
+ * unit-tests/test-cases/stabs-coalesce/Makefile: add -gstabs+ to be compatible with latest compiler
+
+
+2006-10-26 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4585230> i386 -mdynamic-no-pic switch statement jump table is out of line
+ * src/MachOWriterExecutable.hpp: for i386 don't check for direct references to weak symbols
+
+
+2006-10-26 Devang Patel <dpatel@apple.com>
+
+ * src/LLVMReader.hpp: Supply final output file path to optimizer.
+
+2006-10-26 Devang Patel <dpatel@apple.com>
+
+ * src/ObjectFile.h: Make setSection* methods virtual.
+ * src/LLVMReader.hpp: Override setSection* methods.
+
+2006-10-26 Devang Patel <dpatel@apple.com>
+
+ * unit-tests/test-case/llvm-integration/a13.h: New.
+ * unit-tests/test-case/llvm-integration/a13.cc: New.
+ * unit-tests/test-case/llvm-integration/main13.cc: New.
+
+2006-10-26 Devang Patel <dpatel@apple.com>
+
+ * src/options.h, src/options.cpp: Add -save-temps command line option.
+ * src/LLVMReader.hpp: Use saveTemps option.
+
+
+2006-10-26 Devang Patel <dpatel@apple.com>
+
+ * src/LLVMReader.hpp: Remove invalid module from memory.
+
+2006-10-26 Devang Patel <dpatel@apple.com>
+
+ * src/LLVMReader.hpp: Collect symbol alignment info from LLVM optimizer.
+
+2006-10-21 Eric Christopher <echristo@apple.com>
+
+ * src/ld.cpp (Linker::Linker): Check for LD_NO_CLASSIC_LINKER before
+ invoking ld_classic.
+ * unit-tests/test-cases/relocs-literals/Makefile: Run for -mdynamic-no-pic
+ and pic.
+ * unit-tests/test-cases/static-executable/Makefile: Skip for 64-bit. Add
+ -dead_strip to command line.
+
+----- Tagged ld64-64.2
+
+2006-10-19 Nick Kledzik <kledzik@apple.com>
+
+ * ld64.xcodeproj/project.pbxproj: stop copying LLVMReader.hpp into man1 directory
+
+----- Tagged ld64-64.1
+
+2006-10-19 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4791643> ld64-63.1 erroneously coalesces an empty string with a non-empty string
+ * src/MachOReaderRelocatable.hpp: rework cstring parsing to not assume all strings are start
+ at section alignment boundaries, and when coalescing empty strings always use one with greatest
+ alignment requirement
+ * src/MachOWriterExecutable.hpp: in -r mode, don't pad end of cstring section
+ * src/ObjectFile.h: correctly name leadingZeros() as trailingZeros()
+ * src/ld.cpp: leadingZeros() --> trailingZeros()
+
+
+2006-10-18 Eric Christopher <echristo@apple.com>
+
+ * unit-tests/test-cases/read-only-relocs/Makefile: Skip for x86_64.
+ * unit-tests/test-cases/llvm-integration/Makefile: Skip if llvm isn't
+ present.
+
+2006-10-18 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4783853> ld64 change required to go with assembler cstring change
+ <rdar://problem/4732996> ld64 should error when a local relocation references an address outside its section
+ * src/MachOReaderRelocatable.hpp: for x86_64 in order to work with local or external relocations to cstrings
+ change parser to allow atoms with a pending name that is resolved after references are instantiated.
+ Make direct references to kRegularDefinition atoms.
+ * src/MachOWriterExecutable.hpp: in -r mode for x86_64 generate L* labels for cstrings and use external relocations
+ * unit-tests/test-cases/relocs-literals/test.c: add two cases of cstring literal plus addend
+
+
+2006-10-06 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4786250> check MACOSX_DEPLOYMENT_TARGET if -macosx_version_min is not used
+ * src/Options.cpp: if -macosx_version_min is not used, check MACOSX_DEPLOYMENT_TARGET, if
+ that is unused, default to 10.5
+
+----- Tagged ld64-64
+
+2006-10-06 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4756806> crash in ppc64 program - bl to saveFP, but saveFP is too far away?
+ * src/MachOWriterExecutable.hpp: in addPPCBranchIslands(), properly account for growth of __text
+
+
+2006-10-06 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4769120> Linker-defined alias converts reference into definition and generates error.
+ * src/MachOReaderRelocatable.hpp: only alias symbols actually in the symbol table
+
+
+2006-10-06 Nick Kledzik <kledzik@apple.com>
+
+ * unit-tests/test-cases/dwarf-debug-notes/Makefile: crt1.o no longer has stabs, so don't need to strip it
+ * unit-tests/test-cases/dwarf-debug-notes-r/Makefile: crt1.o no longer has stabs, so don't need to strip it
+
+
+2006-10-06 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: rework dwarf line parsing to fix warnings that starting
+ showing up with gcc-5421
+
+
+2006-10-05 Eric Christopher <echristo@apple.com>
+
+ <rdar://problem/4760935> ld64 needs to support libtool options
+ * src/Options.cpp (Options::parse): Add -noall_load, -install_name,
+ -current_version and -compatibility_version.
+
+2006-10-03 Eric Christopher <echristo@apple.com>
+
+ * src/Options.cpp (Options::gotoClassicLinker): Use execvp
+ to call ld_classic.
+
+2006-10-03 Eric Christopher <echristo@apple.com>
+
+ * unit-tests/test-cases/tentative-to-real/Makefile: Clean up after tests.
+
+2006-10-03 Eric Christopher <echristo@apple.com>
+
+ * unit-tests/include/common.makefile (VALID_ARCHS): Add x86_64.
+ (OTOOL): Remove munging based on ARCH.
+
+2006-09-29 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4743925> problem merging .o files built with and without -fno-common
+ src/Options.*: make MakeTentativeDefinitionsReal a reader option
+ src/ObjectFile.h: make MakeTentativeDefinitionsReal a reader option
+ src/MachOWriterExecutable.hpp: make MakeTentativeDefinitionsReal a reader option
+ src/MachOReaderRelocatable.hpp: only assign a section name of __common to
+ tentative defintions when making a final linked image
+
+
+2006-09-28 Nick Kledzik <kledzik@apple.com>
+
+ src/Options.h/.cpp: add support for -segaddr option
+ src/MachOWriterExecutable.hpp: In Writer::assignFileOffsets(), use -segaddr info
+
+
+2006-09-28 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4587349> Emit new CPU subtypes for ppc64 and x86-64 when targeting 10.5 or later
+ src/MachOWriterExecutable.hpp: set high bit of cpusubtype of 64-bit main executables when targeting 10.5 or later
+
+
+2006-09-28 Devang Patel <dpatel@apple.com>
+
+ Add LLVM LTO support
+ src/LLVMReader.hpp: New file.
+ src/ld.cpp: Add optimization phase. Use LLVM LTO.
+ unit-tests/test-cases/llvm-integration: New tests.
+
+2006-09-27 Nick Kledzik <kledzik@apple.com>
+
+ ld64.xcodeproj/project.pbxproj: remove accidental install of source file into man1
+
+
+2006-09-25 Nick Kledzik <kledzik@apple.com>
+
+ src/Architectures.hpp: add kPointerDiff16 for ppc and ppc64
+ src/MachOReaderRelocatable.hpp: support kPointerDiff16
+ src/MachOWriterExecutable.hpp: support kPointerDiff16
+
+----- Tagged ld64-63.1
+
+2006-09-22 Nick Kledzik <kledzik@apple.com>
+
+ src/MachOWriterExecutable.hpp: include stubs in LC_SEGMENT_SPLIT_INFO
+
+
+2006-09-21 Nick Kledzik <kledzik@apple.com>
+
+ src/Options.cpp: disable split-seg dylibs for 64-bit architectures
+
+
+2006-09-19 Nick Kledzik <kledzik@apple.com>
+
+ src/MachOReaderRelocatable.hpp: rework __cstring parsing to better handle mixed alignment cstrings
+ src/MachOWriterExecutable.hpp: in -r mode, make all __cstrings aligned to section alignment
+
+
+2006-09-19 Nick Kledzik <kledzik@apple.com>
+
+ src/MachOWriterExecutable.hpp: rework encoding of LC_SEGMENT_SPLIT_INFO
+
+
+2006-09-19 Nick Kledzik <kledzik@apple.com>
+
+ src/Options.cpp: check for -search_paths_first in first pass
+
+
+----- Tagged ld64-63
+
+2006-09-15 Nick Kledzik <kledzik@apple.com>
+
+ src/Options.cpp: since the ld64 will repeatedly search an archive, and some project list archives
+ multiple times on command line to work with traditional linkers, automatically ignore duplicate libraries
+ unit-tests/test-cases/archive-duplicate: added test case
+
+
+2006-09-15 Nick Kledzik <kledzik@apple.com>
+
+ src/Options.cpp: support -r -static
+ src/MachOWriterExecutable.hpp: support -r -static an don't generate LC_DYSYMTAB
+
+
+2006-09-14 Nick Kledzik <kledzik@apple.com>
+
+ src/MachOWriterExecutable.hpp: in -r mode references to weak symbols should not create external relocations
+ as that can cause nmedit to errror later.
+
+
+2006-09-13 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4718189> ld64: Handle .objc_class_name exports specially
+ src/Options.cpp: add hack so that .objc_class_name_XXX in -exported_symbols_list imples _OBJC_CLASS_$_XXX
+ src/ld.cpp: add hack to supporess errors about .objc_class_name_XXX or _OBJC_CLASS_$_XXX being undefined
+
+
+2006-09-12 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4474172> Support -prebind when targeting ppc and OS < 10.4
+ src/Options.h: add splitSeg() and baseWritableAddress()
+ src/Options.cpp: Add support for -seg_addr_table and LD_SEG_ADDR_TABLE, and -prebind and LD_PREBIND.
+ src/src/MachOWriterExecutable.hpp: support split-seg and canonical prebound files to be generated
+
+
+2006-09-11 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4464904> Linking a dylib or binary from identical binaries should produce the same output
+ src/MachOWriterExecutable.hpp: set the timestamps to be constant
+
+
+2006-09-11 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4070025> Linker support for ordering all sections and symbols
+ src/Options.cpp: Add -order_file_statistics. Allow architecture prefixes in order files
+ src/ld.cpp: Use fOptions.printOrderFileStatistics()
+
+
+2006-09-11 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/3894079> Support -sectorder
+ unit-tests/test-cases/order_file: added test case
+ src/ld.cpp: Implement order file support in Linker::sortAtoms()
+ src/Options.h: add Options.orderedSymbols()
+ src/Options.cpp: add parseOrderFile(), implement -order_file
+
+
+2006-09-07 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4637023> need -i for 64-bit (or equivalent)
+ <rdar://problem/4014529> Support -i for aliasing exported symbols
+ unit-tests/test-cases/alias-objects: added
+ unit-tests/test-cases/alias-command-line: added
+ src/ObjectFile.h: Added Atom::getOrdinal() as new way to sort atoms. Added ReaderOptions.fAliases
+ src/MachOReaderRelocatable.hpp: Added SymbolAliasAtom to handle multiple symbols to same address
+ src/MachOReaderArchive.hpp: implement Atom::getOrdinal() to space out atom ordinals across member objects
+ src/Options.cpp: support -i, -alias, -alias_list. Move search of /Network/Library/Frameworks to after /System/Library/Frameworks
+ src/MachOWriterExecutable.hpp: pad out seg_info data. Implement getOrdinal().
+ src/ObjectDump.cpp: call constructors directly instead of using make() wrapper
+
+
+2006-09-01 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4458878> Need the ability to tag libraries/plug-ins with security attributes
+ src/MachOReaderDylib.hpp: add warning if using -root_safe or -setuid_safe and link against dylib that is not
+ src/ObjectFile.h: add ReaderOption fRootSafe and fSetuidSafe
+ src/Options.cpp: handle -root_safe or -setuid_safe command line options
+ src/MachOWriterExecutable.hpp: set MH_ROOT_SAFE and MH_SETUID_SAFE flags
+
+
+2006-08-31 Nick Kledzik <kledzik@apple.com>
+
+ src/ld.cpp: Add Linker::processDTrace() for processing dtrace static probes
+ src/OpaqueSection.hpp: renamed, add symbol name, add ability to add references
+ ld64.xcodeproj/project.pbxproj: remove SectCreate.cpp, add OpaqueSection.hpp
+
+
+2006-08-28 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4571042> Add convention for removing symbols at link time
+ <rdar://problem/3962731> Assembler -L option causes ld64 to split stubs
+ unit-tests/test-cases/special-labels: added test case
+ src/MachOReaderRelocatable.hpp: ignore L* labels, make l* labels as kSymbolTableNotIn
+
+
+2006-08-28 Nick Kledzik <kledzik@apple.com>
+
+ src/lObjectFile.h: refactor isTargetUnbound() into getTargetBinding()
+ src/ld.cpp: create __dof section in final linked images from dtrace static probes
+ src/Architectures.hpp: add kDtraceProbe
+ src/Options.h/cpp: Add support for -dtrace
+ src/machochecker.cpp: support LC_SEGMENT_SPLIT_INFO
+ src/MachOWriterExecutable.hpp: support kDtraceProbe
+ src/MachOReaderRelocatable.hpp: suppport kDtraceProbe
+
+
+2006-08-25 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4701529> generate LC_SEGMENT_SPLIT_INFO for 10.5 or later dylibs
+ src/Options.h&.cpp: implement sharedRegionEligible() to control when LC_SEGMENT_SPLIT_INFO is added
+ src/MachOFileAbstraction.hpp: add macho_linkedit_data_command
+ src/MachOWriterExecutable.hpp: generate LC_SEGMENT_SPLIT_INFO load command and linkedit content
+
+----- Tagged ld64-62
+
+2006-08-15 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4681062> wrong error message when symbol is found in unused indirect library
+ src/ld.cpp: remove indirect libraries if they are not re-exported
+ unit-tests/test-cases/indirect-dylib: added test case
+
+
+2006-08-15 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/3930461> alignment needs to be richer
+ src/ObjectFile.h: define ObjectFile::Alignment class for tracking rich alignment info
+ src/ld.cpp: modify SymbolTable::add() to work with new Alignment type
+ src/MachOReaderRelocatable.hpp: use new Alignment type. Remove alignAtLeast() and handleAnonymousNonLazyPointers()
+ src/MachOWriterExecutable.hpp: update for new Alignment type, use modulus when calculating layout address
+ src/ObjectDump.cpp: print richer Alignment info
+ unit-tests/test-cases/align-modulus: added test case
+
+
+2006-08-11 Nick Kledzik <kledzik@apple.com>
+
+ remove OPEN_SOURCE conditionals around x86_64 support
+
+
+2006-07-31 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4654131> ld64 while linking cc1 [ when dead_strip is ON]
+ src/ld.cpp: Add ivar fAtomsWithUnresolvedReferences to track atoms not initially resolvable
+ unit-tests/test-cases/dead_strip-archive: added test case
+
+
+2006-07-31 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4656617> x86_64: instructions with immediate and rip-relative operands need to use new relocation types
+ src/MachOWriterExecutable.hpp: generate new reloc types in -r mode
+ src/MachOReaderRelocatable.hpp: parse new reloc types
+ unit-tests/test-cases/relocs-asm/relocs-asm.s: add test cases for new reloc type
+
+
+2006-07-18 Nick Kledzik <kledzik@apple.com>
+
+ src/MachOReaderRelocatable.hpp: suppress warning about dwarf info parsing for one benign no-op case
+ the compiler emits when there are not functions in the __text section
+
+
+2006-07-17 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4634840> faster debug note generation
+ src/ld.cpp: rework collectDebugInfo() to produce all debug notes in one pass, intead of a
+ pass per .o file. Added timing info for collectDebugInfo() to -print_statistics
+ unit-tests/test-cases/dwarf-debug-notes-r/Makefile: add expliced -arch to ld -r
+ unit-tests/test-cases/dwarf-debug-notes-r/expected-stabs: alter for new debug notes order
+
+
+2006-07-17 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4623994> ld64 VSIZE is 1.18GB when building Finder ppc64
+ src/ld.cpp: fixed typo in createReader() that prevented dylibs from being unmapped
+
+----- Tagged ld64-61.1
+
+2006-07-11 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4622049> ld64-61: gcc DejaGnu tests failing due to -arch followed by unknown architecture name
+ src/Options.cpp: map ppc750, ppc7400, ppc7450, and ppc970 to ppc. Improve error message
+
+2006-07-11 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4622769> If -arch is missing, rollover to ld_classic does not happen
+ src/Options.h: make gotoClassicLinker() public
+ src/ld.cpp: call gotoClassicLinker() if the inferred architecture is ppc or i386
+
+----- Tagged ld64-61
+
+2006-06-29 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4606628> ld64 should be renamed to ld
+ src/Options.cpp: exec() ld_classic if -arch ppc or -arch i386 is seen
+ src/ld.cpp: alter version string
+ ld64.xcodeproj/project.pbxproj: change install location to /usr/bin/ld, add symlink from /usr/bin/ld64
+ doc/man/man1/ld.1: added
+
+----- Tagged ld64-60
+
+2006-06-28 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4604539> Can't link large ppc64 program: ld64 says "bl out of range"
+ MachOWriterExecutable.hpp: fix branch island generation to work for weak_import functions
+ and properly chain together branch islands
+ MachOReaderRelocatable.hpp: improve performance of huge .o file reading by sorted references
+ only when done
+
+2006-06-28 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4603454> MySQL-36 fails to build with ld64-59
+ src/MachOReaderRelocatable.hpp: back out fix for 4585335
+ src/MachOWriterExecutable.hpp: back out fix for 4585335
+
+2006-06-27 Nick Kledzik <kledzik@apple.com>
+
+ src/MachOReaderRelocatable.hpp: handle N_GSYM without ending :G() since that is how
+ dwarf debug notes are formed.
+
+2006-06-23 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4599239 objc class with no superclass causes bad undefined symbol
+ src/MachOReaderRelocatable.hpp: handle NULL superclass in objc_class
+ unit-tests/test-cases/relocs-objc/test.m: add case with no super class
+
+
+2006-06-23 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4313369> ld64 doesn't support variant linking -framework fw,_debug
+ src/Options.cpp: enhance findFramework() to support suffixes
+
+----- Tagged ld64-59
+
+2006-06-22 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4596726> ld64 lost DWARF debug notes
+ src/MachOReaderRelocatable.hpp: add fHasUUID so kDebugInfoStabsUUID can be set later
+ unit-tests/test-cases/dwarf-debug-notes-r: added test case
+
+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
+
+2006-06-21 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 should not increment address
+
+
+----- Tagged ld64-58
+
+2006-06-16 Nick Kledzik <kledzik@apple.com>
+
+ src/rebase.cpp: fix page alignment problem
+ src/rebase.cpp: fix endianess problem with local non-lazy pointers
+
+2006-06-15 Nick Kledzik <kledzik@apple.com>
+
+ src/rebase.cpp: fix to build in CurryWeed
+ ld64.xcodeproj/project.pbxproj: fix to build properly in CurryWeed
+
+2006-06-15 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
+
+2006-06-15 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4484369> SECTION_ATTRIBUTES unset in ppc64 mach-o header
+ src/MachOWriterExecutable.hpp: add section attribute for sections with code
+
+2006-06-15 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4569407> ld64 bogus duplicate symbol name linking GNU libobjc
+ src/MachOReaderRelocatable.hpp: only special case Apple objc runtime objc classes
+
+2006-06-15 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4582999> x86_64: ".align" directive not honored
+ src/MachOReaderRelocatable.hpp: change code alignment to not depend on atom size
+
+2006-06-14 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: do not error on absolute references to interior of weak symbols
+
+2006-06-13 Nick Kledzik <kledzik@apple.com>
+
+ src/Options.cpp: allow -image_base as an alias for -seg1addr
+
+2006-06-13 Nick Kledzik <kledzik@apple.com>
+
+ <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-06-13 Nick Kledzik <kledzik@apple.com>
+
+ <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-06-12 Nick Kledzik <kledzik@apple.com>
+
+ <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-06-10 Nick Kledzik <kledzik@apple.com>
+
+ <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-06-10 Nick Kledzik <kledzik@apple.com>
+
+ src/machochecker.cpp: add some ppc reloc sanity checking
+
+----- Tagged ld64-57
+
+2006-06-06 Nick Kledzik <kledzik@apple.com>
+
+ <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>
+
+ <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
+
+2006-06-04 Eric Christopher <echristo@apple.com>
+
+ Radar 4560240
+ Radar 3964999
+ * src/ld.cpp (createReader): Fixed error message.
+ (resolve): Ditto.
+ (resolveFrom): Ditto.
+ (checkUndefines): Ditto.
+
+----- Tagged ld64-56
+
+2006-05-23 Nick Kledzik <kledzik@apple.com>
+
+ <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
+
+2006-05-22 Nick Kledzik <kledzik@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
+
+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-05-19 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4544001> ld64 does not honor -arch_multiple
+ * ld.cpp: If fOptions.printArchPrefix(), add architecture name to error message
+
+2006-05-19 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4555973> Support S_16BYTE_LITERALS section types
+ * src/MachOReaderRelocatable.hpp: support S_16BYTE_LITERALS
+ * src/MachOWriterExecutable.hpp: support S_16BYTE_LITERALS
+
+2006-05-19 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4548803> "warning can't parse dwarf compilation unit info" warnings building debug
+ * src/MachOReaderRelocatable.hpp: fix bugs in dwarf line table parsing
+
+----- Tagged ld64-55
+
+2006-05-18 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
+
+2006-05-18 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
+
+2006-05-18 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4553555> ld64: .section defaults to read-only
+ * src/MachOReaderRelocatable.hpp: default unknown segments to r/w
+
+2006-05-18 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4551990> -fvisibility=hidden causes crashes for x86_64
+ * src/MachOWriterExecutable.hpp: properly handle RIP relative tentative definitions
+
+2006-05-12 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
+
+----- Tagged ld64-54
+
+2006-05-11 Nick Kledzik <kledzik@apple.com>
+
+ <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-05-11 Nick Kledzik <kledzik@apple.com>
+
+ <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-05-10 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4543754> x86_64: .objc_class_name symbol names scrambled
+ * src/MachOReaderRelocatable.hpp: properly compute alignment of __OBJC __class sections
+
+
+2006-05-08 Nick Kledzik <kledzik@apple.com>
+
+ <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
+
+----- Tagged ld64-53
+
+2006-05-05 Nick Kledzik <kledzik@apple.com>
+
+ * src/Options.cpp: make 10.4 be minimum OS version for newer architectures
+
+2006-05-05 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
+
+2006-05-05 Nick Kledzik <kledzik@apple.com>
+
+ * MachOWriterExecutable.hpp: fix end FUN stab to have length of function
+
+
+2006-05-02 Nick Kledzik <kledzik@apple.com>
+
+ <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-05-02 Nick Kledzik <kledzik@apple.com>
+
+ * src/Opptions.cpp: remove warning about -stack_addr not specified. Add warning if 32-bit stack
+ overlaps shared region
+
+----- Tagged ld64-52.1
+
+2006-05-01 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.cpp: rework handleAnonymousNonLazyPointers() to handle anl's in the middle
+ the __data section too.
+
+----- Tagged ld64-52
+
+2006-04-28 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4513304> 64-bit: 9A152 TextEdit crashes in dlopen on bring-up
+ * src/MachOReaderRelocatable.cpp: rework anonymous non-lazy-pointer detection
+
+2006-04-28 Nick Kledzik <kledzik@apple.com>
+
+ <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-04-21 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
+
+
+2006-04-14 Nick Kledzik <kledzik@apple.com>
+
+ * src/Options.cpp: fix -sub_library processing to work it dylib is specifed with leaf name
+
+----- Tagged ld64-51.1
+
+2006-04-13 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
+
+
+----- Tagged ld64-51
+
+2006-04-11 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/4499168> exported symbols not properly stripped
+ * src/MachOReaderRelocatable.hpp: enable AnonymousAtom::setScope()
+
+2006-03-31 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
+
+
+2006-03-31 Nick Kledzik <kledzik@apple.com>
+
+ <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
+
+
+----- Tagged ld64-50
+
+
+2006-03-29 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: synthesize .objc_class_name symbols
+ * src/MachOFileAbstraction.hpp: use strncpy for sect/seg names to zero fill trailing space
+
+2006-03-28 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: fix spurious warning about dwarf line info
+
+----- Tagged ld64-49.1
+
+2006-03-25 Nick Kledzik <kledzik@apple.com>
+
+ * MachOWriterExecutable.hpp : don't complain about ppc64 dyld being based > 4GB
+
+----- Tagged ld64-49
+
+2006-03-24 Nick Kledzik <kledzik@apple.com>
+
+ * 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
+
+2006-03-23 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOWriterExecutable.hpp: in Writer<x86>::fixUpReferenceFinal() fix when x86::kPointer is for an
+ external relocation
+
+2006-03-23 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
+
+----- Tagged ld64-48
+
+2006-03-21 Nick Kledzik <kledzik@apple.com>
+
+ <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.
+
+2006-03-21 Nick Kledzik <kledzik@apple.com>
+
+ <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
+
+2006-03-21 Nick Kledzik <kledzik@apple.com>
+
+ <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
+
+
+----- Tagged ld64-47.1
+
+
+----- Tagged ld64-47
+
+
+----- Tagged ld64-46
+
+2006-03-10 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
+
+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
+
+
+----- 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-02-24 Nick Kledzik <kledzik@apple.com>
+
+ * src/Options.cpp: <rdar://problem/4457078> 9A110: ld64 can't deal with section names >16 chars
+
+2006-02-23 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOWriterExecutable.hpp: use vector.reserve() to minimize re-allocations
+ * src/Options.cpp: use vector.reserve() to minimize re-allocations
+ * src/MachOReaderRelocatable.hpp: use vector.reserve() to minimize re-allocations
+ * src/MachOReaderDylib.hpp: use vector.reserve() to minimize re-allocations
+ * src/ld.cpp: use vector.reserve() to minimize re-allocations
+
+2006-02-23 Nick Kledzik <kledzik@apple.com>
+
+ <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-02-23 Nick Kledzik <kledzik@apple.com>
+
+ <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-02-22 Nick Kledzik <kledzik@apple.com>
+
+ * src/Options.cpp: <rdar://problem/4453468> chnage printf on unknown arg to a throw
+
+----- Tagged ld64-39
+
+2006-02-20 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
+
+2006-02-20 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
+
+----- Tagged ld64-38
+
+2006-02-17 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOWriterExecutable.hpp: <rdar://problem/4434578> set correct n_sect field of stabs
+
+2006-02-15 Nick Kledzik <kledzik@apple.com>
+
+ * 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
+
+----- Tagged ld64-37
+
+2006-02-13 Eric Christopher <echristo@apple.com>
+
+ * src/MachOWriterExecutable.hpp (assignFileOffsets): Simplify. Add comments.
+ Adjust whitespace.
+
+2006-02-13 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOWriterExecutable.hpp: in Writer<x86>::fixUpReferenceRelocatable() fix kPCRel32 for external case
+
+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-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
+
+2006-02-02 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: properly coalesce 8-byte literals
+ * src/MachOWriterExecutable.hpp: support ppc64::kPointerDiff32
+
+----- Tagged ld64-32
+
+2006-02-02 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: support anonymous zero fill atoms
+
+2006-02-02 Nick Kledzik <kledzik@apple.com>
+
+ * src/ld.cpp: A weak definition is good enough, do not search archives for a non-weak one
+ * unit-tests/test-cases/archive-weak: add test case for above
+ * src/MachOReaderRelocatable.hpp: an atom should never have a by-name reference to itself
+ * src/Options.cpp: prevent .eh symbols from being exported via a -exported_symbols_list
+
+2006-02-01 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: Support -macosx_version_min 10.5
+
+2006-02-01 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: don't try to parse debug_line dwarf if no symboled atoms
+
+----- Tagged ld64-31
+
+2006-02-01 Eric Christopher <echristo@apple.com>
+
+ * unit-tests/test-cases/allow-stack-execute/Makefile: Move otool handling...
+ * unit-tests/include/common.makefile: ... here.
+ * unit-tests/bin/fail-if-stdin.pl: New.
+ * unit-tests/test-cases/no-uuid: Ditto.
+ * src/ld.cpp (Linker::) Add fCreateUUID.
+ (::Linker): Initialize.
+ (::collectStabs): Use. Set if dwarf or we have a UUID already.
+ (::writeOutput): Pass as argument to Writer::write along with option.
+ * src/Options.h (Option::emitUUID): Declare.
+ (Option::fEmitUUID): Ditto.
+ * src/Options.cpp (Option::emitUUID): New.
+ (parse): Handle -no_uuid.
+ * src/MachOReaderRelocatable (Reader::Reader): Handle LC_UUID.
+ * src/ExecutableFile.h (Writer::Write): Add createUUID boolean.
+ * src/MachOWriterExecutable: Add UUID forward declaration.
+ (fUUIDAtom): New.
+ (UUIDLoadCommandAtom): Emit LC_UUID if fEmit. New function emit. Size
+ to zero at start.
+ (Writer::writer): Add handle for LC_UUID. If createUUID emit LC_UUID.
+ (MachHeaderAtom::copyRawContent): Don't count a load command if its size is
+ 0.
+ (UUIDLoadCommandAtom::copyRawContent): Depend on fEmit.
+
+
+2006-01-31 Nick Kledzik <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
+
+2006-01-31 Nick Kledzik <kledzik@apple.com>
+
+ * ld64.xcodeproj/project.pbxproj : Make buildable on Leopard
+ * src/MachOFileAbstraction.hpp: make buildable without latest cctools headers
+
+2006-01-31 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
+
+2006-01-30 Eric Christopher <echristo@apple.com>
+
+ * src/ExecutableFile.h: Indent.
+
+2006-01-30 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: performance improvements
+ * src/ld.cpp: now that stubs are synthesized in write, don't need to special case anymore
+
+2006-01-30 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: fix parsing of pcc relocs
+ * unit-tests/test-cases/relocs-asm/relocs-asm.s: add test case for above
+
+2006-01-29 Nick Kledzik <kledzik@apple.com>
+
+ * unit-tests/test-cases/weak_import: added test case
+ * src/ld.cpp: move code for weak_import mismatch to writer
+ * src/ObjectFile.h: remove ImportWeakness methods
+ * src/MachOReaderDylib.hpp: ditto
+ * src/SectCreate.cpp: ditto
+ * src/Architectures.hpp: add new ReferenceKinds for weak_imports
+ * src/MachOReaderRelocatable.hpp: implement new ReferenceKinds
+ * src/MachOWriterExecutable.hpp: handle new ReferenceKinds and weak_import mismatches
+
+2006-01-29 Nick Kledzik <kledzik@apple.com>
+
+ * src/Options.cpp: verify -allow_stack_execute is only used on main executables
+
+2006-01-29 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: sync with latest dwarf reader from Geoff
+ * src/debugline.c: sync with latest dwarf reader from Geoff
+
+2006-01-27 Eric Christopher <echristo@apple.com>
+
+ * src/ld.cpp (Linker::syntesizeStabs): Correct spelling. Update all uses.
+
+2006-01-27 Eric Christopher <echristo@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.
+
+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-01-27 Eric Christopher <echristo@apple.com>
+
+ * 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>
+
+ * src/MachOWriterExecutable.hpp: handle NULL strings in SO debug notes
+
+2006-01-26 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOWriterExecutable.hpp: fix header padding calculation and thread state
+
+2006-01-26 Nick Kledzik <kledzik@apple.com>
+
+ Rewrite all stabs processing.
+ Move sythesize of debug notes into ld.cpp
+
+2006-01-26 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOWriterExecutable.hpp: fix ppc and ppc64 stub relocs
+
+2006-01-25 Nick Kledzik <kledzik@apple.com>
+
+ * ld64.xcodeproj/project.pbxproj: special case building in Curry
+
+2006-01-25 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOWriterExecutable.hpp: fix bugs in stub/lazy-pointer synthesis
+
+2006-01-24 Eric Christopher <echristo@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.
+
+2006-01-24 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: better C++ eh parsing
+
+2006-01-23 Eric Christopher <echristo@apple.com>
+
+ * unit-tests/bin/fail-if-exit-zero.pl: New.
+ * unit-tests/include/common.makefile (FAIL_IF_SUCCESS): Use.
+ * unit-tests/allowable-client: New test.
+ * src/ld.cpp (Linker::addDylib): Check allowable clients before adding dylib.
+ * src/Options.h (allowableClients): New.
+ (clientName): Ditto.
+ (fAllowableClients): Ditto.
+ (fClientName): Ditto.
+ * src/Options.cpp: Implement above.
+ (parse): Handle -allowable_client and -client_name.
+ * src/MachOReaderDylib.hpp (getAllowableClients): New.
+ (fAllowableClients): Ditto.
+ (Reader): Process LC_SUB_CLIENT load command.
+ * src/ObjectFile.h (parentUmbrella): New.
+ (getAllowableClients): New.
+ * src/MachOWriterExecutable.hpp (AllowableClientLoadCommandsAtom): New.
+
+2006-01-23 Nick Kledzik <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
+
+2006-01-23 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
+
+2006-01-17 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: tweaks to synthesizing debug notes
+
+2006-01-16 Nick Kledzik <kledzik@apple.com>
+
+ * src/debugline.{sh}: added
+ * src/MachOReaderRelocatable.hpp: synthesize debug notes SOL from dwarf
+ * src/MachOWriterExecutable.hpp: fix lazy pointer section
+ * src/ObjectDump.hpp: Fix conditionalization
+ * unit-tests/test-cases/dwarf-strip: added
+
+2006-01-11 Nick Kledzik <kledzik@apple.com>
+
+ * 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>
+
+ * src/MachOWriterExecutable.hpp: fix stubs for ppc64
+ * src/MachOFileAbstraction.hpp: fix typo for macho_routines
+ * ld64.xcodeproj/project.pbxproj: add machochecker target
+ * src/machochecker.cpp: new skeleton for checking mach-o file bit
+ * unit-tests/: Add support for running machochecker
+
+2006-01-10 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: warn if dwarf can't be parsed
+ * src/MachOReaderArchive.hpp: modTime for OSO stabs from archives is .a modTime
+
+2006-01-09 Nick Kledzik <kledzik@apple.com>
+
+ * track modification time of .o files so that sythesized OSO stab will have it
+
+2006-01-09 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOFileAbstraction.hpp: add macho_uuid_command
+ * src/MachOWriterExecutable.cpp: add UUID load command to generated files
+
+2006-01-09 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderDylib.hpp: no longer keep dylib memory mapped
+ * src/ld.cpp: don't track dylib sizes because they are not longer memory mapped
+
+2006-01-05 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: support new relocations
+
+2006-01-05 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderDylib.hpp: support MH_DYLIB_STUB
+ * src/MachOReaderRelocatable.hpp: Add Geoff's comp unit extractor
+
+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
+
+2006-01-03 Eric Christopher <echristo@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.
+
+2006-01-02 Nick Kledzik <kledzik@apple.com>
+
+ * Refactor: move Atom::writeContent() to Writer
+
+2005-12-23 Nick Kledzik <kledzik@apple.com>
+
+ * Reworked, simplify, and document test harness
+ * unit-tests/README: Added
+
+2005-12-23 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: fixes for Objective-C
+ * unit-tests/test-cases/relocs-objc: Added
+
+2005-12-22 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: fix check that next reloc is pair
+ * src/MachOReaderRelocatable.hpp: Add code to synthesize essential stabs from dwarf
+
+2005-12-21 Nick Kledzik <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
+
+2005-12-15 Eric Christopher <echristo@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/Options.cpp: Fix whitespace. Add comments on options.
+ (Options::Options): Add initializers for new variables.
+ (Options::prebind): New.
+ (Options::macosxVersionMin): Ditto.
+ (Options::parseTreatment): Ditto.
+ (Options::setVersionMin): Ditto.
+ (Options::setReadOnlyRelocTreatment): Delete.
+ (Options::setPICTreatment): Ditto.
+ (Options::Parse): Update for above. Add comments.
+
+2005-12-15 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: Add comments about dwarf
+
+2005-12-14 Nick Kledzik <kledzik@apple.com>
+
+ * src/ELFFileAbstraction.hpp: Added
+ * src/ELFReaderRelocatable.hpp: Added
+ * Lot of fixes for new architecture
+ * Added __OPEN_SOURCE__ to "Preprocessor Macros" to disable new architecture support by default
+
+2005-12-13 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOReaderRelocatable.hpp: check for S_ATTR_DEBUG and ignore those sections
+ * unit-tests/test-cases/dwarf-ignore: added
+
+2005-12-12 Nick Kledzik <kledzik@apple.com>
+
+ * Added test harness and three initial tests:
+ relocs-asm, relocs-c, and hello-world
+
+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.
+
+2005-12-12 Nick Kledzik <kledzik@apple.com>
+
+ * src/ObjectDump.cpp: Add command line options: -no_content, -stabs, -no_sort
+
+2005-12-11 Eric Christopher <echristo@apple.com>
+
+ * src/Options.cpp: Reformat.
+ * src/Options.h: Ditto.
+
+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.
+
+2005-12-06 Nick Kledzik <kledzik@apple.com>
+
+ * src/Options.cpp src/Options.h: Add design comments
+
+2005-12-05 Eric Christopher <echristo@apple.com>
+
+ * src/ld.cpp (Linker::createWriter): Uncomment ppc64 and
+ i386 linkers.
+
+2005-12-05 Eric Christopher <echristo@apple.com>
+
+ * ChangeLog: New file.
+
+2005-12-02 Nick Kledzik <kledzik@apple.com>
+
+ * src/ObjectFile.h: Add design comments
+
+2005-11-30 Nick Kledzik <kledzik@apple.com>
+
+ * Fix uses of __OPEN_SOURCE__
+
+2005-11-28 Nick Kledzik <kledzik@apple.com>
+
+ * Refactor Atom to use getDefinitionKind()
+
+2005-11-21 Nick Kledzik <kledzik@apple.com>
+
+ * src/MachOWriterExecutable.hpp: don't generate section for commons in -r mode
+
+2005-11-18 Nick Kledzik <kledzik@apple.com>
+
+ * x86 tweaks
+
+2005-11-18 Nick Kledzik <kledzik@apple.com>
+
+ * src/ObjectDump.cpp: make work with command line arguments
+
+2005-11-18 Nick Kledzik <kledzik@apple.com>
+
+ * Massive rework to remove preprocessor conditionals and use templates
+
+2005-11-14 Nick Kledzik <kledzik@apple.com>
+
+ * Created new Subversion repository for ld64 from cvs tag ld64-27.2
--- /dev/null
+.Dd November 20, 2008
+.Dt dyldinfo 1
+.Os Darwin
+.Sh NAME
+.Nm dyldinfo
+.Nd "Displays information used by dyld in an executable"
+.Sh SYNOPSIS
+.Nm
+.Op Fl arch Ar arch-name
+.Op Fl rebase
+.Op Fl bind
+.Op Fl weak_bind
+.Op Fl lazy_bind
+.Op Fl export
+.Op Fl opcodes
+.Ar file(s)
+.Sh DESCRIPTION
+Executables built for Mac OS X 10.6 and later have a new format for the
+information in the __LINKEDIT segment. The dyldinfo tool will display
+that information.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl arch Ar arch
+Only display the specified architecture. Other architectures in a universal image are ignored.
+.It Fl rebase
+Display the table of rebasing information. Rebasing is what dyld does when an image is
+not loaded at its preferred address. Typically, this involves updating pointers in the __DATA
+segment which are point within the image.
+.It Fl bind
+Display the table of binding information. These are the symbolic fix ups that dyld must
+do when an image is loaded.
+.It Fl weak_bind
+Display the table of weak binding information. Typically, only C++ progams will have any
+weak binding. These are symbols which dyld must unique accross all images.
+.It Fl lazy_bind
+Display the table of lazy binding information. These are symbols which dyld delays binding
+until they are first used. Lazy binding is automatically used for all function calls to
+functions in some external dylib.
+.It Fl export
+Display the table symbols which this image exports.
+.It Fl opcodes
+Display the low level opcodes used to encode all rebase and binding information.
+.El
+.Sh SEE ALSO
+.Xr otool 1
+.Xr nm 1
-.Dd December 8, 2006
+.Dd December 15, 2008
.Dt ld 1
.Os Darwin
.Sh NAME
Loads all members of static archive libraries.
.It Fl ObjC
Loads all members of static archive libraries that implement an Objective-C class or category.
+.It Fl force_load Ar path_to_archive
+Loads all members of the specified static archive library. Note: -all_load forces all members of all
+archives to be loaded. This option allows you to target a specific archive.
.El
.Ss Options that control additional content
.Bl -tag
.Ar file
must be a DTrace script which declares the static probes.
.El
-.Ss Options that control optimizations
+.Ss Options that control optimizations
.Bl -tag
.It Fl dead_strip
Remove functions and data that are unreachable by the entry point or exported symbols.
-.It Fl dead_strip_dylibs
-Remove dylibs that are unreachable by the entry point or exported symbols. That is,
-suppresses the generation of load command commands for dylibs which supplied no
-symbols during the link. This option should not be used when linking against a dylib which
-is required at runtime for some indirect reason such as the dylib has an important initializer.
.It Fl order_file Ar file
Alters the order in which functions and data are laid out. For each section in the output file,
any symbol in that section that are specified in the order file
A symbol name may also be optionally preceded with the architecture (e.g. ppc:_foo or ppc:foo.o:_foo).
This enables you to have one order file that works for multiple architectures.
Literal c-strings may be ordered by by quoting the string (e.g. "Hello, world\\n") in the order file.
+.It Fl no_order_inits
+When the -order_file option is not used, the linker lays out functions in object file order and
+it moves all initializer routines to the start of the __text section and terminator routines
+to the end. Use this option to disable the automatic rearrangement of initializers and terminators.
+.It Fl no_order_data
+By default the linker reorders global data in the __DATA segment so that all global variables that
+dyld will need to adjust at launch time will early in the __DATA segment. This reduces the number
+of dirty pages at launch time. This option disables that optimization.
.It Fl macosx_version_min Ar version
This is set to indicate the oldest Mac OS X version that that the output is to be used on. Specifying
a later version enables the linker to assumes features of that OS in the output file. The format of
command to load Foundation and encode the symbol as coming from Foundation. If you use this option,
the linker will not add a load command for Foundation and encode the symbol as coming from Cocoa. Then
at runtime dyld will have to search Cocoa and AppKit before finding the symbol in Foundation.
+.It Fl exported_symbols_order Ar file
+When targeting Mac OS X 10.6 or later, the format of the exported symbol information can be optimized to
+make lookups of popular symbols faster. This option is used to pass a file containing a list of
+the symbols most frequently used by clients of the dynamic library being built. Not all exported symbols
+need to be listed.
.El
-.Ss Options when creating a dynamic library (dylib)
+.Ss Options when creating a dynamic library (dylib)
.Bl -tag
.It Fl install_name Ar name
Sets an internal "install path" (LC_ID_DYLIB) in a dynamic library. Any clients linked against the library
will record that path as the way dyld should locate this library. If this option is not specified, then
the -o path will be used. This option is also called -dylib_install_name for compatibility.
+.It Fl mark_dead_strippable_dylib
+Specifies that the dylib being built can be dead strip by any client. That is, the dylib has
+no initialization side effects. So if a client links against the dylib, but never uses
+any symbol from it, the linker can optimize away the use of the dylib.
.It Fl compatibility_version Ar number
Specifies the compatibility version number of the library. When a library is loaded by dyld, the
compatibility version is checked and if the program's version is greater that the library's version, it is an error.
.Bl -tag
.It Fl v
Prints the version of the linker.
+.It Fl no_compact_linkedit
+Normally when targeting Mac OS X 10.6, the linker will generate compact information
+in the __LINKEDIT segment.
+This option causes the linker to instead produce traditional relocation information.
+.It Fl no_eh_labels
+Normally in -r mode, the linker produces .eh labels on all FDEs in the __eh_frame section.
+This option suppresses those labels. Those labels are not needed by the Mac OS X 10.6
+linker but are needed by earlier linker tools.
+.It Fl warn_compact_unwind
+When producing a final linked image, the linker processes the __eh_frame section and
+produces an __unwind_info section. Most FDE entries in the __eh_frame can be represented
+by a 32-bit value in the __unwind_info section. The option issues a warning for
+any function whose FDE cannot be expressed in the compact unwind format.
+.It Fl dead_strip_dylibs
+Remove dylibs that are unreachable by the entry point or exported symbols. That is,
+suppresses the generation of load command commands for dylibs which supplied no
+symbols during the link. This option should not be used when linking against a dylib which
+is required at runtime for some indirect reason such as the dylib has an important initializer.
+.It Fl allow_sub_type_mismatches
+Normally the linker consisders different cpu-subtype for ARM (e.g. armv4t and armv6) to be different
+different architectures that cannot be mixed at build time. This option relaxes that requirement,
+allowing you to mix object files compiled for different ARM subtypes.
.It Fl no_uuid
Do not generate an LC_UUID load command in the output file.
.It Fl root_safe
.It Fl segaddr Ar name Ar address
Specifies the starting address of the segment named name to be address. The address must be a hexadecimal number
that is a multiple of 4K page size.
+.It Fl seg_page_size Ar name Ar size
+Specifies the page size used by the specified segment. By default the page size is 4096 for all segments.
+The linker will lay out segments such that size of a segment is always an even multiple of its page size.
.It Fl dylib_file Ar install_name:file_name
Specifies that a dynamic shared library is in a different location than its standard location. Use this option
when you link with a library that is dependent on a dynamic library, and the dynamic library is in a location other
--- /dev/null
+.Dd November 7, 2008
+.Dt unwinddump 1
+.Os Darwin
+.Sh NAME
+.Nm unwinddump
+.Nd "Displays compact unwind information in an executable"
+.Sh SYNOPSIS
+.Nm
+.Op Fl arch Ar arch-name
+.Ar file(s)
+.Sh DESCRIPTION
+When a C++ (or x86_64 Objective-C) exception is thrown, the runtime must unwind
+the stack looking for some function to catch the exception. Traditionally,
+the unwind information is stored in the __TEXT/__eh_frame section of each executable
+as Dwarf CFI (call frame information). Beginning in Mac OS X 10.6, the unwind
+information is also encoded in the __TEXT/__unwind_info section using a two-level
+lookup table of compact unwind encodings.
+.Pp
+The unwinddump tool displays the content of the __TEXT/__unwind_info section.
+.Sh SEE ALSO
+.Xr ld 1
+.Xr dwarfdump 1
F96D536C094A275F008E9EE8 /* PBXTargetDependency */,
F96904890A4333AC00B77D2A /* PBXTargetDependency */,
F9EA73970974999B008B4F1D /* PBXTargetDependency */,
+ F9B693890EC4D28C00076912 /* PBXTargetDependency */,
);
name = "unit-tests";
productName = "unit-tests";
dependencies = (
F9B1A2690A3A568200DA8FAB /* PBXTargetDependency */,
F9B1A26B0A3A568400DA8FAB /* PBXTargetDependency */,
+ F9C12EEA0ED65765005BC69D /* PBXTargetDependency */,
+ F9B8135D0EC2620E00F94C13 /* PBXTargetDependency */,
+ F9A3DE160ED76D9A00C590B9 /* PBXTargetDependency */,
);
name = all;
productName = all;
/* Begin PBXBuildFile section */
F9023C4E06D5A272001BBF46 /* ld.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9023C3F06D5A254001BBF46 /* ld.cpp */; };
F933E3D9092E855B0083EAC8 /* ObjectDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F971EED706D5AD240041D381 /* ObjectDump.cpp */; };
- F97F5029070D0BB200B9FCD7 /* ld.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = F97F5028070D0BB200B9FCD7 /* ld.1 */; };
- F9B1A2640A3A563E00DA8FAB /* rebase.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = F9B1A2580A3A448800DA8FAB /* rebase.1 */; };
+ F97F5029070D0BB200B9FCD7 /* ld.1 in copy man page */ = {isa = PBXBuildFile; fileRef = F97F5028070D0BB200B9FCD7 /* ld.1 */; };
+ F9A3DDD30ED762E400C590B9 /* PruneTrie.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A3DDD20ED762E400C590B9 /* PruneTrie.cpp */; };
+ F9A3DE1E0ED7738300C590B9 /* prune_trie.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = F9A3DE0F0ED76D1900C590B9 /* prune_trie.h */; };
+ F9B1A2640A3A563E00DA8FAB /* rebase.1 in install man page */ = {isa = PBXBuildFile; fileRef = F9B1A2580A3A448800DA8FAB /* rebase.1 */; };
+ F9B670120DDA17E800E6D0DA /* UnwindDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B670110DDA17E800E6D0DA /* UnwindDump.cpp */; };
+ F9B813850EC2657800F94C13 /* unwinddump.1 in install man page */ = {isa = PBXBuildFile; fileRef = F9B813810EC2653000F94C13 /* unwinddump.1 */; };
+ F9BA51650ECE58C800D1D62E /* dyldinfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9BA515B0ECE58AA00D1D62E /* dyldinfo.cpp */; };
F9C0D4BD06DD28D2001C7193 /* Options.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9C0D48A06DD1E1B001C7193 /* Options.cpp */; };
+ F9C12EA30ED63DE7005BC69D /* dyldinfo.1 in install man page */ = {isa = PBXBuildFile; fileRef = F9C12E9F0ED63DB1005BC69D /* dyldinfo.1 */; };
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 */; };
- F9FCC3F20A54A75600CEB866 /* ld64.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = F9FCC3F10A54A75600CEB866 /* ld64.1 */; };
/* End PBXBuildFile section */
/* Begin PBXBuildRule section */
remoteGlobalIDString = F971EED206D5ACF60041D381;
remoteInfo = ObjectDump;
};
+ F9A3DE150ED76D9A00C590B9 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = F9023C3006D5A227001BBF46 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = F9A3DDC90ED762B700C590B9;
+ remoteInfo = libprunetrie;
+ };
F9B1A2680A3A568200DA8FAB /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F9023C3006D5A227001BBF46 /* Project object */;
remoteGlobalIDString = F9EC77ED0A2F85F6002A3E39;
remoteInfo = rebase;
};
+ F9B693880EC4D28C00076912 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = F9023C3006D5A227001BBF46 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = F9B670010DDA176100E6D0DA;
+ remoteInfo = unwinddump;
+ };
+ F9B8135C0EC2620E00F94C13 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = F9023C3006D5A227001BBF46 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = F9B670010DDA176100E6D0DA;
+ remoteInfo = unwinddump;
+ };
+ F9C12EE90ED65765005BC69D /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = F9023C3006D5A227001BBF46 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = F9BA51600ECE58BE00D1D62E;
+ remoteInfo = dyldinfo;
+ };
F9EA73960974999B008B4F1D /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F9023C3006D5A227001BBF46 /* Project object */;
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
- F97F5025070D0B6300B9FCD7 /* CopyFiles */ = {
+ F97F5025070D0B6300B9FCD7 /* copy man page */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 8;
dstPath = /usr/share/man/man1;
dstSubfolderSpec = 0;
files = (
- F97F5029070D0BB200B9FCD7 /* ld.1 in CopyFiles */,
- F9FCC3F20A54A75600CEB866 /* ld64.1 in CopyFiles */,
+ F97F5029070D0BB200B9FCD7 /* ld.1 in copy man page */,
);
+ name = "copy man page";
runOnlyForDeploymentPostprocessing = 1;
};
- F9B1A25E0A3A44CB00DA8FAB /* CopyFiles */ = {
+ F9A3DE140ED76D7700C590B9 /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 8;
+ dstPath = "/usr/local/include/mach-o";
+ dstSubfolderSpec = 0;
+ files = (
+ F9A3DE1E0ED7738300C590B9 /* prune_trie.h in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ F9B1A25E0A3A44CB00DA8FAB /* install man page */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 8;
dstPath = /usr/share/man/man1;
dstSubfolderSpec = 0;
files = (
- F9B1A2640A3A563E00DA8FAB /* rebase.1 in CopyFiles */,
+ F9B1A2640A3A563E00DA8FAB /* rebase.1 in install man page */,
+ );
+ name = "install man page";
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ F9B813870EC2659600F94C13 /* install man page */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 8;
+ dstPath = usr/share/man/man1;
+ dstSubfolderSpec = 0;
+ files = (
+ F9B813850EC2657800F94C13 /* unwinddump.1 in install man page */,
);
+ name = "install man page";
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ F9C12EA50ED63E05005BC69D /* install man page */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 8;
+ dstPath = usr/share/man/man1;
+ dstSubfolderSpec = 0;
+ files = (
+ F9C12EA30ED63DE7005BC69D /* dyldinfo.1 in install man page */,
+ );
+ name = "install man page";
runOnlyForDeploymentPostprocessing = 1;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
- 3DA587190ACC53BE0015C432 /* LTOReader.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = LTOReader.hpp; path = src/LTOReader.hpp; sourceTree = "<group>"; };
+ 3DA587190ACC53BE0015C432 /* LTOReader.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = LTOReader.hpp; path = src/ld/LTOReader.hpp; sourceTree = "<group>"; };
C02A29DE0953B26E001FB8C1 /* ChangeLog */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = ChangeLog; sourceTree = "<group>"; };
F9023C3906D5A23E001BBF46 /* ld */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ld; sourceTree = BUILT_PRODUCTS_DIR; };
- F9023C3E06D5A254001BBF46 /* ExecutableFile.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ExecutableFile.h; path = src/ExecutableFile.h; sourceTree = "<group>"; };
- F9023C3F06D5A254001BBF46 /* ld.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ld.cpp; path = src/ld.cpp; sourceTree = "<group>"; };
- F9023C4106D5A254001BBF46 /* ObjectFile.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ObjectFile.h; path = src/ObjectFile.h; sourceTree = "<group>"; };
- F933D9460929277C0083EAC8 /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = FileAbstraction.hpp; path = src/FileAbstraction.hpp; sourceTree = "<group>"; };
- F933D9470929277C0083EAC8 /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOFileAbstraction.hpp; path = src/MachOFileAbstraction.hpp; sourceTree = "<group>"; };
- F933DC37092A82480083EAC8 /* Architectures.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = Architectures.hpp; path = src/Architectures.hpp; sourceTree = "<group>"; };
- F933E3CC092E84250083EAC8 /* MachOReaderDylib.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOReaderDylib.hpp; path = src/MachOReaderDylib.hpp; sourceTree = "<group>"; };
- F933E3CD092E84250083EAC8 /* MachOReaderRelocatable.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOReaderRelocatable.hpp; path = src/MachOReaderRelocatable.hpp; sourceTree = "<group>"; };
- F933E3CE092E84250083EAC8 /* MachOWriterExecutable.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOWriterExecutable.hpp; path = src/MachOWriterExecutable.hpp; sourceTree = "<group>"; };
+ F9023C3E06D5A254001BBF46 /* ExecutableFile.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ExecutableFile.h; path = src/ld/ExecutableFile.h; sourceTree = "<group>"; };
+ F9023C3F06D5A254001BBF46 /* ld.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ld.cpp; path = src/ld/ld.cpp; sourceTree = "<group>"; };
+ F9023C4106D5A254001BBF46 /* ObjectFile.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ObjectFile.h; path = src/ld/ObjectFile.h; sourceTree = "<group>"; };
+ F933D9460929277C0083EAC8 /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = FileAbstraction.hpp; path = src/abstraction/FileAbstraction.hpp; sourceTree = "<group>"; };
+ F933D9470929277C0083EAC8 /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOFileAbstraction.hpp; path = src/abstraction/MachOFileAbstraction.hpp; sourceTree = "<group>"; };
+ F933DC37092A82480083EAC8 /* Architectures.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = Architectures.hpp; path = src/ld/Architectures.hpp; sourceTree = "<group>"; };
+ F933E3CC092E84250083EAC8 /* MachOReaderDylib.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOReaderDylib.hpp; path = src/ld/MachOReaderDylib.hpp; sourceTree = "<group>"; };
+ F933E3CD092E84250083EAC8 /* MachOReaderRelocatable.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOReaderRelocatable.hpp; path = src/ld/MachOReaderRelocatable.hpp; sourceTree = "<group>"; };
+ F933E3CE092E84250083EAC8 /* MachOWriterExecutable.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOWriterExecutable.hpp; path = src/ld/MachOWriterExecutable.hpp; sourceTree = "<group>"; };
F971EED306D5ACF60041D381 /* ObjectDump */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ObjectDump; sourceTree = BUILT_PRODUCTS_DIR; };
- F971EED706D5AD240041D381 /* ObjectDump.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ObjectDump.cpp; path = src/ObjectDump.cpp; sourceTree = "<group>"; };
+ F971EED706D5AD240041D381 /* ObjectDump.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ObjectDump.cpp; path = src/other/ObjectDump.cpp; sourceTree = "<group>"; };
F97F5028070D0BB200B9FCD7 /* ld.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = ld.1; path = doc/man/man1/ld.1; sourceTree = "<group>"; };
- F98D26850AA779BD00416316 /* OpaqueSection.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = OpaqueSection.hpp; path = src/OpaqueSection.hpp; sourceTree = "<group>"; };
- F99F63CE0D99A291007F5394 /* ArchiveReader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ArchiveReader.hpp; path = src/ArchiveReader.hpp; sourceTree = SOURCE_ROOT; };
+ F98D26850AA779BD00416316 /* OpaqueSection.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = OpaqueSection.hpp; path = src/ld/OpaqueSection.hpp; sourceTree = "<group>"; };
+ F99F63CE0D99A291007F5394 /* ArchiveReader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ArchiveReader.hpp; path = src/ld/ArchiveReader.hpp; sourceTree = "<group>"; };
+ F9A3DDCA0ED762B700C590B9 /* libprunetrie.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libprunetrie.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ F9A3DDD20ED762E400C590B9 /* PruneTrie.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PruneTrie.cpp; path = src/other/PruneTrie.cpp; sourceTree = "<group>"; };
+ F9A3DE0F0ED76D1900C590B9 /* prune_trie.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = prune_trie.h; path = src/other/prune_trie.h; 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>"; };
+ F9B670080DDA176100E6D0DA /* unwinddump */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = unwinddump; sourceTree = BUILT_PRODUCTS_DIR; };
+ F9B670110DDA17E800E6D0DA /* UnwindDump.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UnwindDump.cpp; path = src/other/unwinddump.cpp; sourceTree = "<group>"; };
+ F9B813810EC2653000F94C13 /* unwinddump.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = unwinddump.1; path = doc/man/man1/unwinddump.1; sourceTree = "<group>"; };
+ F9B813BF0EC27C6700F94C13 /* MachOTrie.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = MachOTrie.hpp; path = src/abstraction/MachOTrie.hpp; sourceTree = "<group>"; };
+ F9BA515B0ECE58AA00D1D62E /* dyldinfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dyldinfo.cpp; path = src/other/dyldinfo.cpp; sourceTree = "<group>"; };
+ F9BA51610ECE58BE00D1D62E /* dyldinfo */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyldinfo; sourceTree = BUILT_PRODUCTS_DIR; };
+ F9C0D48A06DD1E1B001C7193 /* Options.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Options.cpp; path = src/ld/Options.cpp; sourceTree = "<group>"; };
+ F9C0D48B06DD1E1B001C7193 /* Options.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Options.h; path = src/ld/Options.h; sourceTree = "<group>"; };
+ F9C12E9F0ED63DB1005BC69D /* dyldinfo.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = dyldinfo.1; path = doc/man/man1/dyldinfo.1; 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>"; };
+ F9EA72D4097454FF008B4F1D /* machochecker.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = machochecker.cpp; path = src/other/machochecker.cpp; sourceTree = "<group>"; };
+ F9EA7582097882F3008B4F1D /* debugline.c */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 2; lastKnownFileType = sourcecode.c.c; name = debugline.c; path = src/ld/debugline.c; sourceTree = "<group>"; tabWidth = 8; usesTabs = 1; };
+ F9EA7583097882F3008B4F1D /* debugline.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = debugline.h; path = src/ld/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>"; };
- F9FCC3F10A54A75600CEB866 /* ld64.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = ld64.1; path = doc/man/man1/ld64.1; sourceTree = "<group>"; };
+ F9EC78050A2F8674002A3E39 /* rebase.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = rebase.cpp; path = src/other/rebase.cpp; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
);
runOnlyForDeploymentPostprocessing = 0;
};
+ F9B670040DDA176100E6D0DA /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F9BA515F0ECE58BE00D1D62E /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
F9EA72C9097454A6008B4F1D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
isa = PBXGroup;
children = (
C02A29DE0953B26E001FB8C1 /* ChangeLog */,
- F933DC37092A82480083EAC8 /* Architectures.hpp */,
+ F9B813A80EC27B6300F94C13 /* abstraction */,
+ F9B813AD0EC27B8500F94C13 /* ld */,
+ F9B813B00EC27B9E00F94C13 /* other */,
+ F9B8137E0EC2651200F94C13 /* doc */,
+ F9023C3A06D5A23E001BBF46 /* Products */,
+ );
+ sourceTree = "<group>";
+ };
+ F9023C3A06D5A23E001BBF46 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ F9023C3906D5A23E001BBF46 /* ld */,
+ F971EED306D5ACF60041D381 /* ObjectDump */,
+ F9EA72CB097454A6008B4F1D /* machocheck */,
+ F9EC77EE0A2F85F6002A3E39 /* rebase */,
+ F9B670080DDA176100E6D0DA /* unwinddump */,
+ F9BA51610ECE58BE00D1D62E /* dyldinfo */,
+ F9A3DDCA0ED762B700C590B9 /* libprunetrie.a */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ F9B8137E0EC2651200F94C13 /* doc */ = {
+ isa = PBXGroup;
+ children = (
+ F97F5028070D0BB200B9FCD7 /* ld.1 */,
+ F9B1A2580A3A448800DA8FAB /* rebase.1 */,
+ F9C12E9F0ED63DB1005BC69D /* dyldinfo.1 */,
+ F9B813810EC2653000F94C13 /* unwinddump.1 */,
+ );
+ name = doc;
+ sourceTree = "<group>";
+ };
+ F9B813A80EC27B6300F94C13 /* abstraction */ = {
+ isa = PBXGroup;
+ children = (
+ F933D9470929277C0083EAC8 /* MachOFileAbstraction.hpp */,
F933D9460929277C0083EAC8 /* FileAbstraction.hpp */,
+ F9B813BF0EC27C6700F94C13 /* MachOTrie.hpp */,
+ );
+ name = abstraction;
+ sourceTree = "<group>";
+ };
+ F9B813AD0EC27B8500F94C13 /* ld */ = {
+ isa = PBXGroup;
+ children = (
+ F9023C3F06D5A254001BBF46 /* ld.cpp */,
+ F933DC37092A82480083EAC8 /* Architectures.hpp */,
F99F63CE0D99A291007F5394 /* ArchiveReader.hpp */,
- F933D9470929277C0083EAC8 /* MachOFileAbstraction.hpp */,
F933E3CD092E84250083EAC8 /* MachOReaderRelocatable.hpp */,
F933E3CC092E84250083EAC8 /* MachOReaderDylib.hpp */,
F933E3CE092E84250083EAC8 /* MachOWriterExecutable.hpp */,
F9023C3E06D5A254001BBF46 /* ExecutableFile.h */,
F9023C4106D5A254001BBF46 /* ObjectFile.h */,
F98D26850AA779BD00416316 /* OpaqueSection.hpp */,
- F9023C3F06D5A254001BBF46 /* ld.cpp */,
F9C0D48A06DD1E1B001C7193 /* Options.cpp */,
F9C0D48B06DD1E1B001C7193 /* Options.h */,
- F9EA7583097882F3008B4F1D /* debugline.h */,
F9EA7582097882F3008B4F1D /* debugline.c */,
- F9EA72D4097454FF008B4F1D /* machochecker.cpp */,
- F971EED706D5AD240041D381 /* ObjectDump.cpp */,
- F9EC78050A2F8674002A3E39 /* rebase.cpp */,
- F97F5028070D0BB200B9FCD7 /* ld.1 */,
- F9FCC3F10A54A75600CEB866 /* ld64.1 */,
- F9B1A2580A3A448800DA8FAB /* rebase.1 */,
- F9023C3A06D5A23E001BBF46 /* Products */,
+ F9EA7583097882F3008B4F1D /* debugline.h */,
);
+ name = ld;
sourceTree = "<group>";
};
- F9023C3A06D5A23E001BBF46 /* Products */ = {
+ F9B813B00EC27B9E00F94C13 /* other */ = {
isa = PBXGroup;
children = (
- F9023C3906D5A23E001BBF46 /* ld */,
- F971EED306D5ACF60041D381 /* ObjectDump */,
- F9EA72CB097454A6008B4F1D /* machocheck */,
- F9EC77EE0A2F85F6002A3E39 /* rebase */,
+ F9EA72D4097454FF008B4F1D /* machochecker.cpp */,
+ F971EED706D5AD240041D381 /* ObjectDump.cpp */,
+ F9BA515B0ECE58AA00D1D62E /* dyldinfo.cpp */,
+ F9B670110DDA17E800E6D0DA /* UnwindDump.cpp */,
+ F9EC78050A2F8674002A3E39 /* rebase.cpp */,
+ F9A3DE0F0ED76D1900C590B9 /* prune_trie.h */,
+ F9A3DDD20ED762E400C590B9 /* PruneTrie.cpp */,
);
- name = Products;
+ name = other;
sourceTree = "<group>";
};
/* End PBXGroup section */
0B12F6A50CE39466008ABCAE /* build configure.h */,
F9023C3606D5A23E001BBF46 /* Sources */,
F9023C3706D5A23E001BBF46 /* Frameworks */,
- F97F5025070D0B6300B9FCD7 /* CopyFiles */,
- F9FCC3EF0A54A4ED00CEB866 /* Run Script */,
+ F97F5025070D0B6300B9FCD7 /* copy man page */,
);
buildRules = (
F9E8D4BE07FCAF2A00FD5801 /* PBXBuildRule */,
productReference = F971EED306D5ACF60041D381 /* ObjectDump */;
productType = "com.apple.product-type.tool";
};
+ F9A3DDC90ED762B700C590B9 /* libprunetrie */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = F9A3DDCF0ED762C100C590B9 /* Build configuration list for PBXNativeTarget "libprunetrie" */;
+ buildPhases = (
+ F9A3DDC70ED762B700C590B9 /* Sources */,
+ F9A3DE140ED76D7700C590B9 /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = libprunetrie;
+ productName = libmachotrie;
+ productReference = F9A3DDCA0ED762B700C590B9 /* libprunetrie.a */;
+ productType = "com.apple.product-type.library.static";
+ };
+ F9B670010DDA176100E6D0DA /* unwinddump */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = F9B670050DDA176100E6D0DA /* Build configuration list for PBXNativeTarget "unwinddump" */;
+ buildPhases = (
+ F9B670020DDA176100E6D0DA /* Sources */,
+ F9B670040DDA176100E6D0DA /* Frameworks */,
+ F9B813870EC2659600F94C13 /* install man page */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = unwinddump;
+ productName = machocheck;
+ productReference = F9B670080DDA176100E6D0DA /* unwinddump */;
+ productType = "com.apple.product-type.tool";
+ };
+ F9BA51600ECE58BE00D1D62E /* dyldinfo */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = F9BA516D0ECE58DA00D1D62E /* Build configuration list for PBXNativeTarget "dyldinfo" */;
+ buildPhases = (
+ F9BA515E0ECE58BE00D1D62E /* Sources */,
+ F9BA515F0ECE58BE00D1D62E /* Frameworks */,
+ F9C12EA50ED63E05005BC69D /* install man page */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = dyldinfo;
+ productName = dyldinfo;
+ productReference = F9BA51610ECE58BE00D1D62E /* dyldinfo */;
+ productType = "com.apple.product-type.tool";
+ };
F9EA72CA097454A6008B4F1D /* machocheck */ = {
isa = PBXNativeTarget;
buildConfigurationList = F9EA72CF097454D5008B4F1D /* Build configuration list for PBXNativeTarget "machocheck" */;
buildPhases = (
F9EC77EB0A2F85F6002A3E39 /* Sources */,
F9EC77EC0A2F85F6002A3E39 /* Frameworks */,
- F9B1A25E0A3A44CB00DA8FAB /* CopyFiles */,
+ F9B1A25E0A3A44CB00DA8FAB /* install man page */,
);
buildRules = (
);
F9B1A2670A3A567B00DA8FAB /* all */,
F9023C3806D5A23E001BBF46 /* ld */,
F9EC77ED0A2F85F6002A3E39 /* rebase */,
+ F9B670010DDA176100E6D0DA /* unwinddump */,
F971EED206D5ACF60041D381 /* ObjectDump */,
F9EA72CA097454A6008B4F1D /* machocheck */,
+ F9BA51600ECE58BE00D1D62E /* dyldinfo */,
+ F9A3DDC90ED762B700C590B9 /* libprunetrie */,
F96D5368094A2754008E9EE8 /* unit-tests */,
);
};
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/bash;
- shellScript = "echo \"#undef LTO_SUPPORT\t\" > ${DERIVED_FILE_DIR}/configure.h\n";
+ shellScript = "if [ -f /Developer/usr/local/include/llvm-c/lto.h ]; then\n\techo \"#define LTO_SUPPORT 1\" > ${DERIVED_FILE_DIR}/configure.h\n\techo \"-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib\" > ${DERIVED_FILE_DIR}/linker_opts\nelse\n\techo \"#undef LTO_SUPPORT\" > ${DERIVED_FILE_DIR}/configure.h\n\techo \"\" > ${DERIVED_FILE_DIR}/linker_opts\nfi\n";
showEnvVarsInLog = 0;
};
F96D5367094A2754008E9EE8 /* ShellScript */ = {
shellScript = "# Let tests set MACOSX_DEPLOYMENT_TARGET as they need\nunsetenv MACOSX_DEPLOYMENT_TARGET\n\n# always use new linker\nsetenv LD_NO_CLASSIC_LINKER\nsetenv LD_NO_CLASSIC_LINKER_STATIC\n\n# run full test suite\n\"$SRCROOT\"/unit-tests/run-all-unit-tests\n\nexit 0";
showEnvVarsInLog = 0;
};
- F9FCC3EF0A54A4ED00CEB866 /* Run Script */ = {
- isa = PBXShellScriptBuildPhase;
- buildActionMask = 8;
- files = (
- );
- inputPaths = (
- );
- name = "Run Script";
- outputPaths = (
- );
- runOnlyForDeploymentPostprocessing = 1;
- shellPath = /bin/sh;
- shellScript = "cd ${DSTROOT}/usr/bin\nln -s ld ld64";
- showEnvVarsInLog = 0;
- };
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
);
runOnlyForDeploymentPostprocessing = 0;
};
+ F9A3DDC70ED762B700C590B9 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F9A3DDD30ED762E400C590B9 /* PruneTrie.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F9B670020DDA176100E6D0DA /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F9B670120DDA17E800E6D0DA /* UnwindDump.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F9BA515E0ECE58BE00D1D62E /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F9BA51650ECE58C800D1D62E /* dyldinfo.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
F9EA72C8097454A6008B4F1D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
target = F971EED206D5ACF60041D381 /* ObjectDump */;
targetProxy = F96D536B094A275F008E9EE8 /* PBXContainerItemProxy */;
};
+ F9A3DE160ED76D9A00C590B9 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = F9A3DDC90ED762B700C590B9 /* libprunetrie */;
+ targetProxy = F9A3DE150ED76D9A00C590B9 /* PBXContainerItemProxy */;
+ };
F9B1A2690A3A568200DA8FAB /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = F9023C3806D5A23E001BBF46 /* ld */;
target = F9EC77ED0A2F85F6002A3E39 /* rebase */;
targetProxy = F9B1A26A0A3A568400DA8FAB /* PBXContainerItemProxy */;
};
+ F9B693890EC4D28C00076912 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = F9B670010DDA176100E6D0DA /* unwinddump */;
+ targetProxy = F9B693880EC4D28C00076912 /* PBXContainerItemProxy */;
+ };
+ F9B8135D0EC2620E00F94C13 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = F9B670010DDA176100E6D0DA /* unwinddump */;
+ targetProxy = F9B8135C0EC2620E00F94C13 /* PBXContainerItemProxy */;
+ };
+ F9C12EEA0ED65765005BC69D /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = F9BA51600ECE58BE00D1D62E /* dyldinfo */;
+ targetProxy = F9C12EE90ED65765005BC69D /* PBXContainerItemProxy */;
+ };
F9EA73970974999B008B4F1D /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = F9EA72CA097454A6008B4F1D /* machocheck */;
"$(DEVELOPER_DIR)/usr/include",
);
INSTALL_PATH = /usr/bin;
+ LINKER_DISPLAYS_MANGLED_NAMES = NO;
MACOSX_DEPLOYMENT_TARGET = "";
OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)";
- OTHER_LDFLAGS = "";
+ OTHER_LDFLAGS = "@$(DERIVED_FILE_DIR)/linker_opts";
PREBINDING = NO;
PRODUCT_NAME = ld;
SECTORDER_FLAGS = "";
F933D91D09291AC90083EAC8 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
- COPY_PHASE_STRIP = YES;
+ ARCHS = x86_64;
+ COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_DYNAMIC_NO_PIC = YES;
);
INSTALL_PATH = /usr/bin;
OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)";
+ OTHER_LDFLAGS = (
+ "@$(DERIVED_FILE_DIR)/linker_opts",
+ "-Wl,-exported_symbol,__mh_execute_header",
+ );
PREBINDING = NO;
PRODUCT_NAME = ld;
SECTORDER_FLAGS = "";
- VALID_ARCHS = "i386 ppc";
+ STRIP_INSTALLED_PRODUCT = YES;
+ STRIP_STYLE = debugging;
+ VALID_ARCHS = "x86_64 i386 ppc";
VERSIONING_SYSTEM = "apple-generic";
WARNING_CFLAGS = "-Wall";
};
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
- HEADER_SEARCH_PATHS = "$(DEVELOPER_DIR)/usr/include";
+ HEADER_SEARCH_PATHS = "$(DEVELOPER_DIR)/usr/local/include";
INSTALL_PATH = "$(HOME)/bin";
OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib";
OTHER_REZFLAGS = "";
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = s;
+ HEADER_SEARCH_PATHS = "$(DEVELOPER_DIR)/usr/local/include";
INSTALL_PATH = "$(HOME)/bin";
OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib";
OTHER_REZFLAGS = "";
buildSettings = {
GCC_DYNAMIC_NO_PIC = NO;
GCC_TREAT_WARNINGS_AS_ERRORS = NO;
- HEADER_SEARCH_PATHS = "$(DEVELOPER_DIR)/SDKs/Extra/usr/include";
};
name = Release;
};
};
name = Release;
};
+ F9A3DDCB0ED762B800C590B9 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_SYMBOLS_PRIVATE_EXTERN = YES;
+ GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
+ INSTALL_PATH = /usr/local/lib;
+ PREBINDING = NO;
+ PRODUCT_NAME = prunetrie;
+ };
+ name = Debug;
+ };
+ F9A3DDCC0ED762B800C590B9 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ GCC_ENABLE_FIX_AND_CONTINUE = NO;
+ GCC_MODEL_TUNING = G5;
+ GCC_SYMBOLS_PRIVATE_EXTERN = YES;
+ INSTALL_PATH = /usr/local/lib;
+ PREBINDING = NO;
+ PRODUCT_NAME = prunetrie;
+ };
+ name = Release;
+ };
F9B1A26D0A3A568700DA8FAB /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
};
name = Release;
};
+ F9B670060DDA176100E6D0DA /* 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;
+ GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
+ INSTALL_PATH = "$(HOME)/bin";
+ PREBINDING = NO;
+ PRODUCT_NAME = unwinddump;
+ };
+ name = Debug;
+ };
+ F9B670070DDA176100E6D0DA /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
+ HEADER_SEARCH_PATHS = "";
+ INSTALL_PATH = /usr/bin;
+ OTHER_LDFLAGS = "-Wl,-exported_symbol,__mh_execute_header";
+ PREBINDING = NO;
+ PRODUCT_NAME = unwinddump;
+ STRIP_INSTALLED_PRODUCT = YES;
+ STRIP_STYLE = debugging;
+ };
+ name = Release;
+ };
+ F9BA51630ECE58BF00D1D62E /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ INSTALL_PATH = /usr/local/bin;
+ PREBINDING = NO;
+ PRODUCT_NAME = dyldinfo;
+ };
+ name = Debug;
+ };
+ F9BA51640ECE58BF00D1D62E /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_ENABLE_FIX_AND_CONTINUE = NO;
+ GCC_MODEL_TUNING = G5;
+ INSTALL_PATH = /usr/bin;
+ OTHER_LDFLAGS = "-Wl,-exported_symbol,__mh_execute_header";
+ PREBINDING = NO;
+ PRODUCT_NAME = dyldinfo;
+ STRIP_INSTALLED_PRODUCT = YES;
+ STRIP_STYLE = debugging;
+ ZERO_LINK = NO;
+ };
+ name = Release;
+ };
F9EA72D0097454D5008B4F1D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
DEBUG_INFORMATION_FORMAT = dwarf;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_MODEL_TUNING = G5;
+ HEADER_SEARCH_PATHS = "";
INSTALL_PATH = "$(HOME)/bin";
PREBINDING = NO;
PRODUCT_NAME = machocheck;
F9EC77F20A2F8616002A3E39 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
- COPY_PHASE_STRIP = YES;
+ COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_MODEL_TUNING = G5;
GCC_PREPROCESSOR_DEFINITIONS = "$(GCC_PREPROCESSOR_DEFINITIONS_$(RC_RELEASE))";
+ HEADER_SEARCH_PATHS = "";
INSTALL_PATH = /usr/bin;
+ OTHER_LDFLAGS = "-Wl,-exported_symbol,__mh_execute_header";
PREBINDING = NO;
PRODUCT_NAME = rebase;
- VALID_ARCHS = "i386 ppc";
+ STRIP_INSTALLED_PRODUCT = YES;
+ STRIP_STYLE = debugging;
+ VALID_ARCHS = "i386 ppc x86_64";
};
name = Release;
};
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ F9A3DDCF0ED762C100C590B9 /* Build configuration list for PBXNativeTarget "libprunetrie" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F9A3DDCB0ED762B800C590B9 /* Debug */,
+ F9A3DDCC0ED762B800C590B9 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
F9B1A26C0A3A568700DA8FAB /* Build configuration list for PBXAggregateTarget "all" */ = {
isa = XCConfigurationList;
buildConfigurations = (
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ F9B670050DDA176100E6D0DA /* Build configuration list for PBXNativeTarget "unwinddump" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F9B670060DDA176100E6D0DA /* Debug */,
+ F9B670070DDA176100E6D0DA /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ F9BA516D0ECE58DA00D1D62E /* Build configuration list for PBXNativeTarget "dyldinfo" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F9BA51630ECE58BF00D1D62E /* Debug */,
+ F9BA51640ECE58BF00D1D62E /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
F9EA72CF097454D5008B4F1D /* Build configuration list for PBXNativeTarget "machocheck" */ = {
isa = XCConfigurationList;
buildConfigurations = (
+++ /dev/null
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
- *
- * Copyright (c) 2005-2007 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#ifndef __ARCHITECTURES__
-#define __ARCHITECTURES__
-
-#include "FileAbstraction.hpp"
-
-
-//
-// Architectures
-//
-struct ppc
-{
- typedef Pointer32<BigEndian> P;
-
- enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff16, kPointerDiff32, kPointerDiff64,
- kBranch24, kBranch24WeakImport, kBranch14,
- kPICBaseLow16, kPICBaseLow14, kPICBaseHigh16,
- kAbsLow16, kAbsLow14, kAbsHigh16, kAbsHigh16AddLow,
- kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference };
-};
-
-struct ppc64
-{
- typedef Pointer64<BigEndian> P;
-
- enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff16, kPointerDiff32, kPointerDiff64,
- kBranch24, kBranch24WeakImport, kBranch14,
- kPICBaseLow16, kPICBaseLow14, kPICBaseHigh16,
- kAbsLow16, kAbsLow14, kAbsHigh16, kAbsHigh16AddLow,
- kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference };
-};
-
-struct x86
-{
- typedef Pointer32<LittleEndian> P;
-
- enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff, kPointerDiff16,
- kPCRel32, kPCRel32WeakImport, kAbsolute32, kPCRel16, kPCRel8,
- kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference };
-};
-
-struct x86_64
-{
- typedef Pointer64<LittleEndian> P;
-
- enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff, kPointerDiff32,
- kPCRel32, kPCRel32_1, kPCRel32_2, kPCRel32_4,
- kBranchPCRel32, kBranchPCRel32WeakImport,
- kPCRel32GOTLoad, kPCRel32GOTLoadWeakImport,
- kPCRel32GOT, kPCRel32GOTWeakImport, kBranchPCRel8,
- kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference };
-};
-
-struct arm
-{
- typedef Pointer32<LittleEndian> P;
-
- enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff, kReadOnlyPointer,
- kBranch24, kBranch24WeakImport, kThumbBranch22, kThumbBranch22WeakImport,
- kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference };
-};
-
-#endif // __ARCHITECTURES__
-
-
+++ /dev/null
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
- *
- * Copyright (c) 2005-2008 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#ifndef __OBJECT_FILE_ARCHIVE__
-#define __OBJECT_FILE_ARCHIVE__
-
-#include <stdint.h>
-#include <math.h>
-#include <unistd.h>
-#include <sys/param.h>
-#include <mach-o/ranlib.h>
-#include <ar.h>
-
-#include <vector>
-#include <set>
-#include <algorithm>
-#include <ext/hash_map>
-
-#include "MachOFileAbstraction.hpp"
-#include "ObjectFile.h"
-#include "MachOReaderRelocatable.hpp"
-#if LTO_SUPPORT
- #include "LTOReader.hpp"
-#endif
-
-namespace archive {
-
-typedef const struct ranlib* ConstRanLibPtr;
-
-template <typename A>
-class Reader : public ObjectFile::Reader
-{
-public:
- static bool validFile(const uint8_t* fileContent, uint64_t fileLength);
- Reader(const uint8_t fileContent[], uint64_t fileLength,
- const char* path, time_t modTime,
- const ObjectFile::ReaderOptions& options, uint32_t ordinalBase);
- virtual ~Reader() {}
-
- virtual const char* getPath() { return fPath; }
- virtual time_t getModificationTime(){ return fModTime; }
- virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; }
- virtual std::vector<class ObjectFile::Atom*>& getAtoms();
- virtual std::vector<class ObjectFile::Atom*>* getJustInTimeAtomsFor(const char* name);
- virtual std::vector<Stab>* getStabs() { return NULL; }
- virtual void optimize(std::vector<ObjectFile::Atom*>&, std::vector<ObjectFile::Atom*>&,
- std::vector<const char*>&, uint32_t, ObjectFile::Reader* writer,
- bool allGlobalsAReDeadStripRoots, int okind,
- bool verbose, bool saveTemps, const char* outputFilePath,
- bool pie, bool allowTextRelocs);
-
-private:
- static bool validMachOFile(const uint8_t* fileContent, uint64_t fileLength);
- static bool validLTOFile(const uint8_t* fileContent, uint64_t fileLength);
- static cpu_type_t architecture();
-
-
- class Entry : ar_hdr
- {
- public:
- const char* getName() const;
- time_t getModTime() const;
- const uint8_t* getContent() const;
- uint32_t getContentSize() const;
- const Entry* getNext() const;
- private:
- bool hasLongName() const;
- unsigned int getLongNameSpace() const;
-
- };
-
- class CStringEquals
- {
- public:
- bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
- };
- typedef __gnu_cxx::hash_map<const char*, const struct ranlib*, __gnu_cxx::hash<const char*>, CStringEquals> NameToEntryMap;
-
- typedef typename A::P P;
- typedef typename A::P::E E;
-
- const struct ranlib* ranlibHashSearch(const char* name);
- ObjectFile::Reader* makeObjectReaderForMember(const Entry* member);
- void dumpTableOfContents();
- void buildHashTable();
-
- const char* fPath;
- time_t fModTime;
- const ObjectFile::ReaderOptions& fOptions;
- uint32_t fOrdinalBase;
- const uint8_t* fFileContent;
- uint64_t fFileLength;
- const struct ranlib* fTableOfContents;
- uint32_t fTableOfContentCount;
- const char* fStringPool;
- std::vector<class ObjectFile::Atom*> fAllAtoms;
- std::vector<class ObjectFile::Reader*> fInstantiatedReaders;
- std::set<const class Entry*> fInstantiatedEntries;
- std::set<const class Entry*> fPossibleEntries;
- NameToEntryMap fHashTable;
-
- static std::vector<class ObjectFile::Atom*> fgEmptyList;
-};
-
-template <typename A>
-std::vector<class ObjectFile::Atom*> Reader<A>::fgEmptyList;
-
-
-template <typename A>
-bool Reader<A>::Entry::hasLongName() const
-{
- return ( strncmp(this->ar_name, AR_EFMT1, strlen(AR_EFMT1)) == 0 );
-}
-
-template <typename A>
-unsigned int Reader<A>::Entry::getLongNameSpace() const
-{
- char* endptr;
- long result = strtol(&this->ar_name[strlen(AR_EFMT1)], &endptr, 10);
- return result;
-}
-
-template <typename A>
-const char* Reader<A>::Entry::getName() const
-{
- if ( this->hasLongName() ) {
- int len = this->getLongNameSpace();
- static char longName[256];
- strncpy(longName, ((char*)this)+sizeof(ar_hdr), len);
- longName[len] = '\0';
- return longName;
- }
- else {
- static char shortName[20];
- strncpy(shortName, this->ar_name, 16);
- shortName[16] = '\0';
- char* space = strchr(shortName, ' ');
- if ( space != NULL )
- *space = '\0';
- return shortName;
- }
-}
-
-template <typename A>
-time_t Reader<A>::Entry::getModTime() const
-{
- char temp[14];
- strncpy(temp, this->ar_date, 12);
- temp[12] = '\0';
- char* endptr;
- return (time_t)strtol(temp, &endptr, 10);
-}
-
-
-template <typename A>
-const uint8_t* Reader<A>::Entry::getContent() const
-{
- if ( this->hasLongName() )
- return ((uint8_t*)this) + sizeof(ar_hdr) + this->getLongNameSpace();
- else
- return ((uint8_t*)this) + sizeof(ar_hdr);
-}
-
-
-template <typename A>
-uint32_t Reader<A>::Entry::getContentSize() const
-{
- char temp[12];
- strncpy(temp, this->ar_size, 10);
- temp[10] = '\0';
- char* endptr;
- long size = strtol(temp, &endptr, 10);
- // long name is included in ar_size
- if ( this->hasLongName() )
- size -= this->getLongNameSpace();
- return size;
-}
-
-
-template <typename A>
-const class Reader<A>::Entry* Reader<A>::Entry::getNext() const
-{
- const uint8_t* p = this->getContent() + getContentSize();
- p = (const uint8_t*)(((uintptr_t)p+3) & (-4)); // 4-byte align
- return (class Reader<A>::Entry*)p;
-}
-
-
-template <> cpu_type_t Reader<ppc>::architecture() { return CPU_TYPE_POWERPC; }
-template <> cpu_type_t Reader<ppc64>::architecture() { return CPU_TYPE_POWERPC64; }
-template <> cpu_type_t Reader<x86>::architecture() { return CPU_TYPE_I386; }
-template <> cpu_type_t Reader<x86_64>::architecture() { return CPU_TYPE_X86_64; }
-template <> cpu_type_t Reader<arm>::architecture() { return CPU_TYPE_ARM; }
-
-
-template <typename A>
-bool Reader<A>::validMachOFile(const uint8_t* fileContent, uint64_t fileLength)
-{
- return mach_o::relocatable::Reader<A>::validFile(fileContent);
-}
-
-template <typename A>
-bool Reader<A>::validLTOFile(const uint8_t* fileContent, uint64_t fileLength)
-{
-#if LTO_SUPPORT
- return lto::Reader::validFile(fileContent, fileLength, architecture());
-#else
- return false;
-#endif
-}
-
-
-
-template <typename A>
-bool Reader<A>::validFile(const uint8_t* fileContent, uint64_t fileLength)
-{
- // must have valid archive header
- if ( strncmp((const char*)fileContent, "!<arch>\n", 8) != 0 )
- return false;
-
- // peak at first .o file and verify it is correct architecture
- const Entry* const start = (Entry*)&fileContent[8];
- const Entry* const end = (Entry*)&fileContent[fileLength];
- for (const Entry* p=start; p < end; p = p->getNext()) {
- const char* memberName = p->getName();
- // skip option table-of-content member
- if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) )
- continue;
- // archive is valid if first .o file is valid
- return (validMachOFile(p->getContent(), p->getContentSize()) || validLTOFile(p->getContent(), p->getContentSize()));
- }
- // empty archive
- return true;
-}
-
-template <typename A>
-Reader<A>::Reader(const uint8_t fileContent[], uint64_t fileLength, const char* path, time_t modTime,
- const ObjectFile::ReaderOptions& options, uint32_t ordinalBase)
- : fPath(NULL), fModTime(modTime), fOptions(options), fOrdinalBase(ordinalBase), fFileContent(NULL),
- fTableOfContents(NULL), fTableOfContentCount(0), fStringPool(NULL)
-{
- fPath = strdup(path);
- fFileContent = fileContent;
- fFileLength = fileLength;
-
- if ( strncmp((const char*)fileContent, "!<arch>\n", 8) != 0 )
- throw "not an archive";
-
- // write out path for -whatsloaded option
- if ( options.fLogAllFiles )
- printf("%s\n", path);
-
- if ( !options.fFullyLoadArchives ) {
- const Entry* const firstMember = (Entry*)&fFileContent[8];
- if ( (strcmp(firstMember->getName(), SYMDEF_SORTED) == 0) || (strcmp(firstMember->getName(), SYMDEF) == 0) ) {
- const uint8_t* contents = firstMember->getContent();
- uint32_t ranlibArrayLen = E::get32(*((uint32_t*)contents));
- fTableOfContents = (const struct ranlib*)&contents[4];
- fTableOfContentCount = ranlibArrayLen / sizeof(struct ranlib);
- fStringPool = (const char*)&contents[ranlibArrayLen+8];
- if ( ((uint8_t*)(&fTableOfContents[fTableOfContentCount]) > &fileContent[fileLength])
- || ((uint8_t*)fStringPool > &fileContent[fileLength]) )
- throw "malformed archive, perhaps wrong architecture";
- this->buildHashTable();
- }
- else
- throw "archive has no table of contents";
- }
-}
-
-
-template <typename A>
-ObjectFile::Reader* Reader<A>::makeObjectReaderForMember(const Entry* member)
-{
- const char* memberName = member->getName();
- char memberPath[strlen(fPath) + strlen(memberName)+4];
- strcpy(memberPath, fPath);
- strcat(memberPath, "(");
- strcat(memberPath, memberName);
- strcat(memberPath, ")");
- //fprintf(stderr, "using %s from %s\n", memberName, fPath);
- try {
- // offset the ordinals in this mach-o .o file, so that atoms layout in same order as in archive
- uint32_t ordinalBase = fOrdinalBase + (uint8_t*)member - fFileContent;
- if ( validMachOFile(member->getContent(), member->getContentSize()) ) {
- return new typename mach_o::relocatable::Reader<A>::Reader(member->getContent(), memberPath, member->getModTime(), fOptions, ordinalBase);
- }
-#if LTO_SUPPORT
- else if ( validLTOFile(member->getContent(), member->getContentSize()) ) {
- return new typename lto::Reader(member->getContent(), member->getContentSize(), memberPath, member->getModTime(), fOptions, architecture());
- }
-#endif
- throw "not a valid archive member";
- }
- catch (const char* msg) {
- throwf("in %s, %s", memberPath, msg);
- }
-}
-
-
-template <typename A>
-std::vector<class ObjectFile::Atom*>& Reader<A>::getAtoms()
-{
- if ( fOptions.fFullyLoadArchives ) {
- // build vector of all atoms from all .o files in this archive
- const Entry* const start = (Entry*)&fFileContent[8];
- const Entry* const end = (Entry*)&fFileContent[fFileLength];
- for (const Entry* p=start; p < end; p = p->getNext()) {
- const char* memberName = p->getName();
- if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) )
- continue;
- if ( fOptions.fWhyLoad )
- printf("-all_load forced load of %s(%s)\n", this->getPath(), memberName);
- ObjectFile::Reader* r = this->makeObjectReaderForMember(p);
- std::vector<class ObjectFile::Atom*>& atoms = r->getAtoms();
- fAllAtoms.insert(fAllAtoms.end(), atoms.begin(), atoms.end());
- fInstantiatedReaders.push_back(r);
- }
- return fAllAtoms;
- }
- else if ( fOptions.fLoadAllObjcObjectsFromArchives ) {
- // build vector of all atoms from all .o files containing objc classes in this archive
- for(class NameToEntryMap::iterator it = fHashTable.begin(); it != fHashTable.end(); ++it) {
- if ( (strncmp(it->first, ".objc_c", 7) == 0) || (strncmp(it->first, "_OBJC_CLASS_$_", 14) == 0) ) {
- const Entry* member = (Entry*)&fFileContent[E::get32(it->second->ran_off)];
- if ( fInstantiatedEntries.count(member) == 0 ) {
- if ( fOptions.fWhyLoad )
- printf("-ObjC forced load of %s(%s)\n", this->getPath(), member->getName());
- // only return these atoms once
- fInstantiatedEntries.insert(member);
- ObjectFile::Reader* r = makeObjectReaderForMember(member);
- std::vector<class ObjectFile::Atom*>& atoms = r->getAtoms();
- fAllAtoms.insert(fAllAtoms.end(), atoms.begin(), atoms.end());
- fInstantiatedReaders.push_back(r);
- }
- }
- }
- return fAllAtoms;
- }
- else {
- // return nonthing for now, getJustInTimeAtomsFor() will return atoms as needed
- return fgEmptyList;
- }
-}
-
-template <typename A>
-void Reader<A>::optimize(std::vector<ObjectFile::Atom*>& allAtoms, std::vector<ObjectFile::Atom*>& newAtoms,
- std::vector<const char*>& additionalUndefines, uint32_t nextOrdinal, ObjectFile::Reader* writer,
- bool allGlobalsAReDeadStripRoots, int okind,
- bool verbose, bool saveTemps, const char* outputFilePath,
- bool pie, bool allowTextRelocs)
-{
- for(std::vector<ObjectFile::Reader*>::iterator it=fInstantiatedReaders.begin(); it != fInstantiatedReaders.end(); ++it) {
- (*it)->optimize(allAtoms, newAtoms, additionalUndefines, nextOrdinal, writer, allGlobalsAReDeadStripRoots,
- okind, verbose, saveTemps, outputFilePath, pie, allowTextRelocs);
- }
-}
-
-
-
-template <typename A>
-ConstRanLibPtr Reader<A>::ranlibHashSearch(const char* name)
-{
- class NameToEntryMap::iterator pos = fHashTable.find(name);
- if ( pos != fHashTable.end() )
- return pos->second;
- else
- return NULL;
-}
-
-template <typename A>
-void Reader<A>::buildHashTable()
-{
- // walk through list backwards, adding/overwriting entries
- // this assures that with duplicates those earliest in the list will be found
- for (int i = fTableOfContentCount-1; i >= 0; --i) {
- const struct ranlib* entry = &fTableOfContents[i];
- const char* entryName = &fStringPool[E::get32(entry->ran_un.ran_strx)];
- const Entry* member = (Entry*)&fFileContent[E::get32(entry->ran_off)];
- //fprintf(stderr, "adding hash %d, %s -> %p\n", i, entryName, entry);
- fHashTable[entryName] = entry;
- fPossibleEntries.insert(member);
- }
-}
-
-template <typename A>
-void Reader<A>::dumpTableOfContents()
-{
- for (unsigned int i=0; i < fTableOfContentCount; ++i) {
- const struct ranlib* e = &fTableOfContents[i];
- printf("%s in %s\n", &fStringPool[E::get32(e->ran_un.ran_strx)], ((Entry*)&fFileContent[E::get32(e->ran_off)])->getName());
- }
-}
-
-template <typename A>
-std::vector<class ObjectFile::Atom*>* Reader<A>::getJustInTimeAtomsFor(const char* name)
-{
- if ( fOptions.fFullyLoadArchives ) {
- return NULL;
- }
- else {
- const struct ranlib* result = NULL;
- // do a hash search of table of contents looking for requested symbol
- result = ranlibHashSearch(name);
- if ( result != NULL ) {
- const Entry* member = (Entry*)&fFileContent[E::get32(result->ran_off)];
- if ( fInstantiatedEntries.count(member) == 0 ) {
- 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);
- fInstantiatedReaders.push_back(r);
- return new std::vector<class ObjectFile::Atom*>(r->getAtoms());
- }
- }
- //fprintf(stderr, "%s NOT found in archive %s\n", name, fPath);
- return NULL;
- }
-}
-
-
-
-
-
-}; // namespace archive
-
-
-#endif // __OBJECT_FILE_ARCHIVE__
+++ /dev/null
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
- *
- * Copyright (c) 2005-2007 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#ifndef __EXECUTABLEFILE__
-#define __EXECUTABLEFILE__
-
-#include <stdint.h>
-#include <vector>
-
-#include "ObjectFile.h"
-#include "Options.h"
-
-
-namespace ExecutableFile {
-
- struct DyLibUsed
- {
- ObjectFile::Reader* reader;
- DynamicLibraryOptions options;
- };
-
- class Writer : public ObjectFile::Reader
- {
- public:
- virtual ~Writer() {};
-
- virtual const char* getPath() = 0;
- virtual std::vector<class ObjectFile::Atom*>& getAtoms() = 0;
- virtual std::vector<class ObjectFile::Atom*>* getJustInTimeAtomsFor(const char* name) = 0;
- virtual ObjectFile::Atom& makeObjcInfoAtom(ObjectFile::Reader::ObjcConstraint objcContraint,
- bool objcReplacementClasses) = 0;
- virtual class ObjectFile::Atom* getUndefinedProxyAtom(const char* name) = 0;
- virtual uint64_t write(std::vector<class ObjectFile::Atom*>& atoms,
- std::vector<class ObjectFile::Reader::Stab>& stabs,
- class ObjectFile::Atom* entryPointAtom,
- class ObjectFile::Atom* dyldHelperAtom,
- class ObjectFile::Atom* dyldLazyDylibHelperAtom,
- bool createUUID, bool canScatter,
- ObjectFile::Reader::CpuConstraint cpuConstraint,
- bool biggerThanTwoGigs,
- bool overridesDylibWeakDefines) = 0;
-
- protected:
- Writer(std::vector<DyLibUsed>&) {};
- };
-
-};
-
-#endif // __EXECUTABLEFILE__
+++ /dev/null
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
- *
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-#ifndef __FILE_ABSTRACTION__
-#define __FILE_ABSTRACTION__
-
-
-#include <stdint.h>
-#include <string.h>
-#include <libkern/OSByteOrder.h>
-
-#ifdef __OPTIMIZE__
-#define INLINE __attribute__((always_inline))
-#else
-#define INLINE
-#endif
-
-//
-// This abstraction layer is for use with file formats that have 64-bit/32-bit and Big-Endian/Little-Endian variants
-//
-// For example: to make a utility that handles 32-bit little enidan files use: Pointer32<LittleEndian>
-//
-//
-// get16() read a 16-bit number from an E endian struct
-// set16() write a 16-bit number to an E endian struct
-// get32() read a 32-bit number from an E endian struct
-// set32() write a 32-bit number to an E endian struct
-// get64() read a 64-bit number from an E endian struct
-// set64() write a 64-bit number to an E endian struct
-//
-// getBits() read a bit field from an E endian struct (bitCount=number of bits in field, firstBit=bit index of field)
-// setBits() write a bit field to an E endian struct (bitCount=number of bits in field, firstBit=bit index of field)
-//
-// getBitsRaw() read a bit field from a struct with native endianness
-// setBitsRaw() write a bit field from a struct with native endianness
-//
-
-class BigEndian
-{
-public:
- static uint16_t get16(const uint16_t& from) INLINE { return OSReadBigInt16(&from, 0); }
- static void set16(uint16_t& into, uint16_t value) INLINE { OSWriteBigInt16(&into, 0, value); }
-
- static uint32_t get32(const uint32_t& from) INLINE { return OSReadBigInt32(&from, 0); }
- static void set32(uint32_t& into, uint32_t value) INLINE { OSWriteBigInt32(&into, 0, value); }
-
- static uint64_t get64(const uint64_t& from) INLINE { return OSReadBigInt64(&from, 0); }
- static void set64(uint64_t& into, uint64_t value) INLINE { OSWriteBigInt64(&into, 0, value); }
-
- static uint32_t getBits(const uint32_t& from,
- uint8_t firstBit, uint8_t bitCount) INLINE { return getBitsRaw(get32(from), firstBit, bitCount); }
- static void setBits(uint32_t& into, uint32_t value,
- uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = get32(into); setBitsRaw(temp, value, firstBit, bitCount); set32(into, temp); }
-
- static uint32_t getBitsRaw(const uint32_t& from,
- uint8_t firstBit, uint8_t bitCount) INLINE { return ((from >> (32-firstBit-bitCount)) & ((1<<bitCount)-1)); }
- static void setBitsRaw(uint32_t& into, uint32_t value,
- uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = into;
- const uint32_t mask = ((1<<bitCount)-1);
- temp &= ~(mask << (32-firstBit-bitCount));
- temp |= ((value & mask) << (32-firstBit-bitCount));
- into = temp; }
- enum { little_endian = 0 };
-};
-
-
-class LittleEndian
-{
-public:
- static uint16_t get16(const uint16_t& from) INLINE { return OSReadLittleInt16(&from, 0); }
- static void set16(uint16_t& into, uint16_t value) INLINE { OSWriteLittleInt16(&into, 0, value); }
-
- static uint32_t get32(const uint32_t& from) INLINE { return OSReadLittleInt32(&from, 0); }
- static void set32(uint32_t& into, uint32_t value) INLINE { OSWriteLittleInt32(&into, 0, value); }
-
- static uint64_t get64(const uint64_t& from) INLINE { return OSReadLittleInt64(&from, 0); }
- static void set64(uint64_t& into, uint64_t value) INLINE { OSWriteLittleInt64(&into, 0, value); }
-
- static uint32_t getBits(const uint32_t& from,
- uint8_t firstBit, uint8_t bitCount) INLINE { return getBitsRaw(get32(from), firstBit, bitCount); }
- static void setBits(uint32_t& into, uint32_t value,
- uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = get32(into); setBitsRaw(temp, value, firstBit, bitCount); set32(into, temp); }
-
- static uint32_t getBitsRaw(const uint32_t& from,
- uint8_t firstBit, uint8_t bitCount) INLINE { return ((from >> firstBit) & ((1<<bitCount)-1)); }
- static void setBitsRaw(uint32_t& into, uint32_t value,
- uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = into;
- const uint32_t mask = ((1<<bitCount)-1);
- temp &= ~(mask << firstBit);
- temp |= ((value & mask) << firstBit);
- into = temp; }
- enum { little_endian = 1 };
-};
-
-
-template <typename _E>
-class Pointer32
-{
-public:
- typedef uint32_t uint_t;
- typedef _E E;
-
- static uint64_t getP(const uint_t& from) INLINE { return _E::get32(from); }
- static void setP(uint_t& into, uint64_t value) INLINE { _E::set32(into, value); }
-};
-
-
-template <typename _E>
-class Pointer64
-{
-public:
- typedef uint64_t uint_t;
- typedef _E E;
-
- static uint64_t getP(const uint_t& from) INLINE { return _E::get64(from); }
- static void setP(uint_t& into, uint64_t value) INLINE { _E::set64(into, value); }
-};
-
-
-
-
-
-#endif // __FILE_ABSTRACTION__
-
-
+++ /dev/null
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
- *
- * Copyright (c) 2006-2008 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#ifndef __LTO_READER_H__
-#define __LTO_READER_H__
-
-#include <stdlib.h>
-#include <mach-o/dyld.h>
-#include <vector>
-#include <ext/hash_set>
-#include <ext/hash_map>
-
-#include "MachOFileAbstraction.hpp"
-#include "Architectures.hpp"
-#include "ObjectFile.h"
-#include "Options.h"
-
-#include "llvm-c/lto.h"
-
-
-namespace lto {
-
-
-//
-// Reference handles Atom references. These references facilitate
-// symbol resolution.
-//
-
-class Reference : public ObjectFile::Reference
-{
-public:
- Reference(const char* name) : fTargetName(name), fTargetAtom(NULL) { }
- Reference(ObjectFile::Atom& atom) : fTargetName(NULL), fTargetAtom(&atom) { }
-
- bool isTargetUnbound() const { return fTargetAtom == NULL; }
- bool isFromTargetUnbound() const { return true; }
- uint8_t getKind() const { return 0; }
- uint64_t getFixUpOffset() const { return 0; }
- const char * getTargetName() const { return fTargetName; }
- ObjectFile::Atom& getTarget() const { return *fTargetAtom; }
- uint64_t getTargetOffset() const { return 0; }
- bool hasFromTarget() const { return false; }
- ObjectFile::Atom& getFromTarget() const { return *((ObjectFile::Atom*)NULL); }
- const char * getFromTargetName() const { return NULL; }
- uint64_t getFromTargetOffset() const { return 0; }
- TargetBinding getTargetBinding() const;
- TargetBinding getFromTargetBinding() const { return kDontBind; }
- void setTarget (ObjectFile::Atom& a, uint64_t offset)
- { fTargetAtom = &a; }
- void setFromTarget(ObjectFile::Atom &a) { }
- const char * getDescription() const;
-
-private:
- const char * fTargetName;
- ObjectFile::Atom * fTargetAtom;
-};
-
-
-ObjectFile::Reference::TargetBinding Reference::getTargetBinding() const
-{
- if ( fTargetAtom == NULL )
- return kUnboundByName;
- else if ( fTargetName == NULL )
- return kBoundDirectly;
- else
- return kBoundByName;
-}
-
-const char* Reference::getDescription() const
-{
- static char temp[256];
- strcpy(temp, "reference to ");
- if ( fTargetName != NULL )
- strcat(temp, fTargetName);
- else
- strcat(temp, fTargetAtom->getDisplayName());
- return temp;
-}
-
-
-class Segment : public ObjectFile::Segment
-{
-public:
- Segment(const char* name, bool readable, bool writable, bool executable, bool fixedAddress)
- : fName(name), fReadable(readable), fWritable(writable), fExecutable(executable), fFixedAddress(fixedAddress) {}
- virtual const char* getName() const { return fName; }
- virtual bool isContentReadable() const { return fReadable; }
- virtual bool isContentWritable() const { return fWritable; }
- virtual bool isContentExecutable() const { return fExecutable; }
- virtual bool hasFixedAddress() const { return fFixedAddress; }
-
- static Segment fgBootstrapSegment;
-
-private:
- const char* fName;
- const bool fReadable;
- const bool fWritable;
- const bool fExecutable;
- const bool fFixedAddress;
-};
-
-Segment Segment:: fgBootstrapSegment("__TEMP", true, false, false, false);
-
-
-
-
-//
-// Atom acts as a proxy Atom for the symbols that are exported by LLVM bitcode file. Initially,
-// Reader creates Atoms to allow linker proceed with usual symbol resolution phase. After
-// optimization is performed, real Atoms are created for these symobls. However these real Atoms
-// are not inserted into global symbol table. Atom holds real Atom and forwards appropriate
-// methods to real atom.
-//
-class Atom : public ObjectFile::Atom
-{
-public:
- Atom(class Reader& owner, const char* name, Scope, DefinitionKind, uint8_t alignment, ObjectFile::Atom& internalAtom);
-
- ObjectFile::Reader* getFile() const { return (ObjectFile::Reader*)&fOwner; }
- bool getTranslationUnitSource (const char **dir, const char **name) const
- { return fRealAtom->getTranslationUnitSource(dir, name); }
- const char * getName () const { return fName; }
- const char * getDisplayName() const { return this->getName(); }
- Scope getScope() const { return fScope; }
- DefinitionKind getDefinitionKind() const { return (fRealAtom ? fRealAtom->getDefinitionKind() : fKind); }
- SymbolTableInclusion getSymbolTableInclusion() const
- { return fRealAtom->getSymbolTableInclusion(); }
- bool dontDeadStrip() const { return false; }
- bool isZeroFill() const { return (fRealAtom ? fRealAtom->isZeroFill() : false); }
- bool isThumb() const { return false; }
- uint64_t getSize() const { return (fRealAtom ? fRealAtom->getSize() : 0); }
- std::vector<ObjectFile::Reference*>& getReferences() const
- { return (fRealAtom ? fRealAtom->getReferences() : (std::vector<ObjectFile::Reference*>&)fReferences); }
- bool mustRemainInSection() const { return fRealAtom->mustRemainInSection(); }
- const char * getSectionName() const { return (fRealAtom ? fRealAtom->getSectionName() : NULL); }
- // Linker::optimize() sets section for this atom, not fRealAtom. Use this Atom's fSection.
- class ObjectFile::Section * getSection() const { return fSection; }
- ObjectFile::Segment& getSegment() const { return (fRealAtom ? fRealAtom->getSegment() : Segment::fgBootstrapSegment); }
- uint32_t getOrdinal() const { return (fRealAtom ? fRealAtom->getOrdinal() : 0); }
- ObjectFile::Atom& getFollowOnAtom() const { return fRealAtom->getFollowOnAtom(); }
- std::vector<ObjectFile::LineInfo>* getLineInfo() const { return (fRealAtom ? fRealAtom->getLineInfo() : NULL); }
- ObjectFile::Alignment getAlignment() const { return (fRealAtom ? fRealAtom->getAlignment() : ObjectFile::Alignment(fAlignment)); }
- void copyRawContent(uint8_t buffer[]) const
- { if (fRealAtom) fRealAtom->copyRawContent(buffer); }
- void setScope(Scope s) { if (fRealAtom) fRealAtom->setScope(s); else fScope = s; }
-
- void setRealAtom (ObjectFile::Atom *atom)
- { fRealAtom = atom; }
- ObjectFile::Atom * getRealAtom() { return fRealAtom; }
- void addReference(ObjectFile::Reference *ref)
- { fReferences.push_back(ref); }
-
- void setSectionOffset(uint64_t offset) { fSectionOffset = offset; if (fRealAtom) fRealAtom->setSectionOffset(offset); }
- void setSection(class ObjectFile::Section* sect) { fSection = sect; if (fRealAtom) fRealAtom->setSection(sect); }
-
-private:
- class Reader& fOwner;
- const char* fName;
- ObjectFile::Atom::Scope fScope;
- ObjectFile::Atom::DefinitionKind fKind;
- uint8_t fAlignment;
- ObjectFile::Atom* fRealAtom;
- std::vector<ObjectFile::Reference*> fReferences;
-};
-
-
-Atom::Atom(class Reader& owner, const char* name, Scope scope, DefinitionKind kind, uint8_t alignment, ObjectFile::Atom& internalAtom)
-: fOwner(owner), fName(name), fScope(scope), fKind(kind), fAlignment(alignment), fRealAtom(NULL)
-{
- // every Atom references the InternalAtom for its reader
- fReferences.push_back(new Reference(internalAtom));
-}
-
-
-//
-// ld64 only tracks non-internal symbols from an llvm bitcode file.
-// We model this by having an InternalAtom which represent all internal functions and data.
-// All non-interal symbols from a bitcode file are represented by a Atom
-// and each Atom has a reference to the InternalAtom. The InternalAtom
-// also has references to each symbol external to the bitcode file.
-//
-class InternalAtom : public ObjectFile::Atom
-{
-public:
- InternalAtom(class Reader& owner) : fOwner(owner) {}
-
- ObjectFile::Reader * getFile() const { return (ObjectFile::Reader*)&fOwner; }
- bool getTranslationUnitSource (const char **dir, const char **name) const
- { return false; }
- const char * getName () const { return "__llvm-internal-atom"; }
- const char * getDisplayName() const { return this->getName(); }
- Scope getScope() const { return scopeTranslationUnit; }
- DefinitionKind getDefinitionKind() const { return kRegularDefinition; }
- SymbolTableInclusion getSymbolTableInclusion() const { return kSymbolTableNotIn; }
- bool dontDeadStrip() const { return false; }
- bool isZeroFill() const { return false; }
- bool isThumb() const { return false; }
- uint64_t getSize() const { return 0; }
- std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)fReferences; }
- bool mustRemainInSection() const { return false; }
- const char * getSectionName() const { return NULL; }
- class ObjectFile::Section * getSection() const { return NULL; }
- ObjectFile::Segment& getSegment() const { return Segment::fgBootstrapSegment; }
- uint32_t getOrdinal() const { return 0; }
- ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); }
- std::vector<ObjectFile::LineInfo>* getLineInfo() const { return NULL; }
- ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); }
- void copyRawContent(uint8_t buffer[]) const { }
- void setScope(Scope s) { }
-
- void addReference(const char* targetName);
-
-private:
- class Reader& fOwner;
- std::vector<ObjectFile::Reference*> fReferences;
-};
-
-
-void InternalAtom::addReference(const char* name)
-{
- fReferences.push_back(new Reference(name));
-}
-
-
-
-
-class RemovableAtoms
-{
-public:
- RemovableAtoms(std::set<ObjectFile::Atom*>& iAtoms) : fAtoms(iAtoms) {}
-
- bool operator()(ObjectFile::Atom*& atom) const {
- return ( fAtoms.count(atom) != 0 );
- }
-
-private:
- std::set<ObjectFile::Atom*>& fAtoms;
-};
-
-
-
-//
-// LLVM bitcode file reader
-//
-class Reader : public ObjectFile::Reader
-{
-public:
- static bool validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture);
- static bool loaded() { return (::lto_get_version() != NULL); }
- Reader(const uint8_t* fileContent, uint64_t fileLength,
- const char* path, time_t modTime,
- const ObjectFile::ReaderOptions&, cpu_type_t arch);
- virtual ~Reader();
-
- virtual std::vector<class ObjectFile::Atom*>& getAtoms() { return (std::vector<class ObjectFile::Atom*>&)(fAtoms); }
- virtual std::vector<class ObjectFile::Atom*>* getJustInTimeAtomsFor(const char* name) { return NULL; }
- virtual const char* getPath() { return fPath; }
- virtual time_t getModificationTime() { return fModTime; }
- virtual ObjectFile::Reader::DebugInfoKind getDebugInfoKind() { return kDebugInfoNone; }
- virtual std::vector<Stab>* getStabs() { return NULL; }
- virtual void optimize(std::vector<ObjectFile::Atom*> &allAtoms, std::vector<ObjectFile::Atom*> &newAtoms,
- std::vector<const char*> &additionalUndefines, uint32_t nextInputOrdinal,
- ObjectFile::Reader* writer, bool allGlobalsAReDeadStripRoots,
- int outputKind, bool verbose, bool saveTemps, const char* outputFilePath,
- bool pie, bool allowTextRelocs);
-
-private:
-
- class CStringEquals
- {
- public:
- bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
- };
- typedef __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> CStringSet;
- typedef __gnu_cxx::hash_map<const char*, Atom*, __gnu_cxx::hash<const char*>, CStringEquals> CStringToAtom;
-
- ObjectFile::Reader* makeMachOReader(const uint8_t* p, size_t len, uint32_t nextInputOrdinal);
- static const char* tripletPrefixForArch(cpu_type_t);
-
- cpu_type_t fArchitecture;
- const char* fPath;
- time_t fModTime;
- lto_module_t fModule;
- std::vector<ObjectFile::Atom*> fAtoms;
- InternalAtom fInternalAtom;
- const ObjectFile::ReaderOptions& fReaderOptions;
- static std::set<Reader*> fgReaders;
- static bool fgOptimized;
-};
-
-bool Reader::fgOptimized = false;
-std::set<Reader*> Reader::fgReaders;
-
-
-Reader::~Reader()
-{
- if ( fModule != NULL )
- ::lto_module_dispose(fModule);
-}
-
-Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime,
- const ObjectFile::ReaderOptions& options, cpu_type_t arch)
- : fArchitecture(arch), fPath(path), fModTime(modTime), fInternalAtom(*this), fReaderOptions(options)
-{
- fgReaders.insert(this);
-
- fModule = ::lto_module_create_from_memory(fileContent, fileLength);
- if ( fModule == NULL )
- throwf("could not parse object file %s: %s", path, lto_get_error_message());
-
-
- uint32_t count = ::lto_module_get_num_symbols(fModule);
- for (uint32_t i=0; i < count; ++i) {
- const char* name = ::lto_module_get_symbol_name(fModule, i);
- lto_symbol_attributes attr = lto_module_get_symbol_attribute(fModule, i);
-
- ObjectFile::Atom::DefinitionKind kind;
- switch ( attr & LTO_SYMBOL_DEFINITION_MASK ) {
- case LTO_SYMBOL_DEFINITION_REGULAR:
- kind = ObjectFile::Atom::kRegularDefinition;
- break;
- case LTO_SYMBOL_DEFINITION_TENTATIVE:
- kind = ObjectFile::Atom::kTentativeDefinition;
- break;
- case LTO_SYMBOL_DEFINITION_WEAK:
- kind = ObjectFile::Atom::kWeakDefinition;
- break;
- case LTO_SYMBOL_DEFINITION_UNDEFINED:
- kind = ObjectFile::Atom::kExternalDefinition;
- break;
- default:
- throwf("unknown definition kind for symbol %s in bitcode file %s", name, path);
- }
-
- // make LLVM atoms for definitions and a reference for undefines
- if ( kind != ObjectFile::Atom::kExternalDefinition ) {
- ObjectFile::Atom::Scope scope;
- switch ( attr & LTO_SYMBOL_SCOPE_MASK) {
- case LTO_SYMBOL_SCOPE_INTERNAL:
- scope = ObjectFile::Atom::scopeTranslationUnit;
- break;
- case LTO_SYMBOL_SCOPE_HIDDEN:
- scope = ObjectFile::Atom::scopeLinkageUnit;
- break;
- case LTO_SYMBOL_SCOPE_DEFAULT:
- scope = ObjectFile::Atom::scopeGlobal;
- break;
- default:
- throwf("unknown scope for symbol %s in bitcode file %s", name, path);
- }
- // only make atoms for non-internal symbols
- if ( scope == ObjectFile::Atom::scopeTranslationUnit )
- continue;
- uint8_t alignment = (attr & LTO_SYMBOL_ALIGNMENT_MASK);
- // make Atom
- fAtoms.push_back(new Atom(*this, name, scope, kind, alignment, fInternalAtom));
- }
- else {
- // add to list of external references
- fInternalAtom.addReference(name);
- }
- }
-}
-
-const char* Reader::tripletPrefixForArch(cpu_type_t arch)
-{
- switch (arch) {
- case CPU_TYPE_POWERPC:
- return "powerpc-";
- case CPU_TYPE_POWERPC64:
- return "powerpc64-";
- case CPU_TYPE_I386:
- return "i386-";
- case CPU_TYPE_X86_64:
- return "x86_64-";
- case CPU_TYPE_ARM:
- return "arm-";
- }
- return "";
-}
-
-bool Reader::validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture)
-{
- return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, tripletPrefixForArch(architecture));
-}
-
-void Reader::optimize(std::vector<ObjectFile::Atom *>& allAtoms, std::vector<ObjectFile::Atom*>& newAtoms,
- std::vector<const char*>& additionalUndefines, uint32_t nextInputOrdinal,
- ObjectFile::Reader* writer, bool allGlobalsAReDeadStripRoots,
- int okind, bool verbose, bool saveTemps, const char* outputFilePath,
- bool pie, bool allowTextRelocs)
-{
- // this method is call on all Readers. We want the first call to trigger optimization
- // across all Readers and the subsequent calls to do nothing.
- if ( fgOptimized )
- return;
- fgOptimized = true;
-
- Options::OutputKind outputKind = (Options::OutputKind)okind; // HACK to work around upward dependency
-
- // print out LTO version string if -v was used
- if ( verbose )
- fprintf(stderr, "%s\n", lto_get_version());
-
- // create optimizer and add each Reader
- lto_code_gen_t generator = ::lto_codegen_create();
- for (std::set<Reader*>::iterator it=fgReaders.begin(); it != fgReaders.end(); ++it) {
- if ( ::lto_codegen_add_module(generator, (*it)->fModule) )
- throwf("lto: could not merge in %s", (*it)->fPath);
- }
-
- // the linker must preserve all globals in dylibs and flat images
- const bool globalsNeedPreserving = allGlobalsAReDeadStripRoots || fReaderOptions.fFlatNamespace;
-
- // The atom graph uses directed edges (references). Collect all references where
- // originating atom is not part of any LTO Reader. This allows optimizer to optimize an
- // external (i.e. not originated from same .o file) reference if all originating atoms are also
- // defined in llvm bitcode file.
- CStringSet nonLLVMRefs;
- CStringToAtom llvmAtoms;
- bool hasNonllvmAtoms = false;
- for (std::vector<ObjectFile::Atom*>::iterator it = allAtoms.begin(); it != allAtoms.end(); ++it) {
- ObjectFile::Atom* atom = *it;
- // only look at references come from an atom that is not an llvm atom
- if ( fgReaders.count((Reader*)(atom->getFile())) == 0 ) {
- // remember if we've seen an atoms not from an llvm reader and not from the writer
- if ( atom->getFile() != writer )
- hasNonllvmAtoms = true;
- std::vector<ObjectFile::Reference*>& refs = atom->getReferences();
- for (std::vector<ObjectFile::Reference*>::iterator ri=refs.begin(), re=refs.end(); ri != re; ++ri) {
- ObjectFile::Reference* ref = *ri;
- // add target name to set if target is an llvm atom
- if ( (ref->getTargetName() != NULL) && (fgReaders.count((Reader*)(ref->getTarget().getFile())) != 0) ) {
- nonLLVMRefs.insert(ref->getTargetName());
- }
- }
- }
- else {
- const char* name = atom->getName();
- if ( name != NULL )
- llvmAtoms[name] = (Atom*)atom;
- }
- }
- // tell code generator about symbols that must be preserved
- for (CStringToAtom::iterator it = llvmAtoms.begin(); it != llvmAtoms.end(); ++it) {
- const char* name = it->first;
- Atom* atom = it->second;
- // Include llvm Symbol in export list if it meets one of following two conditions
- // 1 - globals need preserving and atom scope is global (and not linkage unit).
- // 2 - included in nonLLVMRefs set.
- // If a symbol is not listed in exportList then LTO is free to optimize it away.
- if ( globalsNeedPreserving && (atom->getScope() == ObjectFile::Atom::scopeGlobal) )
- ::lto_codegen_add_must_preserve_symbol(generator, name);
- else if ( nonLLVMRefs.find(name) != nonLLVMRefs.end() )
- ::lto_codegen_add_must_preserve_symbol(generator, name);
- }
-
- // special case running ld -r on all bitcode files to produce another bitcode file (instead of mach-o)
- if ( (outputKind == Options::kObjectFile) && !hasNonllvmAtoms ) {
- if ( ! ::lto_codegen_write_merged_modules(generator, outputFilePath) ) {
- // HACK, no good way to tell linker we are all done, so just quit
- exit(0);
- }
- warning("could not produce merged bitcode file");
- }
-
- // if requested, save off merged bitcode file
- if ( saveTemps ) {
- char tempBitcodePath[MAXPATHLEN];
- strcpy(tempBitcodePath, outputFilePath);
- strcat(tempBitcodePath, ".lto.bc");
- ::lto_codegen_write_merged_modules(generator, tempBitcodePath);
- }
-
- // set code-gen model
- lto_codegen_model model = LTO_CODEGEN_PIC_MODEL_DYNAMIC;
- switch ( outputKind ) {
- case Options::kDynamicExecutable:
- if ( pie )
- model = LTO_CODEGEN_PIC_MODEL_DYNAMIC;
- else
- model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC;
- break;
- case Options::kDynamicLibrary:
- case Options::kDynamicBundle:
- case Options::kObjectFile: // ?? Is this appropriate ?
- case Options::kDyld:
- if ( allowTextRelocs )
- model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC;
- else
- model = LTO_CODEGEN_PIC_MODEL_DYNAMIC;
- break;
- case Options::kStaticExecutable:
- model = LTO_CODEGEN_PIC_MODEL_STATIC;
- break;
- }
- if ( ::lto_codegen_set_pic_model(generator, model) )
- throwf("could not create set codegen model: %s", lto_get_error_message());
-
- // run code generator
- size_t machOFileLen;
- const uint8_t* machOFile = (uint8_t*)::lto_codegen_compile(generator, &machOFileLen);
- if ( machOFile == NULL )
- throwf("could not do LTO codegen: %s", ::lto_get_error_message());
-
- // if requested, save off temp mach-o file
- if ( saveTemps ) {
- char tempMachoPath[MAXPATHLEN];
- strcpy(tempMachoPath, outputFilePath);
- strcat(tempMachoPath, ".lto.o");
- int fd = ::open(tempMachoPath, O_CREAT | O_WRONLY | O_TRUNC, 0666);
- if ( fd != -1) {
- ::write(fd, machOFile, machOFileLen);
- ::close(fd);
- }
- }
-
- // parse generated mach-o file into a MachOReader
- ObjectFile::Reader* machoReader = this->makeMachOReader(machOFile, machOFileLen, nextInputOrdinal);
-
- // sync generated mach-o atoms with existing atoms ld know about
- std::vector<ObjectFile::Atom*> machoAtoms = machoReader->getAtoms();
- for (std::vector<ObjectFile::Atom *>::iterator it = machoAtoms.begin(); it != machoAtoms.end(); ++it) {
- ObjectFile::Atom* atom = *it;
- const char* name = atom->getName();
- if ( name != NULL ) {
- CStringToAtom::iterator pos = llvmAtoms.find(name);
- if ( pos != llvmAtoms.end() ) {
- // turn Atom into a proxy for this mach-o atom
- pos->second->setRealAtom(atom);
- }
- else {
- // this atom is did not exist orginally, tell ld about it
- newAtoms.push_back(atom);
- }
- }
- else {
- // ld only knew about named atoms, so this one must be new
- newAtoms.push_back(atom);
- }
- std::vector<class ObjectFile::Reference*>& references = atom->getReferences();
- for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); ++rit) {
- ObjectFile::Reference* ref = *rit;
- const char* targetName = ref->getTargetName();
- CStringToAtom::iterator pos;
- if (targetName != NULL) {
- switch ( ref->getTargetBinding() ) {
- case ObjectFile::Reference::kUnboundByName:
- // accumulate unbounded references so that ld can bound them.
- additionalUndefines.push_back(targetName);
- break;
- case ObjectFile::Reference::kBoundDirectly:
- case ObjectFile::Reference::kBoundByName:
- // If mach-o atom is referencing another mach-o atom then
- // reference is not going through Atom proxy. Fix it here to ensure that all
- // llvm symbol references always go through Atom proxy.
- pos = llvmAtoms.find(targetName);
- if ( pos != llvmAtoms.end() )
- ref->setTarget(*pos->second, ref->getTargetOffset());
- break;
- case ObjectFile::Reference::kDontBind:
- break;
- }
- }
- }
- }
-
- // Remove InternalAtoms from ld
- std::set<class ObjectFile::Atom*> deletedAtoms;
- for (std::set<Reader*>::iterator it=fgReaders.begin(); it != fgReaders.end(); ++it) {
- deletedAtoms.insert(&((*it)->fInternalAtom));
- }
- // Remove Atoms from ld if code generator optimized them away
- for (CStringToAtom::iterator li = llvmAtoms.begin(), le = llvmAtoms.end(); li != le; ++li) {
- // check if setRealAtom() called on this Atom
- if ( li->second->getRealAtom() == NULL )
- deletedAtoms.insert(li->second);
- }
- allAtoms.erase(std::remove_if(allAtoms.begin(), allAtoms.end(), RemovableAtoms(deletedAtoms)), allAtoms.end());
-}
-
-
-ObjectFile::Reader* Reader::makeMachOReader(const uint8_t* p, size_t len, uint32_t nextInputOrdinal)
-{
- switch ( fArchitecture ) {
- case CPU_TYPE_POWERPC:
- if ( mach_o::relocatable::Reader<ppc>::validFile(p) )
- return new mach_o::relocatable::Reader<ppc>(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal);
- break;
- case CPU_TYPE_POWERPC64:
- if ( mach_o::relocatable::Reader<ppc64>::validFile(p) )
- return new mach_o::relocatable::Reader<ppc64>(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal);
- break;
- case CPU_TYPE_I386:
- if ( mach_o::relocatable::Reader<x86>::validFile(p) )
- return new mach_o::relocatable::Reader<x86>(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal);
- break;
- case CPU_TYPE_X86_64:
- if ( mach_o::relocatable::Reader<x86_64>::validFile(p) )
- return new mach_o::relocatable::Reader<x86_64>(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal);
- break;
- case CPU_TYPE_ARM:
- if ( mach_o::relocatable::Reader<arm>::validFile(p) )
- return new mach_o::relocatable::Reader<arm>(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal);
- break;
- }
- throw "LLVM LTO, file is not of required architecture";
-}
-
-}; // namespace lto
-
-
-void printLTOVersion(Options &opts) {
- const char* vers = lto_get_version();
- if ( vers != NULL )
- fprintf(stderr, "%s\n", vers);
-}
-
-
-#endif
-
+++ /dev/null
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
- *
- * Copyright (c) 2005-2008 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
-*/
-#ifndef __MACH_O_FILE_ABSTRACTION__
-#define __MACH_O_FILE_ABSTRACTION__
-
-#include <mach-o/loader.h>
-#include <mach-o/nlist.h>
-#include <mach-o/reloc.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 <mach/machine.h>
-
-#include "FileAbstraction.hpp"
-#include "Architectures.hpp"
-
-// stuff that will eventually go away once newer cctools headers are widespread
-#ifndef LC_LAZY_LOAD_DYLIB
- #define LC_LAZY_LOAD_DYLIB 0x20
-#endif
-#ifndef S_LAZY_DYLIB_SYMBOL_POINTERS
- #define S_LAZY_DYLIB_SYMBOL_POINTERS 0x10
-#endif
-#ifndef CPU_SUBTYPE_ARM_V5TEJ
- #define CPU_SUBTYPE_ARM_V5TEJ ((cpu_subtype_t) 7)
-#endif
-#ifndef CPU_SUBTYPE_ARM_XSCALE
- #define CPU_SUBTYPE_ARM_XSCALE ((cpu_subtype_t) 8)
-#endif
-#ifndef CPU_SUBTYPE_ARM_V7
- #define CPU_SUBTYPE_ARM_V7 ((cpu_subtype_t) 9)
-#endif
-#ifndef N_ARM_THUMB_DEF
- #define N_ARM_THUMB_DEF 0x0008
-#endif
-enum reloc_type_arm
-{
- ARM_RELOC_VANILLA, /* generic relocation as discribed above */
- ARM_RELOC_PAIR, /* the second relocation entry of a pair */
- ARM_RELOC_SECTDIFF, /* a PAIR follows with subtract symbol value */
- ARM_RELOC_LOCAL_SECTDIFF, /* like ARM_RELOC_SECTDIFF, but the symbol
- referenced was local. */
- ARM_RELOC_PB_LA_PTR,/* prebound lazy pointer */
- ARM_RELOC_BR24, /* 24 bit branch displacement (to a word address) */
- ARM_THUMB_RELOC_BR22, /* 22 bit branch displacement (to a half-word
- address) */
-};
-
-#ifndef LC_ENCRYPTION_INFO
- #define LC_ENCRYPTION_INFO 0x21
- struct encryption_info_command {
- uint32_t cmd;
- uint32_t cmdsize;
- uint32_t cryptoff; /* file offset of encrypted range */
- uint32_t cryptsize; /* file size of encrypted range */
- uint32_t cryptid; /* which enryption system, 0 means not-encrypted yet */
- };
-#endif
-
-
-//
-// This abstraction layer makes every mach-o file look like a 64-bit mach-o file with native endianness
-//
-
-
-
-//
-// mach-o file header
-//
-template <typename P> struct macho_header_content {};
-template <> struct macho_header_content<Pointer32<BigEndian> > { mach_header fields; };
-template <> struct macho_header_content<Pointer64<BigEndian> > { mach_header_64 fields; };
-template <> struct macho_header_content<Pointer32<LittleEndian> > { mach_header fields; };
-template <> struct macho_header_content<Pointer64<LittleEndian> > { mach_header_64 fields; };
-
-template <typename P>
-class macho_header {
-public:
- uint32_t magic() const INLINE { return E::get32(header.fields.magic); }
- void set_magic(uint32_t value) INLINE { E::set32(header.fields.magic, value); }
-
- uint32_t cputype() const INLINE { return E::get32(header.fields.cputype); }
- void set_cputype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cputype, value); }
-
- uint32_t cpusubtype() const INLINE { return E::get32(header.fields.cpusubtype); }
- void set_cpusubtype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cpusubtype, value); }
-
- uint32_t filetype() const INLINE { return E::get32(header.fields.filetype); }
- void set_filetype(uint32_t value) INLINE { E::set32(header.fields.filetype, value); }
-
- uint32_t ncmds() const INLINE { return E::get32(header.fields.ncmds); }
- void set_ncmds(uint32_t value) INLINE { E::set32(header.fields.ncmds, value); }
-
- uint32_t sizeofcmds() const INLINE { return E::get32(header.fields.sizeofcmds); }
- void set_sizeofcmds(uint32_t value) INLINE { E::set32(header.fields.sizeofcmds, value); }
-
- uint32_t flags() const INLINE { return E::get32(header.fields.flags); }
- void set_flags(uint32_t value) INLINE { E::set32(header.fields.flags, value); }
-
- uint32_t reserved() const INLINE { return E::get32(header.fields.reserved); }
- void set_reserved(uint32_t value) INLINE { E::set32(header.fields.reserved, value); }
-
- typedef typename P::E E;
-private:
- macho_header_content<P> header;
-};
-
-
-//
-// mach-o load command
-//
-template <typename P>
-class macho_load_command {
-public:
- uint32_t cmd() const INLINE { return E::get32(command.cmd); }
- void set_cmd(uint32_t value) INLINE { E::set32(command.cmd, value); }
-
- uint32_t cmdsize() const INLINE { return E::get32(command.cmdsize); }
- void set_cmdsize(uint32_t value) INLINE { E::set32(command.cmdsize, value); }
-
- typedef typename P::E E;
-private:
- load_command command;
-};
-
-
-//
-// mach-o segment load command
-//
-template <typename P> struct macho_segment_content {};
-template <> struct macho_segment_content<Pointer32<BigEndian> > { segment_command fields; enum { CMD = LC_SEGMENT }; };
-template <> struct macho_segment_content<Pointer64<BigEndian> > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; };
-template <> struct macho_segment_content<Pointer32<LittleEndian> > { segment_command fields; enum { CMD = LC_SEGMENT }; };
-template <> struct macho_segment_content<Pointer64<LittleEndian> > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; };
-
-template <typename P>
-class macho_segment_command {
-public:
- uint32_t cmd() const INLINE { return E::get32(segment.fields.cmd); }
- void set_cmd(uint32_t value) INLINE { E::set32(segment.fields.cmd, value); }
-
- uint32_t cmdsize() const INLINE { return E::get32(segment.fields.cmdsize); }
- void set_cmdsize(uint32_t value) INLINE { E::set32(segment.fields.cmdsize, value); }
-
- const char* segname() const INLINE { return segment.fields.segname; }
- void set_segname(const char* value) INLINE { 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); }
-
- uint64_t vmsize() const INLINE { return P::getP(segment.fields.vmsize); }
- void set_vmsize(uint64_t value) INLINE { P::setP(segment.fields.vmsize, value); }
-
- uint64_t fileoff() const INLINE { return P::getP(segment.fields.fileoff); }
- void set_fileoff(uint64_t value) INLINE { P::setP(segment.fields.fileoff, value); }
-
- uint64_t filesize() const INLINE { return P::getP(segment.fields.filesize); }
- void set_filesize(uint64_t value) INLINE { P::setP(segment.fields.filesize, value); }
-
- uint32_t maxprot() const INLINE { return E::get32(segment.fields.maxprot); }
- void set_maxprot(uint32_t value) INLINE { E::set32((uint32_t&)segment.fields.maxprot, value); }
-
- uint32_t initprot() const INLINE { return E::get32(segment.fields.initprot); }
- void set_initprot(uint32_t value) INLINE { E::set32((uint32_t&)segment.fields.initprot, value); }
-
- uint32_t nsects() const INLINE { return E::get32(segment.fields.nsects); }
- void set_nsects(uint32_t value) INLINE { E::set32(segment.fields.nsects, value); }
-
- uint32_t flags() const INLINE { return E::get32(segment.fields.flags); }
- void set_flags(uint32_t value) INLINE { E::set32(segment.fields.flags, value); }
-
- enum {
- CMD = macho_segment_content<P>::CMD
- };
-
- typedef typename P::E E;
-private:
- macho_segment_content<P> segment;
-};
-
-
-//
-// mach-o section
-//
-template <typename P> struct macho_section_content {};
-template <> struct macho_section_content<Pointer32<BigEndian> > { section fields; };
-template <> struct macho_section_content<Pointer64<BigEndian> > { section_64 fields; };
-template <> struct macho_section_content<Pointer32<LittleEndian> > { section fields; };
-template <> struct macho_section_content<Pointer64<LittleEndian> > { section_64 fields; };
-
-template <typename P>
-class macho_section {
-public:
- const char* sectname() const INLINE { return section.fields.sectname; }
- 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 { 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); }
-
- uint64_t size() const INLINE { return P::getP(section.fields.size); }
- void set_size(uint64_t value) INLINE { P::setP(section.fields.size, value); }
-
- uint32_t offset() const INLINE { return E::get32(section.fields.offset); }
- void set_offset(uint32_t value) INLINE { E::set32(section.fields.offset, value); }
-
- uint32_t align() const INLINE { return E::get32(section.fields.align); }
- void set_align(uint32_t value) INLINE { E::set32(section.fields.align, value); }
-
- uint32_t reloff() const INLINE { return E::get32(section.fields.reloff); }
- void set_reloff(uint32_t value) INLINE { E::set32(section.fields.reloff, value); }
-
- uint32_t nreloc() const INLINE { return E::get32(section.fields.nreloc); }
- void set_nreloc(uint32_t value) INLINE { E::set32(section.fields.nreloc, value); }
-
- uint32_t flags() const INLINE { return E::get32(section.fields.flags); }
- void set_flags(uint32_t value) INLINE { E::set32(section.fields.flags, value); }
-
- uint32_t reserved1() const INLINE { return E::get32(section.fields.reserved1); }
- void set_reserved1(uint32_t value) INLINE { E::set32(section.fields.reserved1, value); }
-
- uint32_t reserved2() const INLINE { return E::get32(section.fields.reserved2); }
- void set_reserved2(uint32_t value) INLINE { E::set32(section.fields.reserved2, value); }
-
- typedef typename P::E E;
-private:
- macho_section_content<P> section;
-};
-
-
-//
-// mach-o dylib load command
-//
-template <typename P>
-class macho_dylib_command {
-public:
- uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
- void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
-
- uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
- void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
-
- uint32_t name_offset() const INLINE { return E::get32(fields.dylib.name.offset); }
- void set_name_offset(uint32_t value) INLINE { E::set32(fields.dylib.name.offset, value); }
-
- uint32_t timestamp() const INLINE { return E::get32(fields.dylib.timestamp); }
- void set_timestamp(uint32_t value) INLINE { E::set32(fields.dylib.timestamp, value); }
-
- uint32_t current_version() const INLINE { return E::get32(fields.dylib.current_version); }
- void set_current_version(uint32_t value) INLINE { E::set32(fields.dylib.current_version, value); }
-
- uint32_t compatibility_version() const INLINE { return E::get32(fields.dylib.compatibility_version); }
- void set_compatibility_version(uint32_t value) INLINE { E::set32(fields.dylib.compatibility_version, value); }
-
- const char* name() const INLINE { return (const char*)&fields + name_offset(); }
- void set_name_offset() INLINE { set_name_offset(sizeof(fields)); }
-
- typedef typename P::E E;
-private:
- dylib_command fields;
-};
-
-
-//
-// mach-o dylinker load command
-//
-template <typename P>
-class macho_dylinker_command {
-public:
- uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
- void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
-
- uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
- void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
-
- uint32_t name_offset() const INLINE { return E::get32(fields.name.offset); }
- void set_name_offset(uint32_t value) INLINE { E::set32(fields.name.offset, value); }
-
- const char* name() const INLINE { return (const char*)&fields + name_offset(); }
- void set_name_offset() INLINE { set_name_offset(sizeof(fields)); }
-
- typedef typename P::E E;
-private:
- dylinker_command fields;
-};
-
-
-//
-// mach-o sub_framework load command
-//
-template <typename P>
-class macho_sub_framework_command {
-public:
- uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
- void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
-
- uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
- void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
-
- uint32_t umbrella_offset() const INLINE { return E::get32(fields.umbrella.offset); }
- void set_umbrella_offset(uint32_t value) INLINE { E::set32(fields.umbrella.offset, value); }
-
- const char* umbrella() const INLINE { return (const char*)&fields + umbrella_offset(); }
- void set_umbrella_offset() INLINE { set_umbrella_offset(sizeof(fields)); }
-
- typedef typename P::E E;
-private:
- sub_framework_command fields;
-};
-
-
-//
-// mach-o sub_client load command
-//
-template <typename P>
-class macho_sub_client_command {
-public:
- uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
- void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
-
- uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
- void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
-
- uint32_t client_offset() const INLINE { return E::get32(fields.client.offset); }
- void set_client_offset(uint32_t value) INLINE { E::set32(fields.client.offset, value); }
-
- const char* client() const INLINE { return (const char*)&fields + client_offset(); }
- void set_client_offset() INLINE { set_client_offset(sizeof(fields)); }
-
- typedef typename P::E E;
-private:
- sub_client_command fields;
-};
-
-
-//
-// mach-o sub_umbrella load command
-//
-template <typename P>
-class macho_sub_umbrella_command {
-public:
- uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
- void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
-
- uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
- void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
-
- uint32_t sub_umbrella_offset() const INLINE { return E::get32(fields.sub_umbrella.offset); }
- void set_sub_umbrella_offset(uint32_t value) INLINE { E::set32(fields.sub_umbrella.offset, value); }
-
- const char* sub_umbrella() const INLINE { return (const char*)&fields + sub_umbrella_offset(); }
- void set_sub_umbrella_offset() INLINE { set_sub_umbrella_offset(sizeof(fields)); }
-
- typedef typename P::E E;
-private:
- sub_umbrella_command fields;
-};
-
-
-//
-// mach-o sub_library load command
-//
-template <typename P>
-class macho_sub_library_command {
-public:
- uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
- void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
-
- uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
- void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
-
- uint32_t sub_library_offset() const INLINE { return E::get32(fields.sub_library.offset); }
- void set_sub_library_offset(uint32_t value) INLINE { E::set32(fields.sub_library.offset, value); }
-
- const char* sub_library() const INLINE { return (const char*)&fields + sub_library_offset(); }
- void set_sub_library_offset() INLINE { set_sub_library_offset(sizeof(fields)); }
-
- typedef typename P::E E;
-private:
- sub_library_command fields;
-};
-
-
-//
-// mach-o uuid load command
-//
-template <typename P>
-class macho_uuid_command {
-public:
- uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
- void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
-
- uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
- void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
-
- const uint8_t* uuid() const INLINE { return fields.uuid; }
- void set_uuid(uint8_t uuid[16]) INLINE { memcpy(&fields.uuid, uuid, 16); }
-
- typedef typename P::E E;
-private:
- uuid_command fields;
-};
-
-
-//
-// mach-o routines load command
-//
-template <typename P> struct macho_routines_content {};
-template <> struct macho_routines_content<Pointer32<BigEndian> > { routines_command fields; enum { CMD = LC_ROUTINES }; };
-template <> struct macho_routines_content<Pointer64<BigEndian> > { routines_command_64 fields; enum { CMD = LC_ROUTINES_64 }; };
-template <> struct macho_routines_content<Pointer32<LittleEndian> > { routines_command fields; enum { CMD = LC_ROUTINES }; };
-template <> struct macho_routines_content<Pointer64<LittleEndian> > { routines_command_64 fields; enum { CMD = LC_ROUTINES_64 }; };
-
-template <typename P>
-class macho_routines_command {
-public:
- uint32_t cmd() const INLINE { return E::get32(routines.fields.cmd); }
- void set_cmd(uint32_t value) INLINE { E::set32(routines.fields.cmd, value); }
-
- uint32_t cmdsize() const INLINE { return E::get32(routines.fields.cmdsize); }
- void set_cmdsize(uint32_t value) INLINE { E::set32(routines.fields.cmdsize, value); }
-
- uint64_t init_address() const INLINE { return P::getP(routines.fields.init_address); }
- void set_init_address(uint64_t value) INLINE { P::setP(routines.fields.init_address, value); }
-
- uint64_t init_module() const INLINE { return P::getP(routines.fields.init_module); }
- void set_init_module(uint64_t value) INLINE { P::setP(routines.fields.init_module, value); }
-
- uint64_t reserved1() const INLINE { return P::getP(routines.fields.reserved1); }
- void set_reserved1(uint64_t value) INLINE { P::setP(routines.fields.reserved1, value); }
-
- uint64_t reserved2() const INLINE { return P::getP(routines.fields.reserved2); }
- void set_reserved2(uint64_t value) INLINE { P::setP(routines.fields.reserved2, value); }
-
- uint64_t reserved3() const INLINE { return P::getP(routines.fields.reserved3); }
- void set_reserved3(uint64_t value) INLINE { P::setP(routines.fields.reserved3, value); }
-
- uint64_t reserved4() const INLINE { return P::getP(routines.fields.reserved4); }
- void set_reserved4(uint64_t value) INLINE { P::setP(routines.fields.reserved4, value); }
-
- uint64_t reserved5() const INLINE { return P::getP(routines.fields.reserved5); }
- void set_reserved5(uint64_t value) INLINE { P::setP(routines.fields.reserved5, value); }
-
- uint64_t reserved6() const INLINE { return P::getP(routines.fields.reserved6); }
- void set_reserved6(uint64_t value) INLINE { P::setP(routines.fields.reserved6, value); }
-
- typedef typename P::E E;
- enum {
- CMD = macho_routines_content<P>::CMD
- };
-private:
- macho_routines_content<P> routines;
-};
-
-
-//
-// mach-o symbol table load command
-//
-template <typename P>
-class macho_symtab_command {
-public:
- uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
- void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
-
- uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
- void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
-
- uint32_t symoff() const INLINE { return E::get32(fields.symoff); }
- void set_symoff(uint32_t value) INLINE { E::set32(fields.symoff, value); }
-
- uint32_t nsyms() const INLINE { return E::get32(fields.nsyms); }
- void set_nsyms(uint32_t value) INLINE { E::set32(fields.nsyms, value); }
-
- uint32_t stroff() const INLINE { return E::get32(fields.stroff); }
- void set_stroff(uint32_t value) INLINE { E::set32(fields.stroff, value); }
-
- uint32_t strsize() const INLINE { return E::get32(fields.strsize); }
- void set_strsize(uint32_t value) INLINE { E::set32(fields.strsize, value); }
-
-
- typedef typename P::E E;
-private:
- symtab_command fields;
-};
-
-
-//
-// mach-o dynamic symbol table load command
-//
-template <typename P>
-class macho_dysymtab_command {
-public:
- uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
- void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
-
- uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
- void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
-
- uint32_t ilocalsym() const INLINE { return E::get32(fields.ilocalsym); }
- void set_ilocalsym(uint32_t value) INLINE { E::set32(fields.ilocalsym, value); }
-
- uint32_t nlocalsym() const INLINE { return E::get32(fields.nlocalsym); }
- void set_nlocalsym(uint32_t value) INLINE { E::set32(fields.nlocalsym, value); }
-
- uint32_t iextdefsym() const INLINE { return E::get32(fields.iextdefsym); }
- void set_iextdefsym(uint32_t value) INLINE { E::set32(fields.iextdefsym, value); }
-
- uint32_t nextdefsym() const INLINE { return E::get32(fields.nextdefsym); }
- void set_nextdefsym(uint32_t value) INLINE { E::set32(fields.nextdefsym, value); }
-
- uint32_t iundefsym() const INLINE { return E::get32(fields.iundefsym); }
- void set_iundefsym(uint32_t value) INLINE { E::set32(fields.iundefsym, value); }
-
- uint32_t nundefsym() const INLINE { return E::get32(fields.nundefsym); }
- void set_nundefsym(uint32_t value) INLINE { E::set32(fields.nundefsym, value); }
-
- uint32_t tocoff() const INLINE { return E::get32(fields.tocoff); }
- void set_tocoff(uint32_t value) INLINE { E::set32(fields.tocoff, value); }
-
- uint32_t ntoc() const INLINE { return E::get32(fields.ntoc); }
- void set_ntoc(uint32_t value) INLINE { E::set32(fields.ntoc, value); }
-
- uint32_t modtaboff() const INLINE { return E::get32(fields.modtaboff); }
- void set_modtaboff(uint32_t value) INLINE { E::set32(fields.modtaboff, value); }
-
- uint32_t nmodtab() const INLINE { return E::get32(fields.nmodtab); }
- void set_nmodtab(uint32_t value) INLINE { E::set32(fields.nmodtab, value); }
-
- uint32_t extrefsymoff() const INLINE { return E::get32(fields.extrefsymoff); }
- void set_extrefsymoff(uint32_t value) INLINE { E::set32(fields.extrefsymoff, value); }
-
- uint32_t nextrefsyms() const INLINE { return E::get32(fields.nextrefsyms); }
- void set_nextrefsyms(uint32_t value) INLINE { E::set32(fields.nextrefsyms, value); }
-
- uint32_t indirectsymoff() const INLINE { return E::get32(fields.indirectsymoff); }
- void set_indirectsymoff(uint32_t value) INLINE { E::set32(fields.indirectsymoff, value); }
-
- uint32_t nindirectsyms() const INLINE { return E::get32(fields.nindirectsyms); }
- void set_nindirectsyms(uint32_t value) INLINE { E::set32(fields.nindirectsyms, value); }
-
- uint32_t extreloff() const INLINE { return E::get32(fields.extreloff); }
- void set_extreloff(uint32_t value) INLINE { E::set32(fields.extreloff, value); }
-
- uint32_t nextrel() const INLINE { return E::get32(fields.nextrel); }
- void set_nextrel(uint32_t value) INLINE { E::set32(fields.nextrel, value); }
-
- uint32_t locreloff() const INLINE { return E::get32(fields.locreloff); }
- void set_locreloff(uint32_t value) INLINE { E::set32(fields.locreloff, value); }
-
- uint32_t nlocrel() const INLINE { return E::get32(fields.nlocrel); }
- void set_nlocrel(uint32_t value) INLINE { E::set32(fields.nlocrel, value); }
-
- typedef typename P::E E;
-private:
- dysymtab_command fields;
-};
-
-
-
-
-//
-// mach-o module table entry (for compatibility with old ld/dyld)
-//
-template <typename P> struct macho_dylib_module_content {};
-template <> struct macho_dylib_module_content<Pointer32<BigEndian> > { struct dylib_module fields; };
-template <> struct macho_dylib_module_content<Pointer32<LittleEndian> > { struct dylib_module fields; };
-template <> struct macho_dylib_module_content<Pointer64<BigEndian> > { struct dylib_module_64 fields; };
-template <> struct macho_dylib_module_content<Pointer64<LittleEndian> > { struct dylib_module_64 fields; };
-
-template <typename P>
-class macho_dylib_module {
-public:
- uint32_t module_name() const INLINE { return E::get32(module.fields.module_name); }
- void set_module_name(uint32_t value) INLINE { E::set32(module.fields.module_name, value); }
-
- uint32_t iextdefsym() const INLINE { return E::get32(module.fields.iextdefsym); }
- void set_iextdefsym(uint32_t value) INLINE { E::set32(module.fields.iextdefsym, value); }
-
- uint32_t nextdefsym() const INLINE { return E::get32(module.fields.nextdefsym); }
- void set_nextdefsym(uint32_t value) INLINE { E::set32(module.fields.nextdefsym, value); }
-
- uint32_t irefsym() const INLINE { return E::get32(module.fields.irefsym); }
- void set_irefsym(uint32_t value) INLINE { E::set32(module.fields.irefsym, value); }
-
- uint32_t nrefsym() const INLINE { return E::get32(module.fields.nrefsym); }
- void set_nrefsym(uint32_t value) INLINE { E::set32(module.fields.nrefsym, value); }
-
- uint32_t ilocalsym() const INLINE { return E::get32(module.fields.ilocalsym); }
- void set_ilocalsym(uint32_t value) INLINE { E::set32(module.fields.ilocalsym, value); }
-
- uint32_t nlocalsym() const INLINE { return E::get32(module.fields.nlocalsym); }
- void set_nlocalsym(uint32_t value) INLINE { E::set32(module.fields.nlocalsym, value); }
-
- uint32_t iextrel() const INLINE { return E::get32(module.fields.iextrel); }
- void set_iextrel(uint32_t value) INLINE { E::set32(module.fields.iextrel, value); }
-
- uint32_t nextrel() const INLINE { return E::get32(module.fields.nextrel); }
- void set_nextrel(uint32_t value) INLINE { E::set32(module.fields.nextrel, value); }
-
- uint16_t iinit() const INLINE { return E::get32(module.fields.iinit_iterm) & 0xFFFF; }
- uint16_t iterm() const INLINE { return E::get32(module.fields.iinit_iterm) > 16; }
- void set_iinit_iterm(uint16_t init, uint16_t term) INLINE { E::set32(module.fields.iinit_iterm, (term<<16) | (init &0xFFFF)); }
-
- uint16_t ninit() const INLINE { return E::get32(module.fields.ninit_nterm) & 0xFFFF; }
- uint16_t nterm() const INLINE { return E::get32(module.fields.ninit_nterm) > 16; }
- void set_ninit_nterm(uint16_t init, uint16_t term) INLINE { E::set32(module.fields.ninit_nterm, (term<<16) | (init &0xFFFF)); }
-
- uint64_t objc_module_info_addr() const INLINE { return P::getP(module.fields.objc_module_info_addr); }
- void set_objc_module_info_addr(uint64_t value) INLINE { P::setP(module.fields.objc_module_info_addr, value); }
-
- uint32_t objc_module_info_size() const INLINE { return E::get32(module.fields.objc_module_info_size); }
- void set_objc_module_info_size(uint32_t value) INLINE { E::set32(module.fields.objc_module_info_size, value); }
-
-
- typedef typename P::E E;
-private:
- macho_dylib_module_content<P> module;
-};
-
-
-//
-// mach-o dylib_reference entry
-//
-template <typename P>
-class macho_dylib_reference {
-public:
- uint32_t isym() const INLINE { return E::getBits(fields, 0, 24); }
- void set_isym(uint32_t value) INLINE { E::setBits(fields, value, 0, 24); }
-
- uint8_t flags() const INLINE { return E::getBits(fields, 24, 8); }
- void set_flags(uint8_t value) INLINE { E::setBits(fields, value, 24, 8); }
-
- typedef typename P::E E;
-private:
- uint32_t fields;
-};
-
-
-
-//
-// mach-o two-level hints load command
-//
-template <typename P>
-class macho_dylib_table_of_contents {
-public:
- uint32_t symbol_index() const INLINE { return E::get32(fields.symbol_index); }
- void set_symbol_index(uint32_t value) INLINE { E::set32(fields.symbol_index, value); }
-
- uint32_t module_index() const INLINE { return E::get32(fields.module_index); }
- void set_module_index(uint32_t value) INLINE { E::set32(fields.module_index, value); }
-
- typedef typename P::E E;
-private:
- dylib_table_of_contents fields;
-};
-
-
-
-//
-// mach-o two-level hints load command
-//
-template <typename P>
-class macho_twolevel_hints_command {
-public:
- uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
- void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
-
- uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
- void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
-
- uint32_t offset() const INLINE { return E::get32(fields.offset); }
- void set_offset(uint32_t value) INLINE { E::set32(fields.offset, value); }
-
- uint32_t nhints() const INLINE { return E::get32(fields.nhints); }
- void set_nhints(uint32_t value) INLINE { E::set32(fields.nhints, value); }
-
- typedef typename P::E E;
-private:
- twolevel_hints_command fields;
-};
-
-
-//
-// mach-o threads load command
-//
-template <typename P>
-class macho_thread_command {
-public:
- uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
- void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
-
- uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
- void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
-
- uint32_t flavor() const INLINE { return E::get32(fields_flavor); }
- void set_flavor(uint32_t value) INLINE { E::set32(fields_flavor, value); }
-
- uint32_t count() const INLINE { return E::get32(fields_count); }
- void set_count(uint32_t value) INLINE { E::set32(fields_count, value); }
-
- uint64_t thread_register(uint32_t index) const INLINE { return P::getP(thread_registers[index]); }
- void set_thread_register(uint32_t index, uint64_t value) INLINE { P::setP(thread_registers[index], value); }
-
- typedef typename P::E E;
- typedef typename P::uint_t pint_t;
-private:
- struct thread_command fields;
- uint32_t fields_flavor;
- uint32_t fields_count;
- pint_t thread_registers[1];
-};
-
-
-//
-// mach-o misc data
-//
-template <typename P>
-class macho_linkedit_data_command {
-public:
- uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
- void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
-
- uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
- void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
-
- uint32_t dataoff() const INLINE { return E::get32(fields.dataoff); }
- void set_dataoff(uint32_t value) INLINE { E::set32(fields.dataoff, value); }
-
- uint32_t datasize() const INLINE { return E::get32(fields.datasize); }
- void set_datasize(uint32_t value)INLINE { E::set32(fields.datasize, value); }
-
-
- typedef typename P::E E;
-private:
- struct linkedit_data_command fields;
-};
-
-
-//
-// mach-o rpath
-//
-template <typename P>
-class macho_rpath_command {
-public:
- uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
- void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
-
- uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
- void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
-
- uint32_t path_offset() const INLINE { return E::get32(fields.path.offset); }
- void set_path_offset(uint32_t value) INLINE { E::set32(fields.path.offset, value); }
-
- const char* path() const INLINE { return (const char*)&fields + path_offset(); }
- void set_path_offset() INLINE { set_path_offset(sizeof(fields)); }
-
-
- typedef typename P::E E;
-private:
- struct rpath_command fields;
-};
-
-
-
-//
-// mach-o symbol table entry
-//
-template <typename P> struct macho_nlist_content {};
-template <> struct macho_nlist_content<Pointer32<BigEndian> > { struct nlist fields; };
-template <> struct macho_nlist_content<Pointer64<BigEndian> > { struct nlist_64 fields; };
-template <> struct macho_nlist_content<Pointer32<LittleEndian> > { struct nlist fields; };
-template <> struct macho_nlist_content<Pointer64<LittleEndian> > { struct nlist_64 fields; };
-
-template <typename P>
-class macho_nlist {
-public:
- uint32_t n_strx() const INLINE { return E::get32(entry.fields.n_un.n_strx); }
- void set_n_strx(uint32_t value) INLINE { E::set32((uint32_t&)entry.fields.n_un.n_strx, value); }
-
- uint8_t n_type() const INLINE { return entry.fields.n_type; }
- void set_n_type(uint8_t value) INLINE { entry.fields.n_type = value; }
-
- uint8_t n_sect() const INLINE { return entry.fields.n_sect; }
- void set_n_sect(uint8_t value) INLINE { entry.fields.n_sect = value; }
-
- uint16_t n_desc() const INLINE { return E::get16(entry.fields.n_desc); }
- void set_n_desc(uint16_t value) INLINE { E::set16((uint16_t&)entry.fields.n_desc, value); }
-
- uint64_t n_value() const INLINE { return P::getP(entry.fields.n_value); }
- void set_n_value(uint64_t value) INLINE { P::setP(entry.fields.n_value, value); }
-
- typedef typename P::E E;
-private:
- macho_nlist_content<P> entry;
-};
-
-
-
-//
-// mach-o relocation info
-//
-template <typename P>
-class macho_relocation_info {
-public:
- uint32_t r_address() const INLINE { return E::get32(address); }
- void set_r_address(uint32_t value) INLINE { E::set32(address, value); }
-
- uint32_t r_symbolnum() const INLINE { return E::getBits(other, 0, 24); }
- void set_r_symbolnum(uint32_t value) INLINE { E::setBits(other, value, 0, 24); }
-
- bool r_pcrel() const INLINE { return E::getBits(other, 24, 1); }
- void set_r_pcrel(bool value) INLINE { E::setBits(other, value, 24, 1); }
-
- uint8_t r_length() const INLINE { return E::getBits(other, 25, 2); }
- void set_r_length(uint8_t value) INLINE { E::setBits(other, value, 25, 2); }
-
- bool r_extern() const INLINE { return E::getBits(other, 27, 1); }
- void set_r_extern(bool value) INLINE { E::setBits(other, value, 27, 1); }
-
- uint8_t r_type() const INLINE { return E::getBits(other, 28, 4); }
- void set_r_type(uint8_t value) INLINE { E::setBits(other, value, 28, 4); }
-
- void set_r_length() INLINE { set_r_length((sizeof(typename P::uint_t)==8) ? 3 : 2); }
-
- typedef typename P::E E;
-private:
- uint32_t address;
- uint32_t other;
-};
-
-
-//
-// mach-o scattered relocation info
-// The bit fields are always in big-endian order (see mach-o/reloc.h)
-//
-template <typename P>
-class macho_scattered_relocation_info {
-public:
- bool r_scattered() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 0, 1); }
- void set_r_scattered(bool x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 0, 1); E::set32(other, temp); }
-
- bool r_pcrel() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 1, 1); }
- void set_r_pcrel(bool x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 1, 1); E::set32(other, temp); }
-
- uint8_t r_length() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 2, 2); }
- void set_r_length(uint8_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 2, 2); E::set32(other, temp); }
-
- uint8_t r_type() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 4, 4); }
- void set_r_type(uint8_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 4, 4); E::set32(other, temp); }
-
- uint32_t r_address() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 8, 24); }
- void set_r_address(uint32_t x) { if ( x > 0x00FFFFFF ) throw "scattered reloc r_address too large";
- uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 8, 24); E::set32(other, temp); }
-
- uint32_t r_value() const INLINE { return E::get32(value); }
- void set_r_value(uint32_t x) INLINE { E::set32(value, x); }
-
- uint32_t r_other() const INLINE { return other; }
-
- void set_r_length() INLINE { set_r_length((sizeof(typename P::uint_t)==8) ? 3 : 2); }
-
- typedef typename P::E E;
-private:
- uint32_t other;
- uint32_t value;
-};
-
-
-
-//
-// mach-o encyrption info load command
-//
-template <typename P>
-class macho_encryption_info_command {
-public:
- uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
- void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
-
- uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
- void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
-
- uint32_t cryptoff() const INLINE { return E::get32(fields.cryptoff); }
- void set_cryptoff(uint32_t value) INLINE { E::set32(fields.cryptoff, value); }
-
- uint32_t cryptsize() const INLINE { return E::get32(fields.cryptsize); }
- void set_cryptsize(uint32_t value) INLINE { E::set32(fields.cryptsize, value); }
-
- uint32_t cryptid() const INLINE { return E::get32(fields.cryptid); }
- void set_cryptid(uint32_t value) INLINE { E::set32(fields.cryptid, value); }
-
- typedef typename P::E E;
-private:
- encryption_info_command fields;
-};
-
-
-
-#endif // __MACH_O_FILE_ABSTRACTION__
-
-
+++ /dev/null
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
- *
- * Copyright (c) 2005-2007 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#ifndef __OBJECT_FILE_DYLIB_MACH_O__
-#define __OBJECT_FILE_DYLIB_MACH_O__
-
-#include <stdint.h>
-#include <math.h>
-#include <unistd.h>
-#include <sys/param.h>
-
-
-#include <vector>
-#include <set>
-#include <algorithm>
-#include <ext/hash_map>
-
-#include "MachOFileAbstraction.hpp"
-#include "ObjectFile.h"
-
-//
-//
-// To implement architecture xxx, you must write template specializations for the following method:
-// Reader<xxx>::validFile()
-//
-//
-
-
-
-
-namespace mach_o {
-namespace dylib {
-
-
-// forward reference
-template <typename A> class Reader;
-
-
-class Segment : public ObjectFile::Segment
-{
-public:
- Segment(const char* name) { fName = name; }
- virtual const char* getName() const { return fName; }
- virtual bool isContentReadable() const { return true; }
- virtual bool isContentWritable() const { return false; }
- virtual bool isContentExecutable() const { return false; }
-private:
- const char* fName;
-};
-
-
-//
-// An ExportAtom has no content. It exists so that the linker can track which imported
-// symbols came from which dynamic libraries.
-//
-template <typename A>
-class ExportAtom : public ObjectFile::Atom
-{
-public:
- virtual ObjectFile::Reader* getFile() const { return &fOwner; }
- virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; }
- virtual const char* getName() const { return fName; }
- virtual const char* getDisplayName() const { return fName; }
- virtual Scope getScope() const { return ObjectFile::Atom::scopeGlobal; }
- virtual DefinitionKind getDefinitionKind() const { return fWeakDefinition ? kExternalWeakDefinition : kExternalDefinition; }
- virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableIn; }
- virtual bool dontDeadStrip() const { return false; }
- virtual bool isZeroFill() const { return false; }
- virtual bool isThumb() const { return false; }
- virtual uint64_t getSize() const { return 0; }
- virtual std::vector<ObjectFile::Reference*>& getReferences() const { return fgEmptyReferenceList; }
- virtual bool mustRemainInSection() const { return false; }
- virtual const char* getSectionName() const { return "._imports"; }
- virtual Segment& getSegment() const { return fgImportSegment; }
- virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); }
- virtual uint32_t getOrdinal() const { return fOrdinal; }
- virtual std::vector<ObjectFile::LineInfo>* getLineInfo() const { return NULL; }
- virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); }
- virtual void copyRawContent(uint8_t buffer[]) const {}
-
- virtual void setScope(Scope) { }
-
-protected:
- friend class Reader<A>;
- typedef typename A::P P;
-
- ExportAtom(ObjectFile::Reader& owner, const char* name, bool weak, uint32_t ordinal)
- : fOwner(owner), fName(name), fOrdinal(ordinal), fWeakDefinition(weak) {}
- virtual ~ExportAtom() {}
-
- ObjectFile::Reader& fOwner;
- const char* fName;
- uint32_t fOrdinal;
- bool fWeakDefinition;
-
- static std::vector<ObjectFile::Reference*> fgEmptyReferenceList;
- static Segment fgImportSegment;
-};
-
-template <typename A>
-Segment ExportAtom<A>::fgImportSegment("__LINKEDIT");
-
-template <typename A>
-std::vector<ObjectFile::Reference*> ExportAtom<A>::fgEmptyReferenceList;
-
-
-
-class ImportReference : public ObjectFile::Reference
-{
-public:
- ImportReference(const char* name)
- : fTarget(NULL), fTargetName(strdup(name)) {}
- virtual ~ImportReference() {}
-
-
- virtual ObjectFile::Reference::TargetBinding getTargetBinding() const { return (fTarget==NULL) ? ObjectFile::Reference::kUnboundByName : ObjectFile::Reference::kBoundByName; }
- virtual ObjectFile::Reference::TargetBinding getFromTargetBinding() const{ return ObjectFile::Reference::kDontBind; }
- virtual uint8_t getKind() const { return 0; }
- virtual uint64_t getFixUpOffset() const { return 0; }
- virtual const char* getTargetName() const { return fTargetName; }
- virtual ObjectFile::Atom& getTarget() const { return *((ObjectFile::Atom*)fTarget); }
- virtual uint64_t getTargetOffset() const { return 0; }
- virtual ObjectFile::Atom& getFromTarget() const { return *((ObjectFile::Atom*)NULL); }
- virtual const char* getFromTargetName() const { return NULL; }
- virtual uint64_t getFromTargetOffset() const { return 0; }
- virtual void setTarget(ObjectFile::Atom& atom, uint64_t offset) { fTarget = &atom; }
- virtual void setFromTarget(ObjectFile::Atom&) { throw "can't set from target"; }
- virtual const char* getDescription() const { return "dylib import reference"; }
-
-private:
- const ObjectFile::Atom* fTarget;
- const char* fTargetName;
-};
-
-
-//
-// An ImportAtom has no content. It exists so that when linking a main executable flat-namespace
-// the imports of all flat dylibs are checked
-//
-template <typename A>
-class ImportAtom : public ObjectFile::Atom
-{
-public:
- virtual ObjectFile::Reader* getFile() const { return &fOwner; }
- virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; }
- virtual const char* getName() const { return "flat-imports"; }
- virtual const char* getDisplayName() const { return "flat_namespace undefines"; }
- 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 false; }
- virtual bool isZeroFill() const { return false; }
- virtual bool isThumb() const { return false; }
- virtual uint64_t getSize() const { return 0; }
- virtual std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(fReferences); }
- virtual bool mustRemainInSection() const { return false; }
- virtual const char* getSectionName() const { return "._imports"; }
- virtual Segment& getSegment() const { return fgImportSegment; }
- virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); }
- virtual uint32_t getOrdinal() const { return fOrdinal; }
- virtual std::vector<ObjectFile::LineInfo>* getLineInfo() const { return NULL; }
- virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); }
- virtual void copyRawContent(uint8_t buffer[]) const {}
-
- virtual void setScope(Scope) { }
-
-protected:
- friend class Reader<A>;
- typedef typename A::P P;
-
- ImportAtom(ObjectFile::Reader& owner, uint32_t ordinal, std::vector<const char*>& imports)
- : fOwner(owner), fOrdinal(ordinal) { makeReferences(imports); }
- virtual ~ImportAtom() {}
- void makeReferences(std::vector<const char*>& imports) {
- for (std::vector<const char*>::iterator it=imports.begin(); it != imports.end(); ++it) {
- fReferences.push_back(new ImportReference(*it));
- }
- }
-
-
- ObjectFile::Reader& fOwner;
- uint32_t fOrdinal;
- std::vector<ObjectFile::Reference*> fReferences;
-
- static Segment fgImportSegment;
-};
-
-template <typename A>
-Segment ImportAtom<A>::fgImportSegment("__LINKEDIT");
-
-
-
-
-//
-// The reader for a dylib extracts all exported symbols names from the memory-mapped
-// dylib, builds a hash table, then unmaps the file. This is an important memory
-// savings for large dylibs.
-//
-template <typename A>
-class Reader : public ObjectFile::Reader
-{
-public:
- static bool validFile(const uint8_t* fileContent, bool executableOrDylib);
- Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path,
- const DynamicLibraryOptions& dylibOptions, const ObjectFile::ReaderOptions& options,
- uint32_t ordinalBase);
- virtual ~Reader() {}
-
- virtual const char* getPath() { return fPath; }
- virtual time_t getModificationTime() { return 0; }
- virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; }
- virtual std::vector<class ObjectFile::Atom*>& getAtoms();
- virtual std::vector<class ObjectFile::Atom*>* getJustInTimeAtomsFor(const char* name);
- virtual std::vector<Stab>* getStabs() { return NULL; }
- virtual ObjectFile::Reader::ObjcConstraint getObjCConstraint() { return fObjcContraint; }
- virtual const char* getInstallPath() { return fDylibInstallPath; }
- virtual uint32_t getTimestamp() { return fDylibTimeStamp; }
- virtual uint32_t getCurrentVersion() { return fDylibtCurrentVersion; }
- virtual uint32_t getCompatibilityVersion() { return fDylibCompatibilityVersion; }
- virtual void processIndirectLibraries(DylibHander* handler);
- virtual void setExplicitlyLinked() { fExplicitlyLinked = true; }
- virtual bool explicitlyLinked() { return fExplicitlyLinked; }
- virtual bool implicitlyLinked() { return fImplicitlyLinked; }
- virtual bool providedExportAtom() { return fProvidedAtom; }
- virtual const char* parentUmbrella() { return fParentUmbrella; }
- virtual std::vector<const char*>* getAllowableClients();
- virtual bool hasWeakExternals() { return fHasWeakExports; }
- virtual bool isLazyLoadedDylib() { return fLazyLoaded; }
-
- virtual void setImplicitlyLinked() { fImplicitlyLinked = true; }
-
-protected:
-
- struct ReExportChain { ReExportChain* prev; Reader<A>* reader; };
-
- void assertNoReExportCycles(ReExportChain*);
-
-private:
- typedef typename A::P P;
- typedef typename A::P::E E;
-
- class CStringEquals
- {
- public:
- bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
- };
- struct AtomAndWeak { ObjectFile::Atom* atom; bool weak; uint32_t ordinal; };
- typedef __gnu_cxx::hash_map<const char*, AtomAndWeak, __gnu_cxx::hash<const char*>, CStringEquals> NameToAtomMap;
- typedef __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> NameSet;
- typedef typename NameToAtomMap::iterator NameToAtomMapIterator;
-
- struct PathAndFlag { const char* path; bool reExport; };
-
- bool isPublicLocation(const char* path);
- void addSymbol(const char* name, bool weak, uint32_t ordinal);
-
- const char* fPath;
- const char* fParentUmbrella;
- std::vector<const char*> fAllowableClients;
- const char* fDylibInstallPath;
- uint32_t fDylibTimeStamp;
- uint32_t fDylibtCurrentVersion;
- uint32_t fDylibCompatibilityVersion;
- uint32_t fReExportedOrdinal;
- std::vector<PathAndFlag> fDependentLibraryPaths;
- NameToAtomMap fAtoms;
- NameSet fIgnoreExports;
- bool fNoRexports;
- bool fHasWeakExports;
- const bool fLinkingFlat;
- const bool fLinkingMainExecutable;
- bool fExplictReExportFound;
- bool fExplicitlyLinked;
- bool fImplicitlyLinked;
- bool fProvidedAtom;
- bool fImplicitlyLinkPublicDylibs;
- bool fLazyLoaded;
- ObjectFile::Reader::ObjcConstraint fObjcContraint;
- std::vector<ObjectFile::Reader*> fReExportedChildren;
- const ObjectFile::ReaderOptions::VersionMin fDeploymentVersionMin;
- std::vector<class ObjectFile::Atom*> fFlatImports;
-
- static bool fgLogHashtable;
- static std::vector<class ObjectFile::Atom*> fgEmptyAtomList;
-};
-
-template <typename A>
-std::vector<class ObjectFile::Atom*> Reader<A>::fgEmptyAtomList;
-template <typename A>
-bool Reader<A>::fgLogHashtable = false;
-
-
-template <typename A>
-Reader<A>::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path,
- const DynamicLibraryOptions& dylibOptions,
- const ObjectFile::ReaderOptions& options, uint32_t ordinalBase)
- : fParentUmbrella(NULL), fDylibInstallPath(NULL), fDylibTimeStamp(0), fDylibtCurrentVersion(0),
- fDylibCompatibilityVersion(0), fLinkingFlat(options.fFlatNamespace),
- fLinkingMainExecutable(options.fLinkingMainExecutable), fExplictReExportFound(false),
- fExplicitlyLinked(false), fImplicitlyLinked(false), fProvidedAtom(false),
- fImplicitlyLinkPublicDylibs(options.fImplicitlyLinkPublicDylibs), fLazyLoaded(dylibOptions.fLazyLoad),
- fObjcContraint(ObjectFile::Reader::kObjcNone),
- fDeploymentVersionMin(options.fVersionMin)
-{
- // sanity check
- if ( ! validFile(fileContent, dylibOptions.fBundleLoader) )
- throw "not a valid mach-o object file";
-
- fPath = strdup(path);
-
- const macho_header<P>* header = (const macho_header<P>*)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>* const cmdsEnd = (macho_load_command<P>*)((char*)header + sizeof(macho_header<P>) + header->sizeofcmds());
-
- // write out path for -whatsloaded option
- if ( options.fLogAllFiles )
- printf("%s\n", path);
-
- if ( options.fRootSafe && ((header->flags() & MH_ROOT_SAFE) == 0) )
- warning("using -root_safe but linking against %s which is not root safe", path);
-
- if ( options.fSetuidSafe && ((header->flags() & MH_SETUID_SAFE) == 0) )
- warning("using -setuid_safe but linking against %s which is not setuid safe", path);
-
- // a "blank" stub has zero load commands
- if ( (header->filetype() == MH_DYLIB_STUB) && (cmd_count == 0) ) {
- // no further processing needed
- munmap((caddr_t)fileContent, fileLength);
- return;
- }
-
-
- // optimize the case where we know there is no reason to look at indirect dylibs
- fNoRexports = (header->flags() & MH_NO_REEXPORTED_DYLIBS);
- fHasWeakExports = (header->flags() & MH_WEAK_DEFINES);
- bool trackDependentLibraries = !fNoRexports || options.fFlatNamespace;
-
- // pass 1 builds list of all dependent libraries
- const macho_load_command<P>* cmd = cmds;
- if ( trackDependentLibraries ) {
- for (uint32_t i = 0; i < cmd_count; ++i) {
- switch (cmd->cmd()) {
- case LC_REEXPORT_DYLIB:
- fExplictReExportFound = true;
- // fall into next case
- case LC_LOAD_DYLIB:
- case LC_LOAD_WEAK_DYLIB:
- PathAndFlag entry;
- entry.path = strdup(((struct macho_dylib_command<P>*)cmd)->name());
- entry.reExport = (cmd->cmd() == LC_REEXPORT_DYLIB);
- fDependentLibraryPaths.push_back(entry);
- break;
- }
- cmd = (const macho_load_command<P>*)(((char*)cmd)+cmd->cmdsize());
- if ( cmd > cmdsEnd )
- throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, path);
- }
- }
-
- // pass 2 determines re-export info
- const macho_dysymtab_command<P>* dynamicInfo = NULL;
- const macho_nlist<P>* symbolTable = NULL;
- const char* strings = NULL;
- cmd = cmds;
- for (uint32_t i = 0; i < cmd_count; ++i) {
- switch (cmd->cmd()) {
- case LC_SYMTAB:
- {
- const macho_symtab_command<P>* symtab = (macho_symtab_command<P>*)cmd;
- symbolTable = (const macho_nlist<P>*)((char*)header + symtab->symoff());
- strings = (char*)header + symtab->stroff();
- }
- break;
- case LC_DYSYMTAB:
- dynamicInfo = (macho_dysymtab_command<P>*)cmd;
- break;
- case LC_ID_DYLIB:
- {
- macho_dylib_command<P>* dylibID = (macho_dylib_command<P>*)cmd;
- fDylibInstallPath = strdup(dylibID->name());
- fDylibTimeStamp = dylibID->timestamp();
- fDylibtCurrentVersion = dylibID->current_version();
- fDylibCompatibilityVersion = dylibID->compatibility_version();
- }
- break;
- case LC_SUB_UMBRELLA:
- if ( trackDependentLibraries ) {
- const char* frameworkLeafName = ((macho_sub_umbrella_command<P>*)cmd)->sub_umbrella();
- for (typename std::vector<PathAndFlag>::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) {
- const char* dylibName = it->path;
- const char* lastSlash = strrchr(dylibName, '/');
- if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], frameworkLeafName) == 0) )
- it->reExport = true;
- }
- }
- break;
- case LC_SUB_LIBRARY:
- if ( trackDependentLibraries) {
- const char* dylibBaseName = ((macho_sub_library_command<P>*)cmd)->sub_library();
- for (typename std::vector<PathAndFlag>::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) {
- const char* dylibName = it->path;
- const char* lastSlash = strrchr(dylibName, '/');
- const char* leafStart = &lastSlash[1];
- if ( lastSlash == NULL )
- leafStart = dylibName;
- const char* firstDot = strchr(leafStart, '.');
- int len = strlen(leafStart);
- if ( firstDot != NULL )
- len = firstDot - leafStart;
- if ( strncmp(leafStart, dylibBaseName, len) == 0 )
- it->reExport = true;
- }
- }
- break;
- case LC_SUB_FRAMEWORK:
- fParentUmbrella = strdup(((macho_sub_framework_command<P>*)cmd)->umbrella());
- break;
- case macho_segment_command<P>::CMD:
- // check for Objective-C info
- if ( strcmp(((macho_segment_command<P>*)cmd)->segname(), "__OBJC") == 0 ) {
- const macho_segment_command<P>* segment = (macho_segment_command<P>*)cmd;
- const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segment + sizeof(macho_segment_command<P>));
- const macho_section<P>* const sectionsEnd = §ionsStart[segment->nsects()];
- for (const macho_section<P>* sect=sectionsStart; sect < sectionsEnd; ++sect) {
- if ( strcmp(sect->sectname(), "__image_info") == 0 ) {
- // struct objc_image_info {
- // uint32_t version; // initially 0
- // uint32_t flags;
- // };
- // #define OBJC_IMAGE_SUPPORTS_GC 2
- // #define OBJC_IMAGE_GC_ONLY 4
- //
- const uint32_t* contents = (uint32_t*)(&fileContent[sect->offset()]);
- if ( (sect->size() >= 8) && (contents[0] == 0) ) {
- uint32_t flags = E::get32(contents[1]);
- if ( (flags & 4) == 4 )
- fObjcContraint = ObjectFile::Reader::kObjcGC;
- else if ( (flags & 2) == 2 )
- fObjcContraint = ObjectFile::Reader::kObjcRetainReleaseOrGC;
- else
- fObjcContraint = ObjectFile::Reader::kObjcRetainRelease;
- }
- else if ( sect->size() > 0 ) {
- warning("can't parse __OBJC/__image_info section in %s", fPath);
- }
- }
- }
- }
- }
-
- cmd = (const macho_load_command<P>*)(((char*)cmd)+cmd->cmdsize());
- if ( cmd > cmdsEnd )
- throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, path);
- }
-
- // Process the rest of the commands here.
- cmd = cmds;
- for (uint32_t i = 0; i < cmd_count; ++i) {
- switch (cmd->cmd()) {
- case LC_SUB_CLIENT:
- const char *temp = strdup(((macho_sub_client_command<P>*)cmd)->client());
- fAllowableClients.push_back(temp);
- break;
- }
- cmd = (const macho_load_command<P>*)(((char*)cmd)+cmd->cmdsize());
- }
-
- // validate minimal load commands
- if ( (fDylibInstallPath == NULL) && ((header->filetype() == MH_DYLIB) || (header->filetype() == MH_DYLIB_STUB)) )
- throwf("dylib %s missing LC_ID_DYLIB load command", path);
- if ( symbolTable == NULL )
- throw "binary missing LC_SYMTAB load command";
- if ( dynamicInfo == NULL )
- throw "binary missing LC_DYSYMTAB load command";
-
- // if linking flat and this is a flat dylib, create one atom that references all imported symbols
- if ( fLinkingFlat && fLinkingMainExecutable && ((header->flags() & MH_TWOLEVEL) == 0) ) {
- std::vector<const char*> importNames;
- importNames.reserve(dynamicInfo->nundefsym());
- const macho_nlist<P>* start = &symbolTable[dynamicInfo->iundefsym()];
- const macho_nlist<P>* end = &start[dynamicInfo->nundefsym()];
- for (const macho_nlist<P>* sym=start; sym < end; ++sym) {
- importNames.push_back(&strings[sym->n_strx()]);
- }
- fFlatImports.push_back(new ImportAtom<A>(*this, ordinalBase++, importNames));
- }
-
- // build hash table
- if ( dynamicInfo->tocoff() == 0 ) {
- if ( fgLogHashtable ) fprintf(stderr, "ld: building hashtable of %u toc entries for %s\n", dynamicInfo->nextdefsym(), path);
- const macho_nlist<P>* start = &symbolTable[dynamicInfo->iextdefsym()];
- const macho_nlist<P>* end = &start[dynamicInfo->nextdefsym()];
- fAtoms.resize(dynamicInfo->nextdefsym()); // set initial bucket count
- uint32_t index = ordinalBase;
- for (const macho_nlist<P>* sym=start; sym < end; ++sym, ++index) {
- this->addSymbol(&strings[sym->n_strx()], (sym->n_desc() & N_WEAK_DEF) != 0, index);
- }
- fReExportedOrdinal = index;
- }
- else {
- int32_t count = dynamicInfo->ntoc();
- fAtoms.resize(count); // set initial bucket count
- if ( fgLogHashtable ) fprintf(stderr, "ld: building hashtable of %u entries for %s\n", count, path);
- const struct dylib_table_of_contents* toc = (dylib_table_of_contents*)((char*)header + dynamicInfo->tocoff());
- for (int32_t i = 0; i < count; ++i) {
- const uint32_t index = E::get32(toc[i].symbol_index);
- const macho_nlist<P>* sym = &symbolTable[index];
- this->addSymbol(&strings[sym->n_strx()], (sym->n_desc() & N_WEAK_DEF) != 0, ordinalBase+i);
- }
- fReExportedOrdinal = ordinalBase + count;
- }
-
-
- // unmap file
- munmap((caddr_t)fileContent, fileLength);
-}
-
-
-
-template <typename A>
-void Reader<A>::addSymbol(const char* name, bool weak, uint32_t ordinal)
-{
- // symbols that start with $ld$ are meta-data to the static linker
- // <rdar://problem/5182537> need way for ld and dyld to see different exported symbols in a dylib
- if ( strncmp(name, "$ld$", 4) == 0 ) {
- // $ld$ <action> $ <condition> $ <symbol-name>
- const char* symAction = &name[4];
- const char* symCond = strchr(symAction, '$');
- if ( symCond != NULL ) {
- ObjectFile::ReaderOptions::VersionMin symVersionCondition = ObjectFile::ReaderOptions::kMinUnset;
- if ( (strncmp(symCond, "$os10.", 6) == 0) && isdigit(symCond[6]) && (symCond[7] == '$') ) {
- switch ( symCond[6] - '0' ) {
- case 0:
- case 1:
- symVersionCondition = ObjectFile::ReaderOptions::k10_1;
- break;
- case 2:
- symVersionCondition = ObjectFile::ReaderOptions::k10_2;
- break;
- case 3:
- symVersionCondition = ObjectFile::ReaderOptions::k10_3;
- break;
- case 4:
- symVersionCondition = ObjectFile::ReaderOptions::k10_4;
- break;
- case 5:
- symVersionCondition = ObjectFile::ReaderOptions::k10_5;
- break;
- case 6:
- symVersionCondition = ObjectFile::ReaderOptions::k10_6;
- break;
- }
- const char* symName = strchr(&symCond[1], '$');
- if ( symName != NULL ) {
- ++symName;
- if ( fDeploymentVersionMin == symVersionCondition ) {
- if ( strncmp(symAction, "hide$", 5) == 0 ) {
- if ( fgLogHashtable ) fprintf(stderr, " adding %s to ignore set for %s\n", symName, this->getPath());
- fIgnoreExports.insert(strdup(symName));
- return;
- }
- else if ( strncmp(symAction, "add$", 4) == 0 ) {
- this->addSymbol(symName, weak, ordinal);
- return;
- }
- else {
- warning("bad symbol action: %s in dylib %s", name, this->getPath());
- }
- }
- }
- else {
- warning("bad symbol name: %s in dylib %s", name, this->getPath());
- }
- }
- else {
- warning("bad symbol version: %s in dylib %s", name, this->getPath());
- }
- }
- else {
- warning("bad symbol condition: %s in dylib %s", name, this->getPath());
- }
- }
-
- // add symbol as possible export if we are not supposed to ignore it
- if ( fIgnoreExports.count(name) == 0 ) {
- AtomAndWeak bucket;
- bucket.atom = NULL;
- bucket.weak = weak;
- bucket.ordinal = ordinal;
- if ( fgLogHashtable ) fprintf(stderr, " adding %s to hash table for %s\n", name, this->getPath());
- fAtoms[strdup(name)] = bucket;
- }
-}
-
-
-template <typename A>
-std::vector<class ObjectFile::Atom*>& Reader<A>::getAtoms()
-{
- return fFlatImports;
-}
-
-
-template <typename A>
-std::vector<class ObjectFile::Atom*>* Reader<A>::getJustInTimeAtomsFor(const char* name)
-{
- std::vector<class ObjectFile::Atom*>* atoms = NULL;
-
- NameToAtomMapIterator pos = fAtoms.find(name);
- if ( pos != fAtoms.end() ) {
- if ( pos->second.atom == NULL ) {
- // instantiate atom and update hash table
- pos->second.atom = new ExportAtom<A>(*this, name, pos->second.weak, pos->second.ordinal);
- fProvidedAtom = true;
- if ( fgLogHashtable ) fprintf(stderr, "getJustInTimeAtomsFor: %s found in %s\n", name, this->getPath());
- }
- // return a vector of one atom
- atoms = new std::vector<class ObjectFile::Atom*>;
- atoms->push_back(pos->second.atom);
- }
- else {
- if ( fgLogHashtable ) fprintf(stderr, "getJustInTimeAtomsFor: %s NOT found in %s\n", name, this->getPath());
- // if not supposed to ignore this export, see if I have it
- if ( fIgnoreExports.count(name) == 0 ) {
- // look in children that I re-export
- for (std::vector<ObjectFile::Reader*>::iterator it = fReExportedChildren.begin(); it != fReExportedChildren.end(); it++) {
- //fprintf(stderr, "getJustInTimeAtomsFor: %s NOT found in %s, looking in child %s\n", name, this->getPath(), (*it)->getInstallPath());
- std::vector<class ObjectFile::Atom*>* childAtoms = (*it)->getJustInTimeAtomsFor(name);
- if ( childAtoms != NULL ) {
- // make a new atom that says this reader is the owner
- bool isWeakDef = (childAtoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition);
- // return a vector of one atom
- ExportAtom<A>* newAtom = new ExportAtom<A>(*this, name, isWeakDef, fReExportedOrdinal++);
- fProvidedAtom = true;
- atoms = new std::vector<class ObjectFile::Atom*>;
- atoms->push_back(newAtom);
- delete childAtoms;
- return atoms;
- }
- }
- }
- }
- return atoms;
-}
-
-
-
-template <typename A>
-bool Reader<A>::isPublicLocation(const char* path)
-{
- // -no_implicit_dylibs disables this optimization
- if ( ! fImplicitlyLinkPublicDylibs )
- return false;
-
- // /usr/lib is a public location
- if ( (strncmp(path, "/usr/lib/", 9) == 0) && (strchr(&path[9], '/') == NULL) )
- return true;
-
- // /System/Library/Frameworks/ is a public location
- if ( strncmp(path, "/System/Library/Frameworks/", 27) == 0 ) {
- const char* frameworkDot = strchr(&path[27], '.');
- // but only top level framework
- // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true
- // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false
- // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar ==> false
- // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo ==> false
- if ( frameworkDot != NULL ) {
- int frameworkNameLen = frameworkDot - &path[27];
- if ( strncmp(&path[strlen(path)-frameworkNameLen-1], &path[26], frameworkNameLen+1) == 0 )
- return true;
- }
- }
-
- return false;
-}
-
-template <typename A>
-void Reader<A>::processIndirectLibraries(DylibHander* handler)
-{
- if ( fLinkingFlat ) {
- for (typename std::vector<PathAndFlag>::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) {
- handler->findDylib(it->path, this->getPath());
- }
- }
- else if ( fNoRexports ) {
- // MH_NO_REEXPORTED_DYLIBS bit set, then nothing to do
- }
- else {
- // two-level, might have re-exports
- for (typename std::vector<PathAndFlag>::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) {
- if ( it->reExport ) {
- //fprintf(stderr, "processIndirectLibraries() parent=%s, child=%s\n", this->getInstallPath(), it->path);
- // a LC_REEXPORT_DYLIB, LC_SUB_UMBRELLA or LC_SUB_LIBRARY says we re-export this child
- ObjectFile::Reader* child = handler->findDylib(it->path, this->getPath());
- if ( isPublicLocation(child->getInstallPath()) ) {
- // promote this child to be automatically added as a direct dependent if this already is
- if ( this->explicitlyLinked() || this->implicitlyLinked() ) {
- //fprintf(stderr, "processIndirectLibraries() implicitly linking %s\n", child->getInstallPath());
- ((Reader<A>*)child)->setImplicitlyLinked();
- }
- else
- fReExportedChildren.push_back(child);
- }
- else {
- // add all child's symbols to me
- fReExportedChildren.push_back(child);
- //fprintf(stderr, "processIndirectLibraries() parent=%s will re-export child=%s\n", this->getInstallPath(), it->path);
- }
- }
- else if ( !fExplictReExportFound ) {
- // see if child contains LC_SUB_FRAMEWORK with my name
- ObjectFile::Reader* child = handler->findDylib(it->path, this->getPath());
- const char* parentUmbrellaName = ((Reader<A>*)child)->parentUmbrella();
- if ( parentUmbrellaName != NULL ) {
- const char* parentName = this->getPath();
- const char* lastSlash = strrchr(parentName, '/');
- if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], parentUmbrellaName) == 0) ) {
- // add all child's symbols to me
- fReExportedChildren.push_back(child);
- //fprintf(stderr, "processIndirectLibraries() umbrella=%s will re-export child=%s\n", this->getInstallPath(), it->path);
- }
- }
- }
- }
- }
-
- // check for re-export cycles
- ReExportChain chain;
- chain.prev = NULL;
- chain.reader = this;
- this->assertNoReExportCycles(&chain);
-}
-
-template <typename A>
-void Reader<A>::assertNoReExportCycles(ReExportChain* prev)
-{
- // recursively check my re-exported dylibs
- ReExportChain chain;
- chain.prev = prev;
- chain.reader = this;
- for (std::vector<ObjectFile::Reader*>::iterator it = fReExportedChildren.begin(); it != fReExportedChildren.end(); it++) {
- ObjectFile::Reader* child = *it;
- // check child is not already in chain
- for (ReExportChain* p = prev; p != NULL; p = p->prev) {
- if ( p->reader == child ) {
- throwf("cycle in dylib re-exports with %s", child->getPath());
- }
- }
- ((Reader<A>*)(*it))->assertNoReExportCycles(&chain);
- }
-}
-
-
-template <typename A>
-std::vector<const char*>* Reader<A>::getAllowableClients()
-{
- std::vector<const char*>* result = new std::vector<const char*>;
- for (typename std::vector<const char*>::iterator it = fAllowableClients.begin();
- it != fAllowableClients.end();
- it++) {
- result->push_back(*it);
- }
- return (fAllowableClients.size() != 0 ? result : NULL);
-}
-
-template <>
-bool Reader<ppc>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle)
-{
- 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;
- switch ( header->filetype() ) {
- case MH_DYLIB:
- case MH_DYLIB_STUB:
- return true;
- case MH_BUNDLE:
- if ( executableOrDyliborBundle )
- return true;
- else
- throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)";
- case MH_EXECUTE:
- if ( executableOrDyliborBundle )
- return true;
- else
- throw "can't link with a main executable";
- default:
- return false;
- }
-}
-
-template <>
-bool Reader<ppc64>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle)
-{
- 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;
- switch ( header->filetype() ) {
- case MH_DYLIB:
- case MH_DYLIB_STUB:
- return true;
- case MH_BUNDLE:
- if ( executableOrDyliborBundle )
- return true;
- else
- throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)";
- case MH_EXECUTE:
- if ( executableOrDyliborBundle )
- return true;
- else
- throw "can't link with a main executable";
- default:
- return false;
- }
-}
-
-template <>
-bool Reader<x86>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle)
-{
- 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;
- switch ( header->filetype() ) {
- case MH_DYLIB:
- case MH_DYLIB_STUB:
- return true;
- case MH_BUNDLE:
- if ( executableOrDyliborBundle )
- return true;
- else
- throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)";
- case MH_EXECUTE:
- if ( executableOrDyliborBundle )
- return true;
- else
- throw "can't link with a main executable";
- default:
- return false;
- }
-}
-
-template <>
-bool Reader<x86_64>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle)
-{
- 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_BUNDLE:
- if ( executableOrDyliborBundle )
- return true;
- else
- throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)";
- case MH_EXECUTE:
- if ( executableOrDyliborBundle )
- return true;
- else
- throw "can't link with a main executable";
- default:
- return false;
- }
-}
-
-template <>
-bool Reader<arm>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle)
-{
- const macho_header<P>* header = (const macho_header<P>*)fileContent;
- if ( header->magic() != MH_MAGIC )
- return false;
- if ( header->cputype() != CPU_TYPE_ARM )
- return false;
- switch ( header->filetype() ) {
- case MH_DYLIB:
- case MH_DYLIB_STUB:
- return true;
- case MH_BUNDLE:
- if ( executableOrDyliborBundle )
- return true;
- else
- throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)";
- case MH_EXECUTE:
- if ( executableOrDyliborBundle )
- return true;
- else
- throw "can't link with a main executable";
- default:
- return false;
- }
-}
-
-}; // namespace dylib
-}; // namespace mach_o
-
-
-#endif // __OBJECT_FILE_DYLIB_MACH_O__
+++ /dev/null
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
- *
- * Copyright (c) 2005-2008 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#ifndef __OBJECT_FILE_MACH_O__
-#define __OBJECT_FILE_MACH_O__
-
-#include <stdint.h>
-#include <math.h>
-#include <unistd.h>
-#include <sys/param.h>
-
-#include <vector>
-#include <set>
-#include <algorithm>
-
-#include "MachOFileAbstraction.hpp"
-#include "Architectures.hpp"
-#include "ObjectFile.h"
-#include "dwarf2.h"
-#include "debugline.h"
-
-
-//
-//
-// To implement architecture xxx, you must write template specializations for the following six methods:
-// Reader<xxx>::validFile()
-// Reader<xxx>::validSectionType()
-// Reader<xxx>::addRelocReference()
-// Reference<xxx>::getDescription()
-//
-//
-
-
-
-extern __attribute__((noreturn)) void throwf(const char* format, ...);
-extern void warning(const char* format, ...);
-
-namespace mach_o {
-namespace relocatable {
-
-
-
-class ReferenceSorter
-{
-public:
- bool operator()(const ObjectFile::Reference* left, const ObjectFile::Reference* right)
- {
- return ( left->getFixUpOffset() < right->getFixUpOffset() );
- }
-};
-
-
-// forward reference
-template <typename A> class Reader;
-
-struct AtomAndOffset
-{
- AtomAndOffset(ObjectFile::Atom* a=NULL) : atom(a), offset(0) {}
- AtomAndOffset(ObjectFile::Atom* a, uint32_t off) : atom(a), offset(off) {}
- ObjectFile::Atom* atom;
- uint32_t offset;
-};
-
-
-template <typename A>
-class Reference : public ObjectFile::Reference
-{
-public:
- typedef typename A::P P;
- typedef typename A::P::uint_t pint_t;
- typedef typename A::ReferenceKinds Kinds;
-
- Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& toTarget);
- Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& fromTarget, const AtomAndOffset& toTarget);
- Reference(Kinds kind, const AtomAndOffset& at, const char* toName, uint32_t toOffset);
-
- virtual ~Reference() {}
-
-
- virtual ObjectFile::Reference::TargetBinding getTargetBinding() const;
- virtual ObjectFile::Reference::TargetBinding getFromTargetBinding() const;
- virtual uint8_t getKind() const { return (uint8_t)fKind; }
- virtual uint64_t getFixUpOffset() const { return fFixUpOffsetInSrc; }
- virtual const char* getTargetName() const { return (fToTargetName != NULL) ? fToTargetName : fToTarget.atom->getName(); }
- virtual ObjectFile::Atom& getTarget() const { return *fToTarget.atom; }
- virtual uint64_t getTargetOffset() const { return (int64_t)((int32_t)fToTarget.offset); }
- virtual ObjectFile::Atom& getFromTarget() const { return *fFromTarget.atom; }
- virtual const char* getFromTargetName() const { return (fFromTargetName != NULL) ? fFromTargetName : fFromTarget.atom->getName(); }
- virtual void setTarget(ObjectFile::Atom& target, uint64_t offset) { fToTarget.atom = ⌖ fToTarget.offset = offset; }
- virtual void setToTargetOffset(uint64_t offset) { fToTarget.offset = offset; }
- virtual void setFromTarget(ObjectFile::Atom& target) { fFromTarget.atom = ⌖ }
- virtual void setFromTargetName(const char* name) { fFromTargetName = name; }
- virtual void setFromTargetOffset(uint64_t offset) { fFromTarget.offset = offset; }
- virtual const char* getDescription() const;
- virtual uint64_t getFromTargetOffset() const { return fFromTarget.offset; }
-
- static bool fgForFinalLinkedImage;
-
-private:
- pint_t fFixUpOffsetInSrc;
- AtomAndOffset fToTarget;
- AtomAndOffset fFromTarget;
- const char* fToTargetName;
- const char* fFromTargetName;
- Kinds fKind;
-
-};
-
-template <typename A> bool Reference<A>::fgForFinalLinkedImage = true;
-
-template <typename A>
-Reference<A>::Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& toTarget)
- : fFixUpOffsetInSrc(at.offset), fToTarget(toTarget), fToTargetName(NULL), fFromTargetName(NULL),
- fKind(kind)
-{
- // make reference a by-name unless:
- // - the reference type is only used with direct references
- // - the target is translation unit scoped
- // - the target kind is not regular (is weak or tentative)
- if ( (kind != A::kNoFixUp) && (kind != A::kFollowOn) && (kind != A::kGroupSubordinate)
- && (toTarget.atom->getScope() != ObjectFile::Atom::scopeTranslationUnit)
- && (toTarget.atom->getDefinitionKind() != ObjectFile::Atom::kRegularDefinition)
- && (toTarget.atom != at.atom) ) {
- fToTargetName = toTarget.atom->getName();
- //fprintf(stderr, "Reference(): changing to by-name %p %s, target scope=%d, target section=%s\n", toTarget.atom, fToTargetName, toTarget.atom->getScope(), toTarget.atom->getSectionName());
- fToTarget.atom = NULL;
- }
- ((class BaseAtom*)at.atom)->addReference(this);
- //fprintf(stderr, "Reference(): %p fToTarget<%s, %08X>\n", this, (fToTarget.atom != NULL) ? fToTarget.atom->getDisplayName() : fToTargetName , fToTarget.offset);
-}
-
-template <typename A>
-Reference<A>::Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& fromTarget, const AtomAndOffset& toTarget)
- : fFixUpOffsetInSrc(at.offset), fToTarget(toTarget), fFromTarget(fromTarget),
- fToTargetName(NULL), fFromTargetName(NULL), fKind(kind)
-{
- // make reference a by-name where needed
- if ( (kind != A::kNoFixUp) && (kind != A::kFollowOn) && (kind != A::kGroupSubordinate)
- && (toTarget.atom->getScope() != ObjectFile::Atom::scopeTranslationUnit)
- && (toTarget.atom->getDefinitionKind() != ObjectFile::Atom::kRegularDefinition)
- && (toTarget.atom != at.atom) ) {
- fToTargetName = toTarget.atom->getName();
- fToTarget.atom = NULL;
- }
- ((class BaseAtom*)at.atom)->addReference(this);
- //fprintf(stderr, "Reference(): %p kind=%d, fToTarget<%s, %08X>, fromTarget<%s, %08X>\n", this, kind,
- // this->getTargetName(), fToTarget.offset, this->getFromTargetName(), fromTarget.offset);
-}
-
-template <typename A>
-Reference<A>::Reference(Kinds kind, const AtomAndOffset& at, const char* toName, uint32_t toOffset)
- : fFixUpOffsetInSrc(at.offset),
- fToTargetName(toName), fFromTargetName(NULL), fKind(kind)
-{
- fToTarget.offset = toOffset;
- ((class BaseAtom*)at.atom)->addReference(this);
-}
-
-template <typename A>
-ObjectFile::Reference::TargetBinding Reference<A>::getTargetBinding() const
-{
- if ( fgForFinalLinkedImage ) {
- if ( (fKind == A::kDtraceProbe) || (fKind == A::kDtraceProbeSite) || (fKind == A::kDtraceIsEnabledSite) || (fKind == A::kDtraceTypeReference) )
- return ObjectFile::Reference::kDontBind;
- }
- if ( fToTarget.atom == NULL )
- return ObjectFile::Reference::kUnboundByName;
- if ( fToTargetName == NULL )
- return ObjectFile::Reference::kBoundDirectly;
- else
- return ObjectFile::Reference::kBoundByName;
-}
-
-template <typename A>
-ObjectFile::Reference::TargetBinding Reference<A>::getFromTargetBinding() const
-{
- if ( fFromTarget.atom == NULL ) {
- if ( fFromTargetName == NULL )
- return ObjectFile::Reference::kDontBind;
- else
- return ObjectFile::Reference::kUnboundByName;
- }
- else {
- if ( fFromTargetName == NULL )
- return ObjectFile::Reference::kBoundDirectly;
- else
- return ObjectFile::Reference::kBoundByName;
- }
-}
-
-
-
-template <typename A>
-class Segment : public ObjectFile::Segment
-{
-public:
- Segment(const macho_section<typename A::P>* sect);
- virtual const char* getName() const { return fSection->segname(); }
- virtual bool isContentReadable() const { return true; }
- virtual bool isContentWritable() const { return fWritable; }
- virtual bool isContentExecutable() const { return fExecutable; }
-private:
- const macho_section<typename A::P>* fSection;
- bool fWritable;
- bool fExecutable;
-};
-
-template <typename A>
-Segment<A>::Segment(const macho_section<typename A::P>* sect)
- : fSection(sect), fWritable(true), fExecutable(false)
-{
- if ( strcmp(fSection->segname(), "__TEXT") == 0 ) {
- fWritable = false;
- fExecutable = true;
- }
- else if ( strcmp(fSection->segname(), "__IMPORT") == 0 ) {
- fWritable = true;
- fExecutable = true;
- }
-}
-
-
-class DataSegment : public ObjectFile::Segment
-{
-public:
- virtual const char* getName() const { return "__DATA"; }
- virtual bool isContentReadable() const { return true; }
- virtual bool isContentWritable() const { return true; }
- virtual bool isContentExecutable() const { return false; }
-
- static DataSegment fgSingleton;
-};
-
-DataSegment DataSegment::fgSingleton;
-
-class LinkEditSegment : public ObjectFile::Segment
-{
-public:
- virtual const char* getName() const { return "__LINKEDIT"; }
- virtual bool isContentReadable() const { return true; }
- virtual bool isContentWritable() const { return false; }
- virtual bool isContentExecutable() const { return false; }
-
- static LinkEditSegment fgSingleton;
-};
-
-LinkEditSegment LinkEditSegment::fgSingleton;
-
-class BaseAtom : public ObjectFile::Atom
-{
-public:
- BaseAtom() : fStabsStartIndex(0), fStabsCount(0) {}
-
- virtual void setSize(uint64_t size) = 0;
- virtual void addReference(ObjectFile::Reference* ref) = 0;
- virtual void sortReferences() = 0;
- virtual void addLineInfo(const ObjectFile::LineInfo& info) = 0;
- virtual uint64_t getObjectAddress() const = 0;
- virtual uint32_t getOrdinal() const { return fOrdinal; }
- virtual void setOrdinal(uint32_t value) { fOrdinal = value; }
- virtual const void* getSectionRecord() const = 0;
- virtual bool isAlias() const { return false; }
-
- uint32_t fStabsStartIndex;
- uint32_t fStabsCount;
- uint32_t fOrdinal;
-};
-
-class BaseAtomSorter
-{
-public:
- bool operator()(const class BaseAtom* left, const class BaseAtom* right) {
- if ( left == right )
- return false;
- uint64_t leftAddr = left->getObjectAddress();
- uint64_t rightAddr = right->getObjectAddress();
- if ( leftAddr < rightAddr ) {
- return true;
- }
- else if ( leftAddr > rightAddr ) {
- return false;
- }
- else {
- // if they have same address, one might be the end of a section and the other the start of the next section
- const void* leftSection = left->getSectionRecord();
- const void* rightSection = right->getSectionRecord();
- if ( leftSection != rightSection ) {
- return ( leftSection < rightSection );
- }
- // if they have same address and section, one might be an alias
- bool leftAlias = left->isAlias();
- bool rightAlias = right->isAlias();
- if ( leftAlias && rightAlias ) {
- // sort multiple aliases for same address first by scope
- ObjectFile::Atom::Scope leftScope = left->getScope();
- ObjectFile::Atom::Scope rightScope = right->getScope();
- if ( leftScope != rightScope ) {
- return ( leftScope < rightScope );
- }
- // sort multiple aliases for same address then by name
- return ( strcmp(left->getName(), right->getName()) < 0 );
- }
- else if ( leftAlias ) {
- return true;
- }
- else if ( rightAlias ) {
- return false;
- }
- else {
- // they must be tentative defintions
- switch ( left->getDefinitionKind() ) {
- case ObjectFile::Atom::kTentativeDefinition:
- // sort tentative definitions by name
- return ( strcmp(left->getName(), right->getName()) < 0 );
- case ObjectFile::Atom::kAbsoluteSymbol:
- // sort absolute symbols with same address by name
- return ( strcmp(left->getName(), right->getName()) < 0 );
- default:
- // hack for rdar://problem/5102873
- if ( !left->isZeroFill() || !right->isZeroFill() )
- warning("atom sorting error for %s and %s in %s", left->getDisplayName(), right->getDisplayName(), left->getFile()->getPath());
- break;
- }
- }
- }
- return false;
- }
-};
-
-
-//
-// A SymbolAtom represents a chunk of a mach-o object file that has a symbol table entry
-// pointing to it. A C function or global variable is represented by one of these atoms.
-//
-//
-template <typename A>
-class SymbolAtom : public BaseAtom
-{
-public:
- virtual ObjectFile::Reader* getFile() const { return &fOwner; }
- virtual bool getTranslationUnitSource(const char** dir, const char** name) const
- { return fOwner.getTranslationUnitSource(dir, name); }
- virtual const char* getName() const { return &fOwner.fStrings[fSymbol->n_strx()]; }
- virtual const char* getDisplayName() const { return getName(); }
- virtual ObjectFile::Atom::Scope getScope() const { return fScope; }
- virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ((fSymbol->n_desc() & N_WEAK_DEF) != 0)
- ? ObjectFile::Atom::kWeakDefinition : ObjectFile::Atom::kRegularDefinition; }
- virtual SymbolTableInclusion getSymbolTableInclusion() const { return fSymbolTableInclusion; }
- virtual bool dontDeadStrip() const;
- virtual bool isZeroFill() const { return ((fSection->flags() & SECTION_TYPE) == S_ZEROFILL); }
- virtual bool isThumb() const { return ((fSymbol->n_desc() & N_ARM_THUMB_DEF) != 0); }
- virtual uint64_t getSize() const { return fSize; }
- virtual std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(fReferences); }
- virtual bool mustRemainInSection() const { return true; }
- virtual const char* getSectionName() const;
- virtual Segment<A>& getSegment() const { return *fSegment; }
- virtual ObjectFile::Atom& getFollowOnAtom() const;
- virtual std::vector<ObjectFile::LineInfo>* getLineInfo() const { return (std::vector<ObjectFile::LineInfo>*)&fLineInfo; }
- virtual ObjectFile::Alignment getAlignment() const { return fAlignment; }
- virtual void copyRawContent(uint8_t buffer[]) const;
- virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; }
- virtual void setSize(uint64_t size);
- virtual void addReference(ObjectFile::Reference* ref) { fReferences.push_back((Reference<A>*)ref); }
- virtual void sortReferences() { std::sort(fReferences.begin(), fReferences.end(), ReferenceSorter()); }
- virtual void addLineInfo(const ObjectFile::LineInfo& info) { fLineInfo.push_back(info); }
- virtual uint64_t getObjectAddress() const { return fAddress; }
- virtual const void* getSectionRecord() const { return (const void*)fSection; }
-
-protected:
- typedef typename A::P P;
- typedef typename A::P::E E;
- typedef typename A::P::uint_t pint_t;
- typedef typename A::ReferenceKinds Kinds;
- typedef typename std::vector<Reference<A>*> ReferenceVector;
- typedef typename ReferenceVector::iterator ReferenceVectorIterator; // seems to help C++ parser
- typedef typename ReferenceVector::const_iterator ReferenceVectorConstIterator; // seems to help C++ parser
- friend class Reader<A>;
-
- SymbolAtom(Reader<A>&, const macho_nlist<P>*, const macho_section<P>*);
- virtual ~SymbolAtom() {}
-
- Reader<A>& fOwner;
- const macho_nlist<P>* fSymbol;
- pint_t fAddress;
- pint_t fSize;
- const macho_section<P>* fSection;
- Segment<A>* fSegment;
- ReferenceVector fReferences;
- std::vector<ObjectFile::LineInfo> fLineInfo;
- ObjectFile::Atom::Scope fScope;
- SymbolTableInclusion fSymbolTableInclusion;
- ObjectFile::Alignment fAlignment;
-};
-
-
-template <typename A>
-SymbolAtom<A>::SymbolAtom(Reader<A>& owner, const macho_nlist<P>* symbol, const macho_section<P>* section)
- : fOwner(owner), fSymbol(symbol), fAddress(0), fSize(0), fSection(section), fSegment(NULL), fAlignment(0)
-{
- uint8_t type = symbol->n_type();
- if ( (type & N_EXT) == 0 )
- fScope = ObjectFile::Atom::scopeTranslationUnit;
- else if ( (type & N_PEXT) != 0 )
- fScope = ObjectFile::Atom::scopeLinkageUnit;
- else
- fScope = ObjectFile::Atom::scopeGlobal;
- if ( (type & N_TYPE) == N_SECT ) {
- // real definition
- fSegment = new Segment<A>(fSection);
- fAddress = fSymbol->n_value();
- pint_t sectionStartAddr = section->addr();
- pint_t sectionEndAddr = section->addr()+section->size();
- if ( (fAddress < sectionStartAddr) || (fAddress > (sectionEndAddr)) ) {
- throwf("malformed .o file, symbol %s with address 0x%0llX is not with section %d (%s,%s) address range of 0x%0llX to 0x%0llX",
- this->getName(), (uint64_t)fAddress, fSymbol->n_sect(), section->segname(), section->sectname(),
- (uint64_t)sectionStartAddr, (uint64_t)(sectionEndAddr) );
- }
- }
- else {
- warning("unknown symbol type: %d", type);
- }
-
- //fprintf(stderr, "SymbolAtom(%p) %s fAddress=0x%X\n", this, this->getDisplayName(), (uint32_t)fAddress);
- // support for .o files built with old ld64
- if ( (fSymbol->n_desc() & N_WEAK_DEF) && (strcmp(fSection->sectname(),"__picsymbolstub1__TEXT") == 0) ) {
- const char* name = this->getName();
- const int nameLen = strlen(name);
- if ( (nameLen > 6) && strcmp(&name[nameLen-5], "$stub") == 0 ) {
- // switch symbol to point at name that does not have trailing $stub
- char correctName[nameLen];
- strncpy(correctName, name, nameLen-5);
- correctName[nameLen-5] = '\0';
- const macho_nlist<P>* symbolsStart = fOwner.fSymbols;
- const macho_nlist<P>* symbolsEnd = &symbolsStart[fOwner.fSymbolCount];
- for(const macho_nlist<P>* s = symbolsStart; s < symbolsEnd; ++s) {
- if ( strcmp(&fOwner.fStrings[s->n_strx()], correctName) == 0 ) {
- fSymbol = s;
- break;
- }
- }
- }
- }
- // support for labeled stubs
- switch ( section->flags() & SECTION_TYPE ) {
- case S_SYMBOL_STUBS:
- setSize(section->reserved2());
- break;
- case S_LAZY_SYMBOL_POINTERS:
- case S_NON_LAZY_SYMBOL_POINTERS:
- setSize(sizeof(pint_t));
- break;
- case S_4BYTE_LITERALS:
- setSize(4);
- break;
- case S_8BYTE_LITERALS:
- setSize(8);
- break;
- case S_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 alignment
- fAlignment = ObjectFile::Alignment(fSection->align(), fAddress % (1 << fSection->align()));
-
- // 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;
- // FDEs and CIEs are always packed together in a final linked image, so ignore section alignment
- fAlignment = ObjectFile::Alignment(0);
- }
- 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 if ( fOwner.fOptions.fForFinalLinkedImage && !fOwner.fOptions.fForStatic && (fOwner.fStrings[fSymbol->n_strx()] == 'l') ) {
- // labels beginning with a lowercase ell are automatically removed in final linked images <rdar://problem/4571042>
- // xnu code base uses a lot of asesembly labels that start with 'l', don't strip those (static executable)
- fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableNotIn;
- }
- else {
- fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn;
- }
-
- // work around malformed icc generated .o files <rdar://problem/5349847>
- // if section starts with a symbol and that symbol address does not match section alignment, then force it to
- if ( (section->addr() == fAddress) && (fAlignment.modulus != 0) )
- fAlignment.modulus = 0;
-}
-
-template <typename A>
-bool SymbolAtom<A>::dontDeadStrip() const
-{
- // the symbol can have a no-dead-strip bit
- if ( (fSymbol->n_desc() & (N_NO_DEAD_STRIP|REFERENCED_DYNAMICALLY)) != 0 )
- return true;
- // or the section can have a no-dead-strip bit
- return ( fSection->flags() & S_ATTR_NO_DEAD_STRIP );
-}
-
-
-template <typename A>
-const char* SymbolAtom<A>::getSectionName() const
-{
- if ( fOwner.fOptions.fForFinalLinkedImage && (strcmp(fSection->sectname(), "__textcoal_nt") == 0) )
- return "__text";
-
- if ( strlen(fSection->sectname()) > 15 ) {
- static char temp[18];
- strncpy(temp, fSection->sectname(), 16);
- temp[17] = '\0';
- return temp;
- }
- return fSection->sectname();
-}
-
-template <typename A>
-ObjectFile::Atom& SymbolAtom<A>::getFollowOnAtom() const
-{
- for (ReferenceVectorConstIterator it=fReferences.begin(); it != fReferences.end(); it++) {
- Reference<A>* ref = *it;
- if ( ref->getKind() == A::kFollowOn )
- return ref->getTarget();
- }
- return *((ObjectFile::Atom*)NULL);
-}
-
-
-class Beyond
-{
-public:
- Beyond(uint64_t offset) : fOffset(offset) {}
- bool operator()(ObjectFile::Reference* ref) const {
- return ( ref->getFixUpOffset() >= fOffset );
- }
-private:
- uint64_t fOffset;
-};
-
-
-template <typename A>
-void SymbolAtom<A>::setSize(uint64_t size)
-{
- // when resizing, any references beyond the new size are tossed
- if ( (fSize != 0) && (fReferences.size() > 0) )
- fReferences.erase(std::remove_if(fReferences.begin(), fReferences.end(), Beyond(size)), fReferences.end());
- // set new size
- fSize = size;
-}
-
-template <typename A>
-void SymbolAtom<A>::copyRawContent(uint8_t buffer[]) const
-{
- // copy base bytes
- if ( isZeroFill() )
- bzero(buffer, fSize);
- else {
- uint32_t fileOffset = fSection->offset() - fSection->addr() + fAddress;
- memcpy(buffer, (char*)(fOwner.fHeader)+fileOffset, fSize);
- }
-}
-
-//
-// A SymbolAliasAtom represents an alternate name for a SymbolAtom
-//
-//
-template <typename A>
-class SymbolAliasAtom : public BaseAtom
-{
-public:
- virtual ObjectFile::Reader* getFile() const { return fAliasOf.getFile(); }
- virtual bool getTranslationUnitSource(const char** dir, const char** name) const
- { return fAliasOf.getTranslationUnitSource(dir, name); }
- virtual const char* getName() const { return fName; }
- virtual const char* getDisplayName() const { return fName; }
- virtual ObjectFile::Atom::Scope getScope() const { return fScope; }
- virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return fAliasOf.getDefinitionKind(); }
- virtual SymbolTableInclusion getSymbolTableInclusion() const { return fAliasOf.getSymbolTableInclusion(); }
- virtual bool dontDeadStrip() const { return fDontDeadStrip; }
- virtual bool isZeroFill() const { return fAliasOf.isZeroFill(); }
- virtual bool isThumb() const { return fAliasOf.isThumb(); }
- virtual uint64_t getSize() const { return 0; }
- virtual std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(fReferences); }
- virtual bool mustRemainInSection() const { return true; }
- virtual const char* getSectionName() const { return fAliasOf.getSectionName(); }
- virtual Segment<A>& getSegment() const { return (Segment<A>&)fAliasOf.getSegment(); }
- virtual ObjectFile::Atom& getFollowOnAtom() const { return (ObjectFile::Atom&)fAliasOf; }
- virtual std::vector<ObjectFile::LineInfo>* getLineInfo() const { return NULL; }
- virtual ObjectFile::Alignment getAlignment() const { return fAliasOf.getAlignment(); }
- virtual void copyRawContent(uint8_t buffer[]) const {}
- virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; }
- virtual void setSize(uint64_t size) { }
- virtual void addReference(ObjectFile::Reference* ref) { fReferences.push_back((Reference<A>*)ref); }
- virtual void sortReferences() { std::sort(fReferences.begin(), fReferences.end(), ReferenceSorter()); }
- virtual void addLineInfo(const ObjectFile::LineInfo& info) { }
- virtual uint64_t getObjectAddress() const { return fAliasOf.getObjectAddress(); }
- virtual const void* getSectionRecord() const { return fAliasOf.getSectionRecord(); }
- virtual bool isAlias() const { return true; }
-
-protected:
- typedef typename A::P P;
- typedef typename std::vector<Reference<A>*> ReferenceVector;
- typedef typename ReferenceVector::iterator ReferenceVectorIterator; // seems to help C++ parser
- typedef typename ReferenceVector::const_iterator ReferenceVectorConstIterator; // seems to help C++ parser
- friend class Reader<A>;
-
- SymbolAliasAtom(const char* name, const macho_nlist<P>*, const BaseAtom& );
- virtual ~SymbolAliasAtom() {}
-
- const char* fName;
- const BaseAtom& fAliasOf;
- ObjectFile::Atom::Scope fScope;
- bool fDontDeadStrip;
- ReferenceVector fReferences;
-};
-
-
-template <typename A>
-SymbolAliasAtom<A>::SymbolAliasAtom(const char* name, const macho_nlist<P>* symbol, const BaseAtom& aliasOf)
- : fName(name), fAliasOf(aliasOf)
-{
- //fprintf(stderr, "SymbolAliasAtom(%p) %s\n", this, name);
- if ( symbol != NULL ) {
- uint8_t type = symbol->n_type();
- if ( (type & N_EXT) == 0 )
- fScope = ObjectFile::Atom::scopeTranslationUnit;
- else if ( (type & N_PEXT) != 0 )
- fScope = ObjectFile::Atom::scopeLinkageUnit;
- else
- fScope = ObjectFile::Atom::scopeGlobal;
- fDontDeadStrip = ((symbol->n_desc() & (N_NO_DEAD_STRIP|REFERENCED_DYNAMICALLY)) != 0);
- }
- else {
- // aliases defined on the command line are initially global scope
- fScope = ObjectFile::Atom::scopeGlobal;
- fDontDeadStrip = false;
- }
- // add follow-on reference to real atom
- new Reference<A>(A::kFollowOn, AtomAndOffset(this), AtomAndOffset((ObjectFile::Atom*)&aliasOf));
-}
-
-
-//
-// A TentativeAtom represents a C "common" or "tentative" defintion of data.
-// For instance, "int foo;" is neither a declaration or a definition and
-// is represented by a TentativeAtom.
-//
-template <typename A>
-class TentativeAtom : public BaseAtom
-{
-public:
- virtual ObjectFile::Reader* getFile() const { return &fOwner; }
- virtual bool getTranslationUnitSource(const char** dir, const char** name) const
- { return fOwner.getTranslationUnitSource(dir, name); }
- virtual const char* getName() const { return &fOwner.fStrings[fSymbol->n_strx()]; }
- virtual const char* getDisplayName() const { return getName(); }
- virtual ObjectFile::Atom::Scope getScope() const { return fScope; }
- virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ObjectFile::Atom::kTentativeDefinition; }
- virtual bool isZeroFill() const { return true; }
- virtual bool isThumb() const { return false; }
- 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 const char* getSectionName() const;
- virtual ObjectFile::Segment& getSegment() const { return DataSegment::fgSingleton; }
- virtual ObjectFile::Atom& getFollowOnAtom() const { return *(ObjectFile::Atom*)NULL; }
- virtual std::vector<ObjectFile::LineInfo>* getLineInfo() const { return NULL; }
- virtual ObjectFile::Alignment getAlignment() const;
- virtual void copyRawContent(uint8_t buffer[]) const;
- virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; }
- virtual void setSize(uint64_t size) { }
- virtual void addReference(ObjectFile::Reference* ref) { throw "ld: can't add references"; }
- virtual void sortReferences() { }
- virtual void addLineInfo(const ObjectFile::LineInfo& info) { throw "ld: can't add line info to tentative definition"; }
- virtual uint64_t getObjectAddress() const { return ULLONG_MAX; }
- virtual const void* getSectionRecord() const { return NULL; }
-
-protected:
- typedef typename A::P P;
- typedef typename A::P::E E;
- typedef typename A::P::uint_t pint_t;
- typedef typename A::ReferenceKinds Kinds;
- friend class Reader<A>;
-
- TentativeAtom(Reader<A>&, const macho_nlist<P>*);
- virtual ~TentativeAtom() {}
-
- Reader<A>& fOwner;
- const macho_nlist<P>* fSymbol;
- ObjectFile::Atom::Scope fScope;
- static std::vector<ObjectFile::Reference*> fgNoReferences;
-};
-
-template <typename A>
-std::vector<ObjectFile::Reference*> TentativeAtom<A>::fgNoReferences;
-
-template <typename A>
-TentativeAtom<A>::TentativeAtom(Reader<A>& owner, const macho_nlist<P>* symbol)
- : fOwner(owner), fSymbol(symbol)
-{
- uint8_t type = symbol->n_type();
- if ( (type & N_EXT) == 0 )
- fScope = ObjectFile::Atom::scopeTranslationUnit;
- else if ( (type & N_PEXT) != 0 )
- fScope = ObjectFile::Atom::scopeLinkageUnit;
- else
- fScope = ObjectFile::Atom::scopeGlobal;
- if ( ((type & N_TYPE) == N_UNDF) && (symbol->n_value() != 0) ) {
- // tentative definition
- }
- else {
- warning("unknown symbol type: %d", type);
- }
- //fprintf(stderr, "TentativeAtom(%p) %s\n", this, this->getDisplayName());
-}
-
-
-template <typename A>
-ObjectFile::Alignment TentativeAtom<A>::getAlignment() const
-{
- uint8_t alignment = GET_COMM_ALIGN(fSymbol->n_desc());
- if ( alignment == 0 ) {
- // common symbols align to their size
- // that is, a 4-byte common aligns to 4-bytes
- // if this size is not a power of two,
- // then round up to the next power of two
- uint64_t size = this->getSize();
- alignment = 63 - (uint8_t)__builtin_clzll(size);
- if ( size != (1ULL << alignment) )
- ++alignment;
- }
- // limit alignment of extremely large commons to 2^15 bytes (8-page)
- if ( alignment < 12 )
- return ObjectFile::Alignment(alignment);
- else
- return ObjectFile::Alignment(12);
-}
-
-template <typename A>
-const char* TentativeAtom<A>::getSectionName() const
-{
- if ( fOwner.fOptions.fForFinalLinkedImage || fOwner.fOptions.fMakeTentativeDefinitionsReal )
- return "__common";
- else
- return "._tentdef";
-}
-
-
-template <typename A>
-void TentativeAtom<A>::copyRawContent(uint8_t buffer[]) const
-{
- bzero(buffer, getSize());
-}
-
-
-//
-// An AnonymousAtom represents compiler generated data that has no name.
-// For instance, a literal C-string or a 64-bit floating point constant
-// is represented by an AnonymousAtom.
-//
-template <typename A>
-class AnonymousAtom : public BaseAtom
-{
-public:
- virtual ObjectFile::Reader* getFile() const { return &fOwner; }
- virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; }
- virtual const char* getName() const { return fSynthesizedName; }
- virtual const char* getDisplayName() const;
- virtual ObjectFile::Atom::Scope getScope() const;
- virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return fKind; }
- virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return fSymbolTableInclusion; }
- virtual bool dontDeadStrip() const { return fDontDeadStrip; }
- virtual bool isZeroFill() const;
- virtual bool isThumb() const { return false; }
- virtual uint64_t getSize() const { return fSize; }
- virtual std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(fReferences); }
- virtual bool mustRemainInSection() const { return true; }
- virtual const char* getSectionName() const;
- virtual Segment<A>& getSegment() const { return *fSegment; }
- virtual ObjectFile::Atom& getFollowOnAtom() const;
- virtual std::vector<ObjectFile::LineInfo>* getLineInfo() const { return NULL; }
- virtual ObjectFile::Alignment getAlignment() const;
- virtual void copyRawContent(uint8_t buffer[]) const;
- virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; }
- virtual void setSize(uint64_t size) { fSize = size; }
- virtual void addReference(ObjectFile::Reference* ref) { fReferences.push_back((Reference<A>*)ref); }
- virtual void sortReferences() { std::sort(fReferences.begin(), fReferences.end(), ReferenceSorter()); }
- virtual void addLineInfo(const ObjectFile::LineInfo& info) { warning("can't add line info to anonymous symbol %s from %s", this->getDisplayName(), this->getFile()->getPath()); }
- virtual uint64_t getObjectAddress() const { return fAddress; }
- virtual const void* getSectionRecord() const { return (const void*)fSection; }
- BaseAtom* redirectTo() { return fRedirect; }
- bool isWeakImportStub() { return fWeakImportStub; }
- void resolveName();
-
-protected:
- typedef typename A::P P;
- typedef typename A::P::E E;
- typedef typename A::P::uint_t pint_t;
- typedef typename A::ReferenceKinds Kinds;
- typedef typename std::vector<Reference<A>*> ReferenceVector;
- typedef typename ReferenceVector::iterator ReferenceVectorIterator; // seems to help C++ parser
- typedef typename ReferenceVector::const_iterator ReferenceVectorConstIterator; // seems to help C++ parser
- friend class Reader<A>;
-
- AnonymousAtom(Reader<A>&, const macho_section<P>*, pint_t addr, pint_t size);
- virtual ~AnonymousAtom() {}
- static bool cstringsHaveLabels();
-
- Reader<A>& fOwner;
- const char* fSynthesizedName;
- const char* fDisplayName;
- const macho_section<P>* fSection;
- pint_t fAddress;
- pint_t fSize;
- Segment<A>* fSegment;
- ReferenceVector fReferences;
- BaseAtom* fRedirect;
- bool fDontDeadStrip;
- bool fWeakImportStub;
- ObjectFile::Atom::SymbolTableInclusion fSymbolTableInclusion;
- ObjectFile::Atom::Scope fScope;
- ObjectFile::Atom::DefinitionKind fKind;
-};
-
-template <typename A>
-AnonymousAtom<A>::AnonymousAtom(Reader<A>& owner, const macho_section<P>* section, pint_t addr, pint_t size)
- : fOwner(owner), fSynthesizedName(NULL), fDisplayName(NULL), fSection(section), fAddress(addr), fSize(size),
- fSegment(NULL), fDontDeadStrip(true), fWeakImportStub(false), fSymbolTableInclusion(ObjectFile::Atom::kSymbolTableNotIn),
- fScope(ObjectFile::Atom::scopeTranslationUnit), fKind(ObjectFile::Atom::kRegularDefinition)
-{
- fSegment = new Segment<A>(fSection);
- fRedirect = this;
- uint8_t type = fSection->flags() & SECTION_TYPE;
- //fprintf(stderr, "AnonymousAtom(%p) addr=0x%llX in %s from %s\n", this, (long long)addr, section->sectname(), owner.getPath());
- switch ( type ) {
- case S_ZEROFILL:
- {
- asprintf((char**)&fSynthesizedName, "zero-fill-at-0x%08X", addr);
- }
- break;
- case S_COALESCED:
- case S_REGULAR:
- 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
- fSynthesizedName = ".objc_class_name_PENDING";
- owner.fAtomsPendingAName.push_back(this);
- owner.fSectionsWithAtomsPendingAName.insert(fSection);
- 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);
- }
- else if ((strcmp(section->sectname(), "__cfstring") == 0) && (strcmp(section->segname(), "__DATA") == 0)) {
- fSynthesizedName = "cfstring-pointer-name-PENDING";
- fScope = ObjectFile::Atom::scopeLinkageUnit;
- owner.fAtomsPendingAName.push_back(this);
- owner.fSectionsWithAtomsPendingAName.insert(fSection);
- fDontDeadStrip = false;
- fKind = ObjectFile::Atom::kWeakDefinition;
- }
- 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;
- fKind = ObjectFile::Atom::kWeakDefinition;
- fDontDeadStrip = false;
- if ( !fOwner.fOptions.fForFinalLinkedImage && cstringsHaveLabels() )
- fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn;
- }
- 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;
- fKind = ObjectFile::Atom::kWeakDefinition;
- 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;
- fKind = ObjectFile::Atom::kWeakDefinition;
- 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;
- fKind = ObjectFile::Atom::kWeakDefinition;
- fDontDeadStrip = false;
- }
- break;
- case S_LITERAL_POINTERS:
- {
- //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);
- fSynthesizedName = "literal-pointer-name-PENDING";
- fScope = ObjectFile::Atom::scopeLinkageUnit;
- fKind = ObjectFile::Atom::kWeakDefinition;
- fDontDeadStrip = false;
- owner.fAtomsPendingAName.push_back(this);
- owner.fSectionsWithAtomsPendingAName.insert(fSection);
- }
- break;
- case S_MOD_INIT_FUNC_POINTERS:
- asprintf((char**)&fSynthesizedName, "initializer$%d", (addr - (uint32_t)fSection->addr())/sizeof(pint_t));
- break;
- case S_MOD_TERM_FUNC_POINTERS:
- asprintf((char**)&fSynthesizedName, "terminator$%d", (addr - (uint32_t)fSection->addr())/sizeof(pint_t));
- break;
- case S_SYMBOL_STUBS:
- {
- uint32_t index = (fAddress - fSection->addr()) / fSection->reserved2();
- index += fSection->reserved1();
- uint32_t symbolIndex = E::get32(fOwner.fIndirectTable[index]);
- const macho_nlist<P>* sym = &fOwner.fSymbols[symbolIndex];
- uint32_t strOffset = sym->n_strx();
- // want name to not have $stub suffix, this is what automatic stub generation expects
- fSynthesizedName = &fOwner.fStrings[strOffset];
- // check for weak import
- fWeakImportStub = fOwner.isWeakImportSymbol(sym);
- // sometimes the compiler gets confused and generates a stub to a static function
- // if so, we should redirect any call to the stub to be calls to the real static function atom
- if ( ((sym->n_type() & N_TYPE) != N_UNDF) && ((sym->n_type() & N_EXT) == 0) ) {
- BaseAtom* staticAtom = fOwner.findAtomByName(fSynthesizedName);
- if ( staticAtom != NULL )
- fRedirect = staticAtom;
- }
- fKind = ObjectFile::Atom::kWeakDefinition;
- // might be a spurious stub for a static function, make stub static too
- if ( (sym->n_type() & N_EXT) == 0 )
- fScope = ObjectFile::Atom::scopeTranslationUnit;
- else
- 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
- 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>* closestSym = NULL;
- 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_type() & N_STAB) == 0) ) {
- if ( sym->n_value() == nonLazyPtrValue ) {
- const char* name = &fOwner.fStrings[sym->n_strx()];
- char* str = new char[strlen(name)+16];
- strcpy(str, name);
- strcat(str, "$non_lazy_ptr");
- fSynthesizedName = str;
- // add direct reference to target later, because its atom may not be constructed yet
- fOwner.fLocalNonLazys.push_back(this);
- fScope = ObjectFile::Atom::scopeTranslationUnit;
- return;
- }
- else if ( (sym->n_value() < nonLazyPtrValue) && ((closestSym == NULL) || (sym->n_value() > closestSym->n_value())) ) {
- closestSym = sym;
- }
- }
- }
- // add direct reference to target later, because its atom may not be constructed yet
- if ( closestSym != NULL ) {
- const char* name = &fOwner.fStrings[closestSym->n_strx()];
- char* str;
- asprintf(&str, "%s+%u$non_lazy_ptr", name, nonLazyPtrValue - closestSym->n_value());
- fSynthesizedName = str;
- }
- else {
- fSynthesizedName = "$interior$non_lazy_ptr";
- }
- fScope = ObjectFile::Atom::scopeTranslationUnit;
- fOwner.fLocalNonLazys.push_back(this);
- return;
- }
- const macho_nlist<P>* targetSymbol = &fOwner.fSymbols[symbolIndex];
- const char* name = &fOwner.fStrings[targetSymbol->n_strx()];
- char* str = new char[strlen(name)+16];
- strcpy(str, name);
- if ( type == S_LAZY_SYMBOL_POINTERS )
- strcat(str, "$lazy_ptr");
- else
- strcat(str, "$non_lazy_ptr");
- fSynthesizedName = str;
-
- // optimize __IMPORT segment out of i386 dyld or if -slow_stubs is used
- if ( (fOwner.fOptions.fForDyld || fOwner.fOptions.fSlowx86Stubs) && (strcmp(fSection->segname(),"__IMPORT") == 0) ) {
- macho_section<P>* dummySection = new macho_section<P>(*fSection);
- dummySection->set_segname("__DATA");
- dummySection->set_sectname("__nl_symbol_ptr");
- fSection = dummySection;
- fSegment = new Segment<A>(fSection);
- }
-
- if ( type == S_NON_LAZY_SYMBOL_POINTERS )
- fKind = ObjectFile::Atom::kWeakDefinition;
-
- 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:
- throwf("section type %d not supported with address=0x%08X", type, addr);
- }
- //fprintf(stderr, "AnonymousAtom(%p) %s \n", this, this->getDisplayName());
-}
-
-// x86_64 uses L labels on cstrings to allow relocs with addends
-template <> bool AnonymousAtom<x86_64>::cstringsHaveLabels() { return true; }
-template <typename A> bool AnonymousAtom<A>::cstringsHaveLabels() { return false; }
-
-
-template <typename A>
-void AnonymousAtom<A>::resolveName()
-{
- if ( (strcmp(fSection->sectname(), "__class") == 0) && (strcmp(fSection->segname(), "__OBJC") == 0) ) {
- std::vector<ObjectFile::Reference*>& references = this->getReferences();
- // references are not yet sorted, so scan the vector
- for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) {
- if ( ((*rit)->getFixUpOffset() == sizeof(pint_t)) && ((*rit)->getKind() == A::kPointer) ) {
- const char* superStr = (*rit)->getTargetName();
- if ( strncmp(superStr, "cstring=", 8) == 0 ) {
- const char* superClassName;
- asprintf((char**)&superClassName, ".objc_class_name_%s", &superStr[8]);
- new Reference<A>(A::kNoFixUp, AtomAndOffset(this), superClassName, 0);
- }
- break;
- }
- }
- for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) {
- if ( ((*rit)->getFixUpOffset() == 2*sizeof(pint_t)) && ((*rit)->getKind() == A::kPointer) ) {
- const char* classStr = (*rit)->getTargetName();
- if ( strncmp(classStr, "cstring=", 8) == 0 ) {
- asprintf((char**)&fSynthesizedName, ".objc_class_name_%s", &classStr[8]);
- }
- break;
- }
- }
- }
- else if ( (fSection->flags() & SECTION_TYPE) == S_LITERAL_POINTERS) {
- std::vector<ObjectFile::Reference*>& references = this->getReferences();
- if ( references.size() < 1 )
- throwf("S_LITERAL_POINTERS section %s,%s missing relocs", fSection->segname(), fSection->sectname());
- ObjectFile::Reference* ref = references[0];
- const char* str = ref->getTargetName();
- if ( strncmp(str, "cstring=", 8) == 0 ) {
- asprintf((char**)&fSynthesizedName, "literal-pointer@%s@%s@%s", fSection->segname(), fSection->sectname(), &str[8]);
- }
- }
- else if ( (strcmp(fSection->sectname(), "__cfstring") == 0) && (strcmp(fSection->segname(), "__DATA") == 0) ) {
- // references are not yet sorted, so scan the vector
- std::vector<ObjectFile::Reference*>& references = this->getReferences();
- for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) {
- if ( ((*rit)->getFixUpOffset() == 2*sizeof(pint_t)) && ((*rit)->getKind() == A::kPointer) ) {
- const char* superStr = (*rit)->getTargetName();
- if ( (superStr != NULL) && (strncmp(superStr, "cstring=", 8) == 0) ) {
- asprintf((char**)&fSynthesizedName, "cfstring=%s", &superStr[8]);
- }
- else {
- // compiled with -fwritable-strings or a non-ASCII string
- ObjectFile::Atom& stringDataAtom = (*rit)->getTarget();
- uint8_t buffer[stringDataAtom.getSize()];
- stringDataAtom.copyRawContent(buffer);
- fKind = ObjectFile::Atom::kRegularDefinition; // these are not coalescable
- fScope = ObjectFile::Atom::scopeTranslationUnit;
- fSynthesizedName = "cfstring-not-coalesable";
- }
- break;
- }
- }
- }
-}
-
-
-template <typename A>
-const char* AnonymousAtom<A>::getDisplayName() const
-{
- if ( fSynthesizedName != NULL )
- return fSynthesizedName;
-
- if ( fDisplayName != NULL )
- return fDisplayName;
-
- if ( (fSection->flags() & SECTION_TYPE) == S_CSTRING_LITERALS ) {
- uint32_t fileOffset = fSection->offset() - fSection->addr() + fAddress;
- asprintf((char**)&fDisplayName, "atom string literal: \"%s\"", (char*)(fOwner.fHeader)+fileOffset);
- }
- else {
- asprintf((char**)&fDisplayName, "%s@%d", fSection->sectname(), fAddress - (uint32_t)fSection->addr() );
- }
- return fDisplayName;
-}
-
-
-template <typename A>
-ObjectFile::Atom::Scope AnonymousAtom<A>::getScope() const
-{
- return fScope;
-}
-
-
-template <typename A>
-bool AnonymousAtom<A>::isZeroFill() const
-{
- return ( (fSection->flags() & SECTION_TYPE) == S_ZEROFILL );
-}
-
-
-template <typename A>
-const char* AnonymousAtom<A>::getSectionName() const
-{
- if ( strlen(fSection->sectname()) > 15 ) {
- static char temp[18];
- strncpy(temp, fSection->sectname(), 16);
- temp[17] = '\0';
- return temp;
- }
- return fSection->sectname();
-}
-
-template <typename A>
-ObjectFile::Alignment AnonymousAtom<A>::getAlignment() const
-{
- switch ( fSection->flags() & SECTION_TYPE ) {
- case S_4BYTE_LITERALS:
- return ObjectFile::Alignment(2);
- case S_8BYTE_LITERALS:
- return ObjectFile::Alignment(3);
- case S_16BYTE_LITERALS:
- return ObjectFile::Alignment(4);
- case S_NON_LAZY_SYMBOL_POINTERS:
- return ObjectFile::Alignment((uint8_t)log2(sizeof(pint_t)));
- case S_CSTRING_LITERALS:
- if ( ! fOwner.fOptions.fForFinalLinkedImage )
- return ObjectFile::Alignment(fSection->align());
- default:
- return ObjectFile::Alignment(fSection->align(), fAddress % (1 << fSection->align()));
- }
-}
-
-
-template <typename A>
-ObjectFile::Atom& AnonymousAtom<A>::getFollowOnAtom() const
-{
- for (ReferenceVectorConstIterator it=fReferences.begin(); it != fReferences.end(); it++) {
- Reference<A>* ref = *it;
- if ( ref->getKind() == A::kFollowOn )
- return ref->getTarget();
- }
- return *((ObjectFile::Atom*)NULL);
-}
-
-template <typename A>
-void AnonymousAtom<A>::copyRawContent(uint8_t buffer[]) const
-{
- // copy base bytes
- if ( isZeroFill() )
- bzero(buffer, fSize);
- else {
- uint32_t fileOffset = fSection->offset() - fSection->addr() + fAddress;
- memcpy(buffer, (char*)(fOwner.fHeader)+fileOffset, fSize);
- }
-}
-
-
-//
-// An AbsoluteAtom represents an N_ABS symbol which can only be created in
-// assembly language and usable by static executables such as the kernel/
-//
-template <typename A>
-class AbsoluteAtom : public BaseAtom
-{
-public:
- virtual ObjectFile::Reader* getFile() const { return &fOwner; }
- virtual bool getTranslationUnitSource(const char** dir, const char** name) const
- { return fOwner.getTranslationUnitSource(dir, name); }
- virtual const char* getName() const { return &fOwner.fStrings[fSymbol->n_strx()]; }
- virtual const char* getDisplayName() const { return getName(); }
- virtual ObjectFile::Atom::Scope getScope() const { return fScope; }
- virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ObjectFile::Atom::kAbsoluteSymbol; }
- virtual bool isZeroFill() const { return false; }
- virtual bool isThumb() const { return ((fSymbol->n_desc() & N_ARM_THUMB_DEF) != 0); }
- virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableInAsAbsolute; }
- virtual bool dontDeadStrip() const { return false; }
- virtual uint64_t getSize() const { return 0; }
- virtual std::vector<ObjectFile::Reference*>& getReferences() const { return fgNoReferences; }
- virtual bool mustRemainInSection() const { return true; }
- virtual const char* getSectionName() const { return "._absolute"; }
- virtual ObjectFile::Segment& getSegment() const { return LinkEditSegment::fgSingleton; }
- virtual ObjectFile::Atom& getFollowOnAtom() const { return *(ObjectFile::Atom*)NULL; }
- virtual std::vector<ObjectFile::LineInfo>* getLineInfo() const { return NULL; }
- virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); }
- 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 "ld: can't add references"; }
- virtual void sortReferences() { }
- virtual void addLineInfo(const ObjectFile::LineInfo& info) { throw "ld: can't add line info to tentative definition"; }
- virtual uint64_t getObjectAddress() const { return fSymbol->n_value(); }
- virtual void setSectionOffset(uint64_t offset) { /* don't let fSectionOffset be altered*/ }
- virtual const void* getSectionRecord() const { return NULL; }
-
-protected:
- typedef typename A::P P;
- typedef typename A::P::E E;
- typedef typename A::P::uint_t pint_t;
- typedef typename A::ReferenceKinds Kinds;
- friend class Reader<A>;
-
- AbsoluteAtom(Reader<A>&, const macho_nlist<P>*);
- virtual ~AbsoluteAtom() {}
-
- Reader<A>& fOwner;
- const macho_nlist<P>* fSymbol;
- ObjectFile::Atom::Scope fScope;
- static std::vector<ObjectFile::Reference*> fgNoReferences;
-};
-
-template <typename A>
-std::vector<ObjectFile::Reference*> AbsoluteAtom<A>::fgNoReferences;
-
-template <typename A>
-AbsoluteAtom<A>::AbsoluteAtom(Reader<A>& owner, const macho_nlist<P>* symbol)
- : fOwner(owner), fSymbol(symbol)
-{
- // store absolute adress in fSectionOffset
- fSectionOffset = symbol->n_value();
- // compute scope
- uint8_t type = symbol->n_type();
- if ( (type & N_EXT) == 0 )
- fScope = ObjectFile::Atom::scopeTranslationUnit;
- else if ( (type & N_PEXT) != 0 )
- fScope = ObjectFile::Atom::scopeLinkageUnit;
- else
- fScope = ObjectFile::Atom::scopeGlobal;
- //fprintf(stderr, "AbsoluteAtom(%p) %s\n", this, this->getDisplayName());
-}
-
-
-
-template <typename A>
-class Reader : public ObjectFile::Reader
-{
-public:
- static bool validFile(const uint8_t* fileContent);
- Reader(const uint8_t* fileContent, const char* path, time_t modTime,
- const ObjectFile::ReaderOptions& options, uint32_t ordinalBase);
- virtual ~Reader() {}
-
- virtual const char* getPath() { return fPath; }
- virtual time_t getModificationTime() { return fModTime; }
- virtual ObjectFile::Reader::DebugInfoKind getDebugInfoKind() { return fDebugInfo; }
- virtual std::vector<class ObjectFile::Atom*>& getAtoms() { return (std::vector<class ObjectFile::Atom*>&)(fAtoms); }
- virtual std::vector<class ObjectFile::Atom*>* getJustInTimeAtomsFor(const char* name) { return NULL; }
- virtual std::vector<Stab>* getStabs() { return &fStabs; }
- virtual ObjectFile::Reader::ObjcConstraint getObjCConstraint() { return fObjConstraint; }
- virtual uint32_t updateCpuConstraint(uint32_t current);
- virtual bool canScatterAtoms() { return (fHeader->flags() & MH_SUBSECTIONS_VIA_SYMBOLS); }
- virtual bool objcReplacementClasses(){ return fReplacementClasses; }
- virtual bool hasLongBranchStubs() { return fHasLongBranchStubs; }
-
- bool getTranslationUnitSource(const char** dir, const char** name) const;
-
-private:
- typedef typename A::P P;
- typedef typename A::P::E E;
- typedef typename A::P::uint_t pint_t;
- //typedef typename std::vector<Atom<A>*> AtomVector;
- //typedef typename AtomVector::iterator AtomVectorIterator; // seems to help C++ parser
- typedef typename A::ReferenceKinds Kinds;
- friend class AnonymousAtom<A>;
- friend class TentativeAtom<A>;
- friend class AbsoluteAtom<A>;
- friend class SymbolAtom<A>;
- typedef std::map<pint_t, BaseAtom*> AddrToAtomMap;
-
- void addReferencesForSection(const macho_section<P>* sect);
- bool addRelocReference(const macho_section<P>* sect, const macho_relocation_info<P>* reloc);
- bool addRelocReference_powerpc(const macho_section<P>* sect, const macho_relocation_info<P>* reloc);
- bool read_comp_unit(const char ** name, const char ** comp_dir, uint64_t *stmt_list);
- static bool isWeakImportSymbol(const macho_nlist<P>* sym);
- static bool skip_form(const uint8_t ** offset, const uint8_t * end, uint64_t form, uint8_t addr_size, bool dwarf64);
- static const char* assureFullPath(const char* path);
- AtomAndOffset findAtomAndOffset(pint_t addr);
- AtomAndOffset findAtomAndOffset(pint_t baseAddr, pint_t realAddr);
- Reference<A>* makeReference(Kinds kind, pint_t atAddr, pint_t toAddr);
- Reference<A>* makeReference(Kinds kind, pint_t atAddr, pint_t fromAddr, pint_t toAddr);
- Reference<A>* makeReferenceWithToBase(Kinds kind, pint_t atAddr, pint_t toAddr, pint_t toBaseAddr);
- Reference<A>* makeReferenceWithToBase(Kinds kind, pint_t atAddr, pint_t fromAddr, pint_t toAddr, pint_t toBaseAddr);
- Reference<A>* makeByNameReference(Kinds kind, pint_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, pint_t atAddr, const macho_nlist<P>* toSymbol, pint_t toOffset);
- void validSectionType(uint8_t type);
- void addDtraceExtraInfos(uint32_t probeAddr, const char* providerName);
- void setCpuConstraint(uint32_t cpusubtype);
-
- BaseAtom* findAtomByName(const char*);
-
- const char* fPath;
- time_t fModTime;
- uint32_t fOrdinalBase;
- const ObjectFile::ReaderOptions& fOptions;
- const macho_header<P>* fHeader;
- const char* fStrings;
- const macho_nlist<P>* fSymbols;
- uint32_t fSymbolCount;
- const macho_segment_command<P>* fSegment;
- const uint32_t* fIndirectTable;
- std::vector<BaseAtom*> fAtoms;
- AddrToAtomMap fAddrToAtom;
- AddrToAtomMap fAddrToAbsoluteAtom;
- std::vector<class AnonymousAtom<A>*> fLocalNonLazys;
- std::vector<class AnonymousAtom<A>*> fAtomsPendingAName;
- std::set<const macho_section<P>*> fSectionsWithAtomsPendingAName;
- std::vector<const char*> fDtraceProviderInfo;
- ObjectFile::Reader::DebugInfoKind fDebugInfo;
- bool fHasUUID;
- const macho_section<P>* fDwarfDebugInfoSect;
- const macho_section<P>* fDwarfDebugAbbrevSect;
- const macho_section<P>* fDwarfDebugLineSect;
- const char* fDwarfTranslationUnitDir;
- const char* fDwarfTranslationUnitFile;
- std::map<uint32_t,const char*> fDwarfIndexToFile;
- std::vector<Stab> fStabs;
- bool fAppleObjc;
- bool fHasDTraceProbes;
- bool fHaveIndirectSymbols;
- bool fReplacementClasses;
- bool fHasLongBranchStubs;
- ObjectFile::Reader::ObjcConstraint fObjConstraint;
- uint32_t fCpuConstraint;
-};
-
-template <typename A>
-Reader<A>::Reader(const uint8_t* fileContent, const char* path, time_t modTime, const ObjectFile::ReaderOptions& options, uint32_t ordinalBase)
- : fPath(strdup(path)), fModTime(modTime), fOrdinalBase(ordinalBase), fOptions(options), fHeader((const macho_header<P>*)fileContent),
- fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fSegment(NULL), fIndirectTable(NULL),
- fDebugInfo(kDebugInfoNone), fHasUUID(false), fDwarfDebugInfoSect(NULL), fDwarfDebugAbbrevSect(NULL), fDwarfDebugLineSect(NULL),
- fDwarfTranslationUnitDir(NULL), fDwarfTranslationUnitFile(NULL), fAppleObjc(false), fHasDTraceProbes(false),
- fHaveIndirectSymbols(false), fReplacementClasses(false), fHasLongBranchStubs(false),
- fObjConstraint(ObjectFile::Reader::kObjcNone), fCpuConstraint(ObjectFile::Reader::kCpuAny)
-{
- // sanity check
- if ( ! validFile(fileContent) )
- throw "not a valid mach-o object file";
-
- Reference<A>::fgForFinalLinkedImage = options.fForFinalLinkedImage;
-
- // write out path for -t or -whatsloaded option
- if ( options.fLogObjectFiles || options.fLogAllFiles )
- printf("%s\n", path);
-
- // cache intersting pointers
- const macho_header<P>* header = (const macho_header<P>*)fileContent;
- this->setCpuConstraint(header->cpusubtype());
- 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>* const cmdsEnd = (macho_load_command<P>*)((char*)header + sizeof(macho_header<P>) + header->sizeofcmds());
- 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_symtab_command<P>* symtab = (macho_symtab_command<P>*)cmd;
- fSymbolCount = symtab->nsyms();
- fSymbols = (const macho_nlist<P>*)((char*)header + symtab->symoff());
- fStrings = (char*)header + symtab->stroff();
- if ( undefinedEndIndex == 0 ) {
- undefinedStartIndex = 0;
- undefinedEndIndex = symtab->nsyms();
- }
- }
- break;
- case LC_DYSYMTAB:
- {
- 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:
- fHasUUID = true;
- break;
-
- default:
- if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
- fSegment = (macho_segment_command<P>*)cmd;
- }
- break;
- }
- cmd = (const macho_load_command<P>*)(((char*)cmd)+cmd->cmdsize());
- if ( cmd > cmdsEnd )
- throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, path);
- }
-
- // if there are no load commands, then this file has no content, so no atoms
- if ( header->ncmds() < 1 )
- return;
-
- 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()];
-
- // inital guess for number of atoms
- fAtoms.reserve(fSymbolCount);
-
- // add all atoms that have entries in symbol table
- const macho_section<P>* sections = (macho_section<P>*)((char*)fSegment + sizeof(macho_segment_command<P>));
- for (int i=fSymbolCount-1; i >= 0 ; --i) {
- // walk backwards through symbol table so globals are see before locals, otherwise a local alias would beome the reaal name
- const macho_nlist<P>& sym = fSymbols[i];
- if ( (sym.n_type() & N_STAB) == 0 ) {
- uint8_t type = (sym.n_type() & N_TYPE);
- if ( type == N_SECT ) {
- const macho_section<P>* section = §ions[sym.n_sect()-1];
- pint_t sectionEndAddr = section->addr() + section->size();
- bool suppress = false;
- // ignore atoms in debugger sections
- if ( (section->flags() & S_ATTR_DEBUG) == 0 ) {
- if ( strncmp(&fStrings[sym.n_strx()], "__dtrace_probe$", 15) == 0 ) {
- // ignore dtrace probe labels
- fHasDTraceProbes = true;
- }
- else if ( fStrings[sym.n_strx()] == 'L' ) {
- // ignore L labels, <rdar://problem/3962731>
- }
- else {
- // ignore labels for atoms in other sections
- switch ( section->flags() & SECTION_TYPE ) {
- case S_REGULAR:
- if ( (sym.n_desc() & N_WEAK_DEF) && strcmp(section->sectname(), "__picsymbolstub1__TEXT") == 0 )
- suppress = true; // ignore stubs in crt1.o built by old ld64 that was missing S_SYMBOL_STUBS
- case S_ZEROFILL:
- case S_COALESCED:
- case S_4BYTE_LITERALS:
- case S_8BYTE_LITERALS:
- case S_16BYTE_LITERALS:
- case S_CSTRING_LITERALS:
- {
- BaseAtom* newAtom;
- typename AddrToAtomMap::iterator pos = fAddrToAtom.find(sym.n_value());
- if ( (pos != fAddrToAtom.end()) && (strcmp(pos->second->getSectionName(), section->sectname())==0) ) {
- // another label to an existing address in the same section, make this an alias
- newAtom = new SymbolAliasAtom<A>(&fStrings[sym.n_strx()], &sym, *pos->second);
- }
- else {
- // make SymbolAtom atom for this address
- newAtom = new SymbolAtom<A>(*this, &sym, section);
- // don't add symbols at end of section to addr->atom map
- if ( sym.n_value() != sectionEndAddr )
- fAddrToAtom[newAtom->getObjectAddress()] = newAtom;
- }
- if ( ! suppress )
- fAtoms.push_back(newAtom);
- }
- break;
- case S_SYMBOL_STUBS:
- case S_LAZY_SYMBOL_POINTERS:
- case S_NON_LAZY_SYMBOL_POINTERS:
- // ignore symboled stubs produces by old ld64
- break;
- default:
- warning("symbol %s found in unsupported section in %s",
- &fStrings[sym.n_strx()], this->getPath());
- }
- }
- }
- }
- else if ( (type == N_UNDF) && (sym.n_value() != 0) ) {
- fAtoms.push_back(new TentativeAtom<A>(*this, &sym));
- }
- else if ( type == N_ABS ) {
- const char* symName = &fStrings[sym.n_strx()];
- if ( strncmp(symName, ".objc_class_name_", 17) == 0 ) {
- // ignore .objc_class_name_* symbols
- fAppleObjc = true;
- }
- else if ( strcmp(&symName[strlen(symName)-3], ".eh") == 0 ) {
- // ignore empty *.eh symbols
- }
- else {
- BaseAtom* abAtom = new AbsoluteAtom<A>(*this, &sym);
- fAtoms.push_back(abAtom);
- fAddrToAbsoluteAtom[sym.n_value()] = abAtom;
- }
- }
- else if ( type == N_INDR ) {
- fHaveIndirectSymbols = true;
- }
- }
- }
-
- // add all fixed size anonymous atoms from special sections
- for (const macho_section<P>* sect=sectionsStart; sect < sectionsEnd; ++sect) {
- pint_t atomSize = 0;
- uint8_t type (sect->flags() & SECTION_TYPE);
- validSectionType(type);
- bool suppress = false;
- switch ( type ) {
- case S_SYMBOL_STUBS:
- suppress = true;
- atomSize = sect->reserved2();
- break;
- case S_LAZY_SYMBOL_POINTERS:
- suppress = true;
- atomSize = sizeof(pint_t);
- break;
- case S_NON_LAZY_SYMBOL_POINTERS:
- case S_LITERAL_POINTERS:
- case S_MOD_INIT_FUNC_POINTERS:
- case S_MOD_TERM_FUNC_POINTERS:
- atomSize = sizeof(pint_t);
- break;
- case S_INTERPOSING:
- atomSize = sizeof(pint_t)*2;
- break;
- case S_4BYTE_LITERALS:
- atomSize = 4;
- break;
- case S_8BYTE_LITERALS:
- atomSize = 8;
- break;
- 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);
- }
- // get objc Garbage Collection info
- else if ( ((strcmp(sect->sectname(), "__image_info") == 0) && (strcmp(sect->segname(), "__OBJC") == 0))
- || ((strncmp(sect->sectname(), "__objc_imageinfo", 16) == 0) && (strcmp(sect->segname(), "__DATA") == 0)) ) {
- // struct objc_image_info {
- // uint32_t version; // initially 0
- // uint32_t flags;
- // };
- // #define OBJC_IMAGE_SUPPORTS_GC 2
- // #define OBJC_IMAGE_GC_ONLY 4
- //
- const uint32_t* contents = (uint32_t*)(((char*)fHeader) + sect->offset());
- if ( (sect->size() >= 8) && (contents[0] == 0) ) {
- uint32_t flags = E::get32(contents[1]);
- if ( (flags & 4) == 4 )
- fObjConstraint = ObjectFile::Reader::kObjcGC;
- else if ( (flags & 2) == 2 )
- fObjConstraint = ObjectFile::Reader::kObjcRetainReleaseOrGC;
- else
- fObjConstraint = ObjectFile::Reader::kObjcRetainRelease;
- if ( (flags & 1) == 1 )
- fReplacementClasses = true;
- // don't make atom for this section
- atomSize = sect->size();
- suppress = true;
- }
- else {
- warning("can't parse __OBJC/__image_info section in %s", fPath);
- }
- }
- // special case constant NS/CFString literals and make an atom out of each one
- else if ((strcmp(sect->sectname(), "__cfstring") == 0) && (strcmp(sect->segname(), "__DATA") == 0)) {
- atomSize = 4 * sizeof(pint_t);
- }
- break;
- }
- if ( atomSize != 0 ) {
- for(pint_t sectOffset=0; sectOffset < sect->size(); sectOffset += atomSize) {
- pint_t atomAddr = sect->addr() + sectOffset;
- // add if not already an atom at that address
- if ( fAddrToAtom.find(atomAddr) == fAddrToAtom.end() ) {
- AnonymousAtom<A>* newAtom = new AnonymousAtom<A>(*this, sect, atomAddr, atomSize);
- if ( !suppress )
- fAtoms.push_back(newAtom);
- fAddrToAtom[atomAddr] = newAtom->redirectTo();
- }
- }
- }
- }
-
- // add all c-string anonymous atoms
- for (const macho_section<P>* sect=sectionsStart; sect < sectionsEnd; ++sect) {
- if ( ((sect->flags() & SECTION_TYPE) == S_CSTRING_LITERALS) || strcmp(sect->sectname(), "__cstring") == 0 ) {
- uint32_t stringLen;
- pint_t stringAddr;
- BaseAtom* mostAlignedEmptyString = NULL;
- uint32_t mostAlignedEmptyStringTrailingZeros = 0;
- std::vector<std::pair<pint_t,BaseAtom*> > emptyStrings;
- for(pint_t sectOffset=0; sectOffset < sect->size(); sectOffset += stringLen) {
- stringAddr = sect->addr() + sectOffset;
- stringLen = strlen((char*)(fHeader) + sect->offset() + sectOffset) + 1;
- // add if not already an atom at that address
- if ( fAddrToAtom.find(stringAddr) == fAddrToAtom.end() ) {
- BaseAtom* newAtom = new AnonymousAtom<A>(*this, sect, stringAddr, stringLen);
- if ( stringLen == 1 ) {
- // because of padding it may look like there are lots of empty strings, keep track of all
- emptyStrings.push_back(std::make_pair<pint_t,BaseAtom*>(stringAddr, newAtom));
- // record empty string with greatest alignment requirement
- uint32_t stringAddrTrailingZeros = (stringAddr==0) ? sect->align() : __builtin_ctz(stringAddr);
- if ( (mostAlignedEmptyString == NULL)
- || ( stringAddrTrailingZeros > mostAlignedEmptyStringTrailingZeros) ) {
- mostAlignedEmptyString = newAtom;
- mostAlignedEmptyStringTrailingZeros = stringAddrTrailingZeros;
- }
- }
- else {
- fAtoms.push_back(newAtom);
- fAddrToAtom[stringAddr] = newAtom;
- }
- }
- }
- // map all uses of empty strings to the most aligned one
- if ( mostAlignedEmptyString != NULL ) {
- // make most aligned atom a real atom
- fAtoms.push_back(mostAlignedEmptyString);
- // map all other empty atoms to this one
- for (typename std::vector<std::pair<pint_t,BaseAtom*> >::iterator it=emptyStrings.begin(); it != emptyStrings.end(); it++) {
- fAddrToAtom[it->first] = mostAlignedEmptyString;
- }
- }
- }
- }
-
- // sort all atoms so far by address and section
- std::sort(fAtoms.begin(), fAtoms.end(), BaseAtomSorter());
-
- //fprintf(stderr, "sorted atoms:\n");
- //for (std::vector<BaseAtom*>::iterator it=fAtoms.begin(); it != fAtoms.end(); it++)
- // fprintf(stderr, "0x%08llX %s\n", (*it)->getObjectAddress(), (*it)->getDisplayName());
-
- // create atoms to cover any non-debug ranges not handled above
- for (const macho_section<P>* sect=sectionsStart; sect < sectionsEnd; ++sect) {
- pint_t sectionStartAddr = sect->addr();
- pint_t sectionEndAddr = sect->addr() + sect->size();
- const bool setFollowOnAtom = ! this->canScatterAtoms();
- if ( sect->size() != 0 ) {
- // ignore dwarf sections. If ld every supports processing dwarf, this logic will need to change
- if ( (sect->flags() & S_ATTR_DEBUG) != 0 ) {
- fDebugInfo = kDebugInfoDwarf;
- if ( strcmp(sect->sectname(), "__debug_info") == 0 )
- fDwarfDebugInfoSect = sect;
- else if ( strcmp(sect->sectname(), "__debug_abbrev") == 0 )
- fDwarfDebugAbbrevSect = sect;
- else if ( strcmp(sect->sectname(), "__debug_line") == 0 )
- fDwarfDebugLineSect = sect;
- }
- else {
- if ( strcmp(sect->segname(), "__DWARFA") == 0 ) {
- throw "object file contains old DWARF debug info - rebuild with newer compiler";
- }
- uint8_t type (sect->flags() & SECTION_TYPE);
- switch ( type ) {
- case S_REGULAR:
- case S_ZEROFILL:
- case S_COALESCED:
- // if there is not an atom already at the start of this section, add an anonymous one
- pint_t previousAtomAddr = 0;
- BaseAtom* previousAtom = NULL;
- if ( fAddrToAtom.find(sectionStartAddr) == fAddrToAtom.end() ) {
- BaseAtom* newAtom = new AnonymousAtom<A>(*this, sect, sect->addr(), 0);
- fAddrToAtom[sect->addr()] = newAtom;
- fAtoms.push_back(newAtom);
- previousAtomAddr = sectionStartAddr;
- previousAtom = newAtom;
- std::sort(fAtoms.begin(), fAtoms.end(), BaseAtomSorter());
- }
- // calculate size of all atoms in this section and add follow-on references
- for (std::vector<BaseAtom*>::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) {
- BaseAtom* atom = (BaseAtom*)(*it);
- pint_t atomAddr = atom->getObjectAddress();
- if ( atom->getSectionRecord() == sect ) {
- //fprintf(stderr, "addr=0x%08llX, atom=%s\n", (uint64_t)atomAddr, atom->getDisplayName());
- if ( (previousAtom != NULL) && (previousAtomAddr != atomAddr) ) {
- previousAtom->setSize(atomAddr - previousAtomAddr);
- if ( setFollowOnAtom && (atom != previousAtom) )
- new Reference<A>(A::kFollowOn, AtomAndOffset(previousAtom), AtomAndOffset(atom));
- }
- previousAtomAddr = atomAddr;
- previousAtom = atom;
- }
- }
- if ( previousAtom != NULL ) {
- // set last atom in section
- previousAtom->setSize(sectionEndAddr - previousAtomAddr);
- }
- break;
- }
- }
- }
- }
-
- // check for object file that defines no objc classes, but uses objc classes
- // check for dtrace provider info
- 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 ) {
- const char* undefinedName = &fStrings[sym.n_strx()];
- if ( !fAppleObjc && (strncmp(undefinedName, ".objc_class_name_", 17) == 0) ) {
- fAppleObjc = true;
- }
- else if ( strncmp(undefinedName, "___dtrace_", 10) == 0 ) {
- if ( strchr(undefinedName, '$') != NULL ) {
- if ( (strncmp(&undefinedName[10], "probe$", 6) != 0) && (strncmp(&undefinedName[10], "isenabled$", 10) != 0) ) {
- // any undefined starting with __dtrace_*$ that is not ___dtrace_probe$* or ___dtrace_isenabled$*
- // is extra provider info
- fDtraceProviderInfo.push_back(undefinedName);
- }
- }
- }
- }
- }
- }
-
- // add relocation based references to sections that have atoms with pending names
- for (const macho_section<P>* sect=sectionsStart; sect < sectionsEnd; ++sect) {
- if ( fSectionsWithAtomsPendingAName.count(sect) != 0 )
- addReferencesForSection(sect);
- }
-
- // update any anonymous atoms that need references built in order to name themselves
- for (typename std::vector<AnonymousAtom<A>*>::iterator it=fAtomsPendingAName.begin(); it != fAtomsPendingAName.end(); it++) {
- (*it)->resolveName();
- }
-
- // add relocation based references to other sections
- for (const macho_section<P>* sect=sectionsStart; sect < sectionsEnd; ++sect) {
- if ( fSectionsWithAtomsPendingAName.count(sect) == 0 )
- addReferencesForSection(sect);
- }
-
- // add objective-c references
- if ( fAppleObjc ) {
- for (const macho_section<P>* sect=sectionsStart; sect < sectionsEnd; ++sect) {
- if ( (strcmp(sect->sectname(), "__cls_refs") == 0) && (strcmp(sect->segname(), "__OBJC") == 0) ) {
- for (uint32_t offset = 0; offset < sect->size(); offset += sizeof(pint_t)) {
- AtomAndOffset ao = this->findAtomAndOffset(sect->addr()+offset);
- ObjectFile::Reference* classRef = ao.atom->getReferences()[0];
- if ( classRef->getFixUpOffset() == 0 ) {
- const char* classStr = classRef->getTargetName();
- if ( strncmp(classStr, "cstring=", 8) == 0 ) {
- const char* className;
- asprintf((char**)&className, ".objc_class_name_%s", &classStr[8]);
- new Reference<A>(A::kNoFixUp, ao, 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;
- uint32_t fileOffset = localNonLazy->fSection->offset() - localNonLazy->fSection->addr() + localNonLazy->fAddress;
- pint_t nonLazyPtrValue = P::getP(*((pint_t*)((char*)(fHeader)+fileOffset)));
- makeReference(A::kPointer, localNonLazy->fAddress, nonLazyPtrValue);
- }
-
- // add implicit direct reference from each C++ function to its eh info
- for (const macho_section<P>* sect=sectionsStart; sect < sectionsEnd; ++sect) {
- if ( ((sect->flags() & SECTION_TYPE) == S_COALESCED) && (strcmp(sect->sectname(), "__eh_frame") == 0) ) {
- for (typename AddrToAtomMap::iterator it=fAddrToAtom.begin(); it != fAddrToAtom.end(); it++) {
- // note: this algorithm depens on the map iterator returning entries in address order
- if ( (it->first >= sect->addr()) && (it->first < sect->addr()+sect->size()) ) {
- pint_t ehAtomAddress = it->first;
- BaseAtom* ehAtom = it->second;
- const char* ehName = ehAtom->getName();
- if ( (ehName != NULL) && (strcmp(&ehName[strlen(ehName)-3], ".eh") == 0) ) {
- makeReferenceToEH(ehName, ehAtomAddress, sect);
- // make EH symbol static so linker does not try to coalesce
- if ( fOptions.fForFinalLinkedImage )
- ehAtom->setScope(ObjectFile::Atom::scopeTranslationUnit);
- // if it has a reference to a LSDA, add a group reference
- std::vector<class ObjectFile::Reference*>& ehrefs = ehAtom->getReferences();
- // all FDE's have at least 2 references (to CIE and to function)
- if ( ehrefs.size() > 2 ) {
- // a third reference means there is a LSDA
- ObjectFile::Atom* lsdaAtom = NULL;
- for (std::vector<ObjectFile::Reference*>::iterator rit=ehrefs.begin(); rit != ehrefs.end(); rit++) {
- ObjectFile::Reference* ref = *rit;
- switch ( ref->getFixUpOffset() ) {
- case 4:
- case 8:
- // these are CIE and function references
- break;
- default:
- // this is LSDA reference
- lsdaAtom = &ref->getTarget();
- }
- }
- if ( lsdaAtom != NULL ) {
- new Reference<A>(A::kGroupSubordinate, AtomAndOffset(ehAtom), AtomAndOffset(lsdaAtom));
- }
- }
- }
- }
- }
- }
- }
-
- // add command line aliases
- for(std::vector<ObjectFile::ReaderOptions::AliasPair>::const_iterator it = fOptions.fAliases.begin(); it != fOptions.fAliases.end(); ++it) {
- BaseAtom* target = this->findAtomByName(it->realName);
- if ( (target != NULL) && target->getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn )
- fAtoms.push_back(new SymbolAliasAtom<A>(it->alias, NULL, *target));
- }
-
- // add dtrace probe locations
- if ( fHasDTraceProbes ) {
- for (uint32_t i=0; i < fSymbolCount; ++i) {
- const macho_nlist<P>& sym = fSymbols[i];
- if ( (sym.n_type() & N_STAB) == 0 ) {
- if ( (sym.n_type() & N_TYPE) == N_SECT ) {
- const char* symbolName = &fStrings[sym.n_strx()];
- if ( strncmp(symbolName, "__dtrace_probe$", 15) == 0 ) {
- //fprintf(stderr, "adding dtrace probe at 0x%08llX %s\n", sym.n_value(), symbolName);
- makeByNameReference(A::kDtraceProbe, sym.n_value(), symbolName, 0);
- }
- }
- }
- }
- }
-
- // turn indirect symbols int SymbolAliasAtom
- if ( fHaveIndirectSymbols ) {
- for (uint32_t i=0; i < fSymbolCount; ++i) {
- const macho_nlist<P>& sym = fSymbols[i];
- if ( (sym.n_type() & N_STAB) == 0 ) {
- if ( (sym.n_type() & N_TYPE) == N_INDR ) {
- const char* aliasName = &fStrings[sym.n_strx()];
- const char* targetName = &fStrings[sym.n_value()];
- //fprintf(stderr, "found alias %s for %s\n", aliasName, targetName);
- BaseAtom* target = this->findAtomByName(targetName);
- // only currently support N_INDR based aliases to something in the same .o file
- if ( target != NULL ) {
- fAtoms.push_back(new SymbolAliasAtom<A>(aliasName, &sym, *target));
- //fprintf(stderr, "creating alias %s for %s\n", aliasName, targetName);
- }
- }
- }
- }
- }
-
- //for (typename AddrToAtomMap::iterator it=fAddrToAtom.begin(); it != fAddrToAtom.end(); it++) {
- // fprintf(stderr, "[0x%0X -> 0x%0llX) : %s\n", it->first, it->first+it->second->getSize(), it->second->getDisplayName());
- //}
-
- // add translation unit info from dwarf
- uint64_t stmtList;
- if ( (fDebugInfo == kDebugInfoDwarf) && (fOptions.fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone) ) {
- // 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;
- warning("can't parse dwarf compilation unit info in %s", this->getPath());
- fDebugInfo = kDebugInfoNone;
- }
- }
- }
-
- // add line number info to atoms from dwarf
- if ( (fDebugInfo == kDebugInfoDwarf) && (fOptions.fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone) ) {
- // file with just data will have no __debug_line info
- if ( (fDwarfDebugLineSect != NULL) && (fDwarfDebugLineSect->size() != 0) && (fAddrToAtom.size() != 0)
- && (fDwarfDebugInfoSect != NULL) && (fDwarfDebugInfoSect->size() != 0) ) {
- // validate stmt_list
- if ( (stmtList != (uint64_t)-1) && (stmtList < fDwarfDebugLineSect->size()) ) {
- const uint8_t* debug_line = (uint8_t*)(fHeader) + fDwarfDebugLineSect->offset();
- if ( debug_line != NULL ) {
- struct line_reader_data* lines = line_open(&debug_line[stmtList],
- fDwarfDebugLineSect->size() - stmtList, E::little_endian);
- struct line_info result;
- ObjectFile::Atom* curAtom = NULL;
- uint32_t curAtomOffset = 0;
- uint32_t curAtomAddress = 0;
- uint32_t curAtomSize = 0;
- while ( line_next (lines, &result, line_stop_pc) ) {
- //fprintf(stderr, "curAtom=%p, result.pc=0x%llX, result.line=%llu, result.end_of_sequence=%d, curAtomAddress=0x%X, curAtomSize=0x%X\n",
- // curAtom, result.pc, result.line, result.end_of_sequence, curAtomAddress, curAtomSize);
- // work around weird debug line table compiler generates if no functions in __text section
- if ( (curAtom == NULL) && (result.pc == 0) && result.end_of_sequence && (result.file == 1))
- continue;
- // for performance, see if in next pc is in current atom
- 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 {
- // do slow look up of atom by address
- AtomAndOffset ao = this->findAtomAndOffset(result.pc);
- curAtom = ao.atom;
- if ( curAtom == NULL )
- break; // file has line info but no functions
- if ( result.end_of_sequence && (curAtomAddress+curAtomSize < result.pc) ) {
- // a one line function can be returned by line_next() as one entry with pc at end of blob
- // look for alt atom starting at end of previous atom
- uint32_t previousEnd = curAtomAddress+curAtomSize;
- AtomAndOffset alt = this->findAtomAndOffset(previousEnd);
- if ( result.pc <= previousEnd - alt.offset + alt.atom->getSize() ) {
- curAtom = alt.atom;
- curAtomOffset = alt.offset;
- curAtomAddress = previousEnd - alt.offset;
- curAtomSize = curAtom->getSize();
- }
- else {
- curAtomOffset = ao.offset;
- curAtomAddress = result.pc - ao.offset;
- curAtomSize = curAtom->getSize();
- }
- }
- else {
- curAtomOffset = ao.offset;
- curAtomAddress = result.pc - ao.offset;
- curAtomSize = curAtom->getSize();
- }
- }
- const char* filename;
- std::map<uint32_t,const char*>::iterator pos = fDwarfIndexToFile.find(result.file);
- if ( pos == fDwarfIndexToFile.end() ) {
- filename = line_file(lines, result.file);
- fDwarfIndexToFile[result.file] = filename;
- }
- else {
- filename = pos->second;
- }
- ObjectFile::LineInfo info;
- info.atomOffset = curAtomOffset;
- info.fileName = filename;
- info.lineNumber = result.line;
- //fprintf(stderr, "addr=0x%08llX, line=%lld, file=%s, 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);
- }
- else {
- warning("could not parse dwarf line number info in %s", this->getPath());
- }
- }
- }
- }
-
- // if no dwarf, try processing stabs debugging info
- if ( (fDebugInfo == kDebugInfoNone) && (fOptions.fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone) ) {
- // scan symbol table for stabs entries
- fStabs.reserve(fSymbolCount); // reduce re-allocations
- BaseAtom* currentAtom = NULL;
- pint_t currentAtomAddress = 0;
- enum { start, inBeginEnd, inFun } state = start;
- for (uint32_t symbolIndex = 0; symbolIndex < fSymbolCount; ++symbolIndex ) {
- const macho_nlist<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 = (fHasUUID ? kDebugInfoStabsUUID : kDebugInfoStabs);
- Stab stab;
- stab.atom = NULL;
- stab.type = type;
- stab.other = sym->n_sect();
- stab.desc = sym->n_desc();
- stab.value = sym->n_value();
- stab.string = NULL;
- switch (state) {
- case start:
- switch (type) {
- case N_BNSYM:
- // beginning of function block
- state = inBeginEnd;
- // fall into case to lookup atom by addresss
- case N_LCSYM:
- case N_STSYM:
- currentAtomAddress = sym->n_value();
- currentAtom = (BaseAtom*)this->findAtomAndOffset(currentAtomAddress).atom;
- if ( currentAtom != NULL ) {
- stab.atom = currentAtom;
- stab.string = symString;
- }
- else {
- fprintf(stderr, "can't find atom for stabs BNSYM at %08llX in %s",
- (uint64_t)sym->n_value(), path);
- }
- break;
- case N_SO:
- case N_OSO:
- case N_OPT:
- case N_LSYM:
- case N_RSYM:
- case N_PSYM:
- // not associated with an atom, just copy
- stab.string = symString;
- break;
- case N_GSYM:
- {
- // n_value field is NOT atom address ;-(
- // need to find atom by name match
- const char* colon = strchr(symString, ':');
- if ( colon != NULL ) {
- // build underscore leading name
- int nameLen = colon - symString;
- char symName[nameLen+2];
- strlcpy(&symName[1], symString, nameLen+1);
- symName[0] = '_';
- symName[nameLen+1] = '\0';
- currentAtom = findAtomByName(symName);
- if ( currentAtom != NULL ) {
- stab.atom = currentAtom;
- stab.string = symString;
- }
- }
- else {
- // might be a debug-note without trailing :G()
- currentAtom = findAtomByName(symString);
- if ( currentAtom != NULL ) {
- stab.atom = currentAtom;
- stab.string = symString;
- }
- }
- if ( stab.atom == NULL ) {
- warning("can't find atom for N_GSYM stabs %s in %s", symString, path);
- useStab = false;
- }
- break;
- }
- case N_FUN:
- // old style stabs without BNSYM
- state = inFun;
- currentAtomAddress = sym->n_value();
- currentAtom = (BaseAtom*)this->findAtomAndOffset(currentAtomAddress).atom;
- if ( currentAtom != NULL ) {
- stab.atom = currentAtom;
- stab.string = symString;
- }
- else {
- warning("can't find atom for stabs FUN at %08llX in %s",
- (uint64_t)currentAtomAddress, path);
- }
- break;
- case N_SOL:
- case N_SLINE:
- stab.string = symString;
- // old stabs
- break;
- case N_BINCL:
- case N_EINCL:
- case N_EXCL:
- stab.string = symString;
- // -gfull built .o file
- break;
- default:
- warning("unknown stabs type 0x%X in %s", type, path);
- }
- break;
- case inBeginEnd:
- stab.atom = currentAtom;
- switch (type) {
- case N_ENSYM:
- state = start;
- currentAtom = NULL;
- break;
- case N_LCSYM:
- case N_STSYM:
- {
- BaseAtom* nestedAtom = (BaseAtom*)this->findAtomAndOffset(sym->n_value()).atom;
- if ( nestedAtom != NULL ) {
- stab.atom = nestedAtom;
- stab.string = symString;
- }
- else {
- warning("can't find atom for stabs 0x%X at %08llX in %s",
- type, (uint64_t)sym->n_value(), path);
- }
- break;
- }
- case N_LBRAC:
- case N_RBRAC:
- case N_SLINE:
- // adjust value to be offset in atom
- stab.value -= currentAtomAddress;
- default:
- stab.string = symString;
- break;
- }
- break;
- case inFun:
- switch (type) {
- case N_FUN:
- if ( sym->n_sect() != 0 ) {
- // found another start stab, must be really old stabs...
- currentAtomAddress = sym->n_value();
- currentAtom = (BaseAtom*)this->findAtomAndOffset(currentAtomAddress).atom;
- if ( currentAtom != NULL ) {
- stab.atom = currentAtom;
- stab.string = symString;
- }
- else {
- warning("can't find atom for stabs FUN at %08llX in %s",
- (uint64_t)currentAtomAddress, path);
- }
- }
- else {
- // found ending stab, switch back to start state
- stab.string = symString;
- stab.atom = currentAtom;
- state = start;
- currentAtom = NULL;
- }
- break;
- case N_LBRAC:
- case N_RBRAC:
- case N_SLINE:
- // adjust value to be offset in atom
- stab.value -= currentAtomAddress;
- stab.atom = currentAtom;
- break;
- case N_SO:
- stab.string = symString;
- state = start;
- break;
- default:
- stab.atom = currentAtom;
- stab.string = symString;
- break;
- }
- break;
- }
- // add to list of stabs for this .o file
- if ( useStab )
- fStabs.push_back(stab);
- }
- }
- }
-
-#if 0
- // special case precompiled header .o file (which has no content) to have one empty atom
- if ( fAtoms.size() == 0 ) {
- int pathLen = strlen(path);
- if ( (pathLen > 6) && (strcmp(&path[pathLen-6], ".gch.o")==0) ) {
- ObjectFile::Atom* phony = new AnonymousAtom<A>(*this, (uint32_t)0);
- //phony->fSynthesizedName = ".gch.o";
- fAtoms.push_back(phony);
- }
- }
-#endif
-
- // sort all atoms by address
- std::sort(fAtoms.begin(), fAtoms.end(), BaseAtomSorter());
-
- // set ordinal and sort references in each atom
- uint32_t index = fOrdinalBase;
- for (std::vector<BaseAtom*>::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) {
- BaseAtom* atom = (BaseAtom*)(*it);
- atom->setOrdinal(index++);
- atom->sortReferences();
- }
-
-}
-
-
-template <>
-void Reader<ppc>::setCpuConstraint(uint32_t cpusubtype)
-{
- switch (cpusubtype) {
- case CPU_SUBTYPE_POWERPC_ALL:
- case CPU_SUBTYPE_POWERPC_750:
- case CPU_SUBTYPE_POWERPC_7400:
- case CPU_SUBTYPE_POWERPC_7450:
- case CPU_SUBTYPE_POWERPC_970:
- fCpuConstraint = cpusubtype;
- break;
- default:
- warning("unknown ppc subtype 0x%08X in %s, defaulting to ALL", cpusubtype, fPath);
- fCpuConstraint = CPU_SUBTYPE_POWERPC_ALL;
- break;
- }
-}
-
-template <>
-void Reader<arm>::setCpuConstraint(uint32_t cpusubtype)
-{
- switch (cpusubtype) {
- case CPU_SUBTYPE_ARM_ALL:
- case CPU_SUBTYPE_ARM_V4T:
- case CPU_SUBTYPE_ARM_V5TEJ:
- case CPU_SUBTYPE_ARM_V6:
- case CPU_SUBTYPE_ARM_XSCALE:
- case CPU_SUBTYPE_ARM_V7:
- fCpuConstraint = cpusubtype;
- break;
- default:
- warning("unknown arm subtype 0x%08X in %s, defaulting to ALL", cpusubtype, fPath);
- fCpuConstraint = CPU_SUBTYPE_ARM_ALL;
- break;
- }
-}
-
-template <typename A>
-void Reader<A>::setCpuConstraint(uint32_t cpusubtype)
-{
- // no cpu sub types for this architecture
-}
-
-template <>
-uint32_t Reader<ppc>::updateCpuConstraint(uint32_t previous)
-{
- switch ( previous ) {
- case CPU_SUBTYPE_POWERPC_ALL:
- return fCpuConstraint;
- break;
- case CPU_SUBTYPE_POWERPC_750:
- if ( fCpuConstraint == CPU_SUBTYPE_POWERPC_7400 ||
- fCpuConstraint == CPU_SUBTYPE_POWERPC_7450 ||
- fCpuConstraint == CPU_SUBTYPE_POWERPC_970 )
- return fCpuConstraint;
- break;
- case CPU_SUBTYPE_POWERPC_7400:
- case CPU_SUBTYPE_POWERPC_7450:
- if ( fCpuConstraint == CPU_SUBTYPE_POWERPC_970 )
- return fCpuConstraint;
- break;
- case CPU_SUBTYPE_POWERPC_970:
- // G5 can run everything
- break;
- default:
- throw "Unhandled PPC cpu subtype!";
- break;
- }
- return previous;
-}
-
-
-
-template <>
-uint32_t Reader<arm>::updateCpuConstraint(uint32_t previous)
-{
- switch (previous) {
- case CPU_SUBTYPE_ARM_ALL:
- return fCpuConstraint;
- break;
- case CPU_SUBTYPE_ARM_V5TEJ:
- // v6, v7, and xscale are more constrained than previous file (v5), so use it
- if ( (fCpuConstraint == CPU_SUBTYPE_ARM_V6)
- || (fCpuConstraint == CPU_SUBTYPE_ARM_V7)
- || (fCpuConstraint == CPU_SUBTYPE_ARM_XSCALE) )
- return fCpuConstraint;
- break;
- case CPU_SUBTYPE_ARM_V4T:
- // v5, v6, v7, and xscale are more constrained than previous file (v4t), so use it
- if ( (fCpuConstraint == CPU_SUBTYPE_ARM_V7)
- || (fCpuConstraint == CPU_SUBTYPE_ARM_V6)
- || (fCpuConstraint == CPU_SUBTYPE_ARM_V5TEJ)
- || (fCpuConstraint == CPU_SUBTYPE_ARM_XSCALE) )
- return fCpuConstraint;
- break;
- case CPU_SUBTYPE_ARM_V6:
- // v6 can run everything except xscale and v7
- if ( fCpuConstraint == CPU_SUBTYPE_ARM_XSCALE )
- throw "can't mix xscale and v6 code";
- if ( fCpuConstraint == CPU_SUBTYPE_ARM_V7 )
- return fCpuConstraint;
- break;
- case CPU_SUBTYPE_ARM_XSCALE:
- // xscale can run everything except v6 and v7
- if ( fCpuConstraint == CPU_SUBTYPE_ARM_V6 )
- throw "can't mix xscale and v6 code";
- if ( fCpuConstraint == CPU_SUBTYPE_ARM_V7 )
- throw "can't mix xscale and v7 code";
- break;
- case CPU_SUBTYPE_ARM_V7:
- // v7 can run everything except xscale
- if ( fCpuConstraint == CPU_SUBTYPE_ARM_XSCALE )
- throw "can't mix xscale and v7 code";
- break;
- default:
- throw "Unhandled ARM cpu subtype!";
- }
- return previous;
-}
-
-template <typename A>
-uint32_t Reader<A>::updateCpuConstraint(uint32_t current)
-{
- // no cpu sub types for this architecture
- return current;
-}
-
-template <typename A>
-void Reader<A>::addDtraceExtraInfos(uint32_t probeAddr, const char* providerName)
-{
- // for every ___dtrace_stability$* and ___dtrace_typedefs$* undefine with
- // a matching provider name, add a by-name kDtraceTypeReference at probe site
- const char* dollar = strchr(providerName, '$');
- if ( dollar != NULL ) {
- int providerNameLen = dollar-providerName+1;
- for ( std::vector<const char*>::iterator it = fDtraceProviderInfo.begin(); it != fDtraceProviderInfo.end(); ++it) {
- const char* typeDollar = strchr(*it, '$');
- if ( typeDollar != NULL ) {
- if ( strncmp(typeDollar+1, providerName, providerNameLen) == 0 ) {
- makeByNameReference(A::kDtraceTypeReference, probeAddr, *it, 0);
- }
- }
- }
- }
-}
-
-
-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)
-{
-}
-
-template <typename A>
-bool Reader<A>::getTranslationUnitSource(const char** dir, const char** name) const
-{
- if ( fDebugInfo == kDebugInfoDwarf ) {
- *dir = fDwarfTranslationUnitDir;
- *name = fDwarfTranslationUnitFile;
- return (fDwarfTranslationUnitFile != NULL);
- }
- return false;
-}
-
-template <typename A>
-BaseAtom* Reader<A>::findAtomByName(const char* name)
-{
- // first search the more important atoms
- for (typename AddrToAtomMap::iterator it=fAddrToAtom.begin(); it != fAddrToAtom.end(); it++) {
- const char* atomName = it->second->getName();
- if ( (atomName != NULL) && (strcmp(atomName, name) == 0) ) {
- return it->second;
- }
- }
- // try all atoms, because this might have been a tentative definition
- for (std::vector<BaseAtom*>::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) {
- BaseAtom* atom = (BaseAtom*)(*it);
- const char* atomName = atom->getName();
- if ( (atomName != NULL) && (strcmp(atomName, name) == 0) ) {
- return atom;
- }
- }
- return NULL;
-}
-
-template <typename A>
-Reference<A>* Reader<A>::makeReference(Kinds kind, pint_t atAddr, pint_t toAddr)
-{
- return new Reference<A>(kind, findAtomAndOffset(atAddr), findAtomAndOffset(toAddr));
-}
-
-template <typename A>
-Reference<A>* Reader<A>::makeReference(Kinds kind, pint_t atAddr, pint_t fromAddr, pint_t toAddr)
-{
- return new Reference<A>(kind, findAtomAndOffset(atAddr), findAtomAndOffset(fromAddr), findAtomAndOffset(toAddr));
-}
-
-template <typename A>
-Reference<A>* Reader<A>::makeReferenceWithToBase(Kinds kind, pint_t atAddr, pint_t toAddr, pint_t toBaseAddr)
-{
- return new Reference<A>(kind, findAtomAndOffset(atAddr), findAtomAndOffset(toBaseAddr, toAddr));
-}
-
-template <typename A>
-Reference<A>* Reader<A>::makeReferenceWithToBase(Kinds kind, pint_t atAddr, pint_t fromAddr, pint_t toAddr, pint_t toBaseAddr)
-{
- return new Reference<A>(kind, findAtomAndOffset(atAddr), findAtomAndOffset(fromAddr), findAtomAndOffset(toBaseAddr, toAddr));
-}
-
-template <typename A>
-Reference<A>* Reader<A>::makeByNameReference(Kinds kind, pint_t atAddr, const char* toName, uint32_t toOffset)
-{
- return new Reference<A>(kind, findAtomAndOffset(atAddr), toName, toOffset);
-}
-
-template <typename A>
-Reference<A>* Reader<A>::makeReferenceToEH(const char* ehName, pint_t ehAtomAddress, const macho_section<P>* ehSect)
-{
- // add a direct reference from function atom to its eh frame atom
- const uint8_t* ehContent = (const uint8_t*)(fHeader) + ehAtomAddress - ehSect->addr() + ehSect->offset();
- int32_t deltaMinus8 = P::getP(*(pint_t*)(&ehContent[8])); // offset 8 in eh info is delta to function
- pint_t funcAddr = ehAtomAddress + deltaMinus8 + 8;
- return makeReference(A::kGroupSubordinate, funcAddr, ehAtomAddress);
-}
-
-
-template <>
-Reference<x86_64>* Reader<x86_64>::makeByNameReference(Kinds kind, pint_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, pint_t atAddr, const macho_nlist<P>* toSymbol, pint_t toOffset)
-{
- // x86_64 uses external relocations everywhere, so external relocations do not imply by-name references
- // instead check scope of target
- const char* symbolName = &fStrings[toSymbol->n_strx()];
- if ( ((toSymbol->n_type() & N_TYPE) == N_SECT) && (((toSymbol->n_type() & N_EXT) == 0) || (symbolName[0] == 'L')) )
- 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), symbolName, 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) ) {
- pint_t funcAddr = fSymbols[reloc->r_symbolnum()].n_value();
- return makeReference(x86_64::kGroupSubordinate, funcAddr, ehAtomAddress);
- }
- }
- warning("can't find matching function for eh symbol %s", ehName);
- return NULL;
-}
-
-
-template <typename A>
-AtomAndOffset Reader<A>::findAtomAndOffset(pint_t addr)
-{
- // STL has no built-in for "find largest key that is same or less than"
- typename AddrToAtomMap::iterator it = fAddrToAtom.upper_bound(addr);
- // if no atoms up to this address return none found
- if ( it == fAddrToAtom.begin() )
- return AtomAndOffset(NULL);
- // otherwise upper_bound gets us next key, so we back up one
- --it;
- AtomAndOffset result;
- result.atom = it->second;
- result.offset = addr - it->first;
- //fprintf(stderr, "findAtomAndOffset(0x%0llX) ==> %s (0x%0llX -> 0x%0llX)\n",
- // (uint64_t)addr, result.atom->getDisplayName(), (uint64_t)it->first, it->first+result.atom->getSize());
- return result;
-}
-
-// "scattered" relocations enable you to offset into an atom past the end of it
-// baseAddr is the address of the target atom,
-// realAddr is the points into it
-template <typename A>
-AtomAndOffset Reader<A>::findAtomAndOffset(pint_t baseAddr, pint_t realAddr)
-{
- typename AddrToAtomMap::iterator it = fAddrToAtom.find(baseAddr);
- if ( it != fAddrToAtom.end() ) {
- AtomAndOffset result;
- result.atom = it->second;
- result.offset = realAddr - it->first;
- //fprintf(stderr, "findAtomAndOffset(0x%08X, 0x%08X) => %s + 0x%08X\n", baseAddr, realAddr, result.atom->getDisplayName(), result.offset);
- return result;
- }
- // getting here means we have a scattered relocation to an address without a label
- // we should never get here...
- // one case we do get here is because sometimes the compiler generates non-lazy pointers in the __data section
- return findAtomAndOffset(realAddr);
-}
-
-
-/* Skip over a LEB128 value (signed or unsigned). */
-static void
-skip_leb128 (const uint8_t ** offset, const uint8_t * end)
-{
- while (*offset != end && **offset >= 0x80)
- (*offset)++;
- if (*offset != end)
- (*offset)++;
-}
-
-/* Read a ULEB128 into a 64-bit word. Return (uint64_t)-1 on overflow
- or error. On overflow, skip past the rest of the uleb128. */
-static uint64_t
-read_uleb128 (const uint8_t ** offset, const uint8_t * end)
-{
- uint64_t result = 0;
- int bit = 0;
-
- do {
- uint64_t b;
-
- if (*offset == end)
- return (uint64_t) -1;
-
- b = **offset & 0x7f;
-
- if (bit >= 64 || b << bit >> bit != b)
- result = (uint64_t) -1;
- else
- result |= b << bit, bit += 7;
- } while (*(*offset)++ >= 0x80);
- return result;
-}
-
-
-/* Skip over a DWARF attribute of form FORM. */
-template <typename A>
-bool Reader<A>::skip_form(const uint8_t ** offset, const uint8_t * end, uint64_t form,
- uint8_t addr_size, bool dwarf64)
-{
- int64_t sz=0;
-
- switch (form)
- {
- case DW_FORM_addr:
- sz = addr_size;
- break;
-
- case DW_FORM_block2:
- if (end - *offset < 2)
- return false;
- sz = 2 + A::P::E::get16(*(uint16_t*)offset);
- break;
-
- case DW_FORM_block4:
- if (end - *offset < 4)
- return false;
- sz = 2 + A::P::E::get32(*(uint32_t*)offset);
- break;
-
- case DW_FORM_data2:
- case DW_FORM_ref2:
- sz = 2;
- break;
-
- case DW_FORM_data4:
- case DW_FORM_ref4:
- sz = 4;
- break;
-
- case DW_FORM_data8:
- case DW_FORM_ref8:
- sz = 8;
- break;
-
- case DW_FORM_string:
- while (*offset != end && **offset)
- ++*offset;
- case DW_FORM_data1:
- case DW_FORM_flag:
- case DW_FORM_ref1:
- sz = 1;
- break;
-
- case DW_FORM_block:
- sz = read_uleb128 (offset, end);
- break;
-
- case DW_FORM_block1:
- if (*offset == end)
- return false;
- sz = 1 + **offset;
- break;
-
- case DW_FORM_sdata:
- case DW_FORM_udata:
- case DW_FORM_ref_udata:
- skip_leb128 (offset, end);
- return true;
-
- case DW_FORM_strp:
- case DW_FORM_ref_addr:
- sz = dwarf64 ? 8 : 4;
- break;
-
- default:
- return false;
- }
- if (end - *offset < sz)
- return false;
- *offset += sz;
- return true;
-}
-
-// Look at the compilation unit DIE and determine
-// its NAME, compilation directory (in COMP_DIR) and its
-// line number information offset (in STMT_LIST). NAME and COMP_DIR
-// may be NULL (especially COMP_DIR) if they are not in the .o file;
-// STMT_LIST will be (uint64_t) -1.
-//
-// At present this assumes that there's only one compilation unit DIE.
-//
-template <typename A>
-bool Reader<A>::read_comp_unit(const char ** name, const char ** comp_dir,
- uint64_t *stmt_list)
-{
- const uint8_t * debug_info;
- const uint8_t * debug_abbrev;
- const uint8_t * di;
- const uint8_t * da;
- const uint8_t * end;
- const uint8_t * enda;
- uint64_t sz;
- uint16_t vers;
- uint64_t abbrev_base;
- uint64_t abbrev;
- uint8_t address_size;
- bool dwarf64;
-
- *name = NULL;
- *comp_dir = NULL;
- *stmt_list = (uint64_t) -1;
-
- if ( (fDwarfDebugInfoSect == NULL) || (fDwarfDebugAbbrevSect == NULL) )
- return false;
-
- debug_info = (uint8_t*)(fHeader) + fDwarfDebugInfoSect->offset();
- debug_abbrev = (uint8_t*)(fHeader) + fDwarfDebugAbbrevSect->offset();
- di = debug_info;
-
- if (fDwarfDebugInfoSect->size() < 12)
- /* Too small to be a real debug_info section. */
- return false;
- sz = A::P::E::get32(*(uint32_t*)di);
- di += 4;
- dwarf64 = sz == 0xffffffff;
- if (dwarf64)
- sz = A::P::E::get64(*(uint64_t*)di), di += 8;
- else if (sz > 0xffffff00)
- /* Unknown dwarf format. */
- return false;
-
- /* Verify claimed size. */
- if (sz + (di - debug_info) > fDwarfDebugInfoSect->size() || sz <= (dwarf64 ? 23 : 11))
- return false;
-
- vers = A::P::E::get16(*(uint16_t*)di);
- if (vers < 2 || vers > 3)
- /* DWARF version wrong for this code.
- Chances are we could continue anyway, but we don't know for sure. */
- return false;
- di += 2;
-
- /* Find the debug_abbrev section. */
- abbrev_base = dwarf64 ? A::P::E::get64(*(uint64_t*)di) : A::P::E::get32(*(uint32_t*)di);
- di += dwarf64 ? 8 : 4;
-
- if (abbrev_base > fDwarfDebugAbbrevSect->size())
- return false;
- da = debug_abbrev + abbrev_base;
- enda = debug_abbrev + fDwarfDebugAbbrevSect->size();
-
- address_size = *di++;
-
- /* Find the abbrev number we're looking for. */
- end = di + sz;
- abbrev = read_uleb128 (&di, end);
- if (abbrev == (uint64_t) -1)
- return false;
-
- /* Skip through the debug_abbrev section looking for that abbrev. */
- for (;;)
- {
- uint64_t this_abbrev = read_uleb128 (&da, enda);
- uint64_t attr;
-
- if (this_abbrev == abbrev)
- /* This is almost always taken. */
- break;
- skip_leb128 (&da, enda); /* Skip the tag. */
- if (da == enda)
- return false;
- da++; /* Skip the DW_CHILDREN_* value. */
-
- do {
- attr = read_uleb128 (&da, enda);
- skip_leb128 (&da, enda);
- } while (attr != 0 && attr != (uint64_t) -1);
- if (attr != 0)
- return false;
- }
-
- /* Check that the abbrev is one for a DW_TAG_compile_unit. */
- if (read_uleb128 (&da, enda) != DW_TAG_compile_unit)
- return false;
- if (da == enda)
- return false;
- da++; /* Skip the DW_CHILDREN_* value. */
-
- /* Now, go through the DIE looking for DW_AT_name,
- DW_AT_comp_dir, and DW_AT_stmt_list. */
- for (;;)
- {
- uint64_t attr = read_uleb128 (&da, enda);
- uint64_t form = read_uleb128 (&da, enda);
-
- if (attr == (uint64_t) -1)
- return false;
- else if (attr == 0)
- return true;
-
- if (form == DW_FORM_indirect)
- form = read_uleb128 (&di, end);
-
- if (attr == DW_AT_name && form == DW_FORM_string)
- *name = (const char *) di;
- else if (attr == DW_AT_comp_dir && form == DW_FORM_string)
- *comp_dir = (const char *) di;
- /* Really we should support DW_FORM_strp here, too, but
- there's usually no reason for the producer to use that form
- for the DW_AT_name and DW_AT_comp_dir attributes. */
- else if (attr == DW_AT_stmt_list && form == DW_FORM_data4)
- *stmt_list = A::P::E::get32(*(uint32_t*)di);
- else if (attr == DW_AT_stmt_list && form == DW_FORM_data8)
- *stmt_list = A::P::E::get64(*(uint64_t*)di);
- if (! skip_form (&di, end, form, address_size, dwarf64))
- return false;
- }
-}
-
-template <typename A>
-const char* Reader<A>::assureFullPath(const char* path)
-{
- if ( path[0] == '/' )
- return path;
- char cwdbuff[MAXPATHLEN];
- if ( getcwd(cwdbuff, MAXPATHLEN) != NULL ) {
- char* result;
- asprintf(&result, "%s/%s", cwdbuff, path);
- if ( result != NULL )
- return result;
- }
- return path;
-}
-
-
-//
-//
-// To implement architecture xxx, you must write template specializations for the following six methods:
-// Reader<xxx>::validFile()
-// Reader<xxx>::addRelocReference()
-// Reference<xxx>::getDescription()
-//
-//
-
-
-template <>
-bool Reader<ppc>::validFile(const uint8_t* fileContent)
-{
- 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_OBJECT )
- return false;
- return true;
-}
-
-template <>
-bool Reader<ppc64>::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_POWERPC64 )
- return false;
- if ( header->filetype() != MH_OBJECT )
- return false;
- return true;
-}
-
-template <>
-bool Reader<x86>::validFile(const uint8_t* fileContent)
-{
- 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_OBJECT )
- return false;
- 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 <>
-bool Reader<arm>::validFile(const uint8_t* fileContent)
-{
- const macho_header<P>* header = (const macho_header<P>*)fileContent;
- if ( header->magic() != MH_MAGIC )
- return false;
- if ( header->cputype() != CPU_TYPE_ARM )
- return false;
- if ( header->filetype() != MH_OBJECT )
- return false;
- return true;
-}
-
-template <typename A>
-bool Reader<A>::isWeakImportSymbol(const macho_nlist<P>* sym)
-{
- return ( ((sym->n_type() & N_TYPE) == N_UNDF) && ((sym->n_desc() & N_WEAK_REF) != 0) );
-}
-
-template <>
-bool Reader<ppc64>::addRelocReference(const macho_section<ppc64::P>* sect, const macho_relocation_info<ppc64::P>* reloc)
-{
- return addRelocReference_powerpc(sect, reloc);
-}
-
-template <>
-bool Reader<ppc>::addRelocReference(const macho_section<ppc::P>* sect, const macho_relocation_info<ppc::P>* reloc)
-{
- return addRelocReference_powerpc(sect, reloc);
-}
-
-
-//
-// ppc and ppc64 both use the same relocations, so process them in one common routine
-//
-template <typename A>
-bool Reader<A>::addRelocReference_powerpc(const macho_section<typename A::P>* sect,
- const macho_relocation_info<typename A::P>* reloc)
-{
- uint32_t srcAddr;
- uint32_t dstAddr;
- uint32_t* fixUpPtr;
- int32_t displacement = 0;
- uint32_t instruction = 0;
- uint32_t offsetInTarget;
- int16_t lowBits;
- bool result = false;
- if ( (reloc->r_address() & R_SCATTERED) == 0 ) {
- const macho_relocation_info<P>* nextReloc = &reloc[1];
- const char* targetName = NULL;
- bool weakImport = false;
- fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address());
- if ( reloc->r_type() != PPC_RELOC_PAIR )
- instruction = BigEndian::get32(*fixUpPtr);
- srcAddr = sect->addr() + reloc->r_address();
- if ( reloc->r_extern() ) {
- const macho_nlist<P>* targetSymbol = &fSymbols[reloc->r_symbolnum()];
- targetName = &fStrings[targetSymbol->n_strx()];
- weakImport = this->isWeakImportSymbol(targetSymbol);
- }
- switch ( reloc->r_type() ) {
- case PPC_RELOC_BR24:
- {
- if ( (instruction & 0x4C000000) == 0x48000000 ) {
- displacement = (instruction & 0x03FFFFFC);
- if ( (displacement & 0x02000000) != 0 )
- displacement |= 0xFC000000;
- }
- else {
- printf("bad instruction for BR24 reloc");
- }
- if ( reloc->r_extern() ) {
- offsetInTarget = srcAddr + displacement;
- if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) {
- makeByNameReference(A::kDtraceProbeSite, srcAddr, targetName, 0);
- addDtraceExtraInfos(srcAddr, &targetName[16]);
- }
- else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) {
- makeByNameReference(A::kDtraceIsEnabledSite, srcAddr, targetName, 0);
- addDtraceExtraInfos(srcAddr, &targetName[20]);
- }
- else if ( weakImport )
- makeByNameReference(A::kBranch24WeakImport, srcAddr, targetName, offsetInTarget);
- else
- makeByNameReference(A::kBranch24, srcAddr, targetName, offsetInTarget);
- }
- else {
- dstAddr = srcAddr + displacement;
- // if this is a branch to a stub, we need to see if the stub is for a weak imported symbol
- ObjectFile::Atom* atom = findAtomAndOffset(dstAddr).atom;
- targetName = atom->getName();
- if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_probe$", 16) == 0) ) {
- makeByNameReference(A::kDtraceProbeSite, srcAddr, targetName, 0);
- addDtraceExtraInfos(srcAddr, &targetName[16]);
- }
- else if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_isenabled$", 20) == 0) ) {
- makeByNameReference(A::kDtraceIsEnabledSite, srcAddr, targetName, 0);
- addDtraceExtraInfos(srcAddr, &targetName[20]);
- }
- else if ( (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn)
- && ((AnonymousAtom<A>*)atom)->isWeakImportStub() )
- makeReference(A::kBranch24WeakImport, srcAddr, dstAddr);
- else
- makeReference(A::kBranch24, srcAddr, dstAddr);
- }
- }
- break;
- case PPC_RELOC_BR14:
- {
- displacement = (instruction & 0x0000FFFC);
- if ( (displacement & 0x00008000) != 0 )
- displacement |= 0xFFFF0000;
- if ( reloc->r_extern() ) {
- offsetInTarget = srcAddr + displacement;
- makeByNameReference(A::kBranch14, srcAddr, targetName, offsetInTarget);
- }
- else {
- dstAddr = srcAddr + displacement;
- makeReference(A::kBranch14, srcAddr, dstAddr);
- }
- }
- break;
- case PPC_RELOC_PAIR:
- // skip, processed by a previous look ahead
- break;
- case PPC_RELOC_LO16:
- {
- if ( nextReloc->r_type() != PPC_RELOC_PAIR ) {
- warning("PPC_RELOC_LO16 missing following pair");
- break;
- }
- result = true;
- lowBits = (instruction & 0xFFFF);
- if ( reloc->r_extern() ) {
- offsetInTarget = (nextReloc->r_address() << 16) | ((uint32_t)lowBits & 0x0000FFFF);
- makeByNameReference(A::kAbsLow16, srcAddr, targetName, offsetInTarget);
- }
- else {
- dstAddr = (nextReloc->r_address() << 16) + ((uint32_t)lowBits & 0x0000FFFF);
- if ( reloc->r_symbolnum() == R_ABS ) {
- // find absolute symbol that corresponds to pointerValue
- typename AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr);
- if ( pos != fAddrToAbsoluteAtom.end() )
- makeByNameReference(A::kAbsLow16, srcAddr, pos->second->getName(), 0);
- else
- makeReference(A::kAbsLow16, srcAddr, dstAddr);
- }
- else {
- makeReference(A::kAbsLow16, srcAddr, dstAddr);
- }
- }
- }
- break;
- case PPC_RELOC_LO14:
- {
- if ( nextReloc->r_type() != PPC_RELOC_PAIR ) {
- warning("PPC_RELOC_LO14 missing following pair");
- break;
- }
- result = true;
- lowBits = (instruction & 0xFFFC);
- if ( reloc->r_extern() ) {
- offsetInTarget = (nextReloc->r_address() << 16) | ((uint32_t)lowBits & 0x0000FFFF);
- makeByNameReference(A::kAbsLow14, srcAddr, targetName, offsetInTarget);
- }
- else {
- dstAddr = (nextReloc->r_address() << 16) | ((uint32_t)lowBits & 0x0000FFFF);
- if ( reloc->r_symbolnum() == R_ABS ) {
- // find absolute symbol that corresponds to pointerValue
- typename AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr);
- if ( pos != fAddrToAbsoluteAtom.end() )
- makeByNameReference(A::kAbsLow14, srcAddr, pos->second->getName(), 0);
- else
- makeReference(A::kAbsLow14, srcAddr, dstAddr);
- }
- else {
- makeReference(A::kAbsLow14, srcAddr, dstAddr);
- }
- }
- }
- break;
- case PPC_RELOC_HI16:
- {
- if ( nextReloc->r_type() != PPC_RELOC_PAIR ) {
- warning("PPC_RELOC_HI16 missing following pair");
- break;
- }
- result = true;
- if ( reloc->r_extern() ) {
- offsetInTarget = ((instruction & 0x0000FFFF) << 16) | (nextReloc->r_address() & 0x0000FFFF);
- makeByNameReference(A::kAbsHigh16, srcAddr, targetName, offsetInTarget);
- }
- else {
- dstAddr = ((instruction & 0x0000FFFF) << 16) | (nextReloc->r_address() & 0x0000FFFF);
- if ( reloc->r_symbolnum() == R_ABS ) {
- // find absolute symbol that corresponds to pointerValue
- typename AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr);
- if ( pos != fAddrToAbsoluteAtom.end() )
- makeByNameReference(A::kAbsHigh16, srcAddr, pos->second->getName(), 0);
- else
- makeReference(A::kAbsHigh16, srcAddr, dstAddr);
- }
- else {
- makeReference(A::kAbsHigh16, srcAddr, dstAddr);
- }
- }
- }
- break;
- case PPC_RELOC_HA16:
- {
- if ( nextReloc->r_type() != PPC_RELOC_PAIR ) {
- warning("PPC_RELOC_HA16 missing following pair");
- break;
- }
- result = true;
- lowBits = (nextReloc->r_address() & 0x0000FFFF);
- if ( reloc->r_extern() ) {
- offsetInTarget = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits;
- makeByNameReference(A::kAbsHigh16AddLow, srcAddr, targetName, offsetInTarget);
- }
- else {
- dstAddr = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits;
- if ( reloc->r_symbolnum() == R_ABS ) {
- // find absolute symbol that corresponds to pointerValue
- typename AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr);
- if ( pos != fAddrToAbsoluteAtom.end() )
- makeByNameReference(A::kAbsHigh16AddLow, srcAddr, pos->second->getName(), 0);
- else
- makeReference(A::kAbsHigh16AddLow, srcAddr, dstAddr);
- }
- else {
- makeReference(A::kAbsHigh16AddLow, srcAddr, dstAddr);
- }
- }
- }
- break;
- case PPC_RELOC_VANILLA:
- {
- pint_t pointerValue = P::getP(*((pint_t*)fixUpPtr));
- if ( reloc->r_extern() ) {
- if ( weakImport )
- makeByNameReference(A::kPointerWeakImport, srcAddr, targetName, pointerValue);
- else
- makeByNameReference(A::kPointer, srcAddr, targetName, pointerValue);
- }
- else {
- makeReference(A::kPointer, srcAddr, pointerValue);
- }
- }
- break;
- case PPC_RELOC_JBSR:
- // this is from -mlong-branch codegen. We ignore the jump island and make reference to the real target
- if ( nextReloc->r_type() != PPC_RELOC_PAIR ) {
- warning("PPC_RELOC_JBSR missing following pair");
- break;
- }
- fHasLongBranchStubs = true;
- result = true;
- makeReference(A::kBranch24, srcAddr, nextReloc->r_address());
- if ( (instruction & 0x4C000000) == 0x48000000 ) {
- displacement = (instruction & 0x03FFFFFC);
- if ( (displacement & 0x02000000) != 0 )
- displacement |= 0xFC000000;
- }
- else {
- fprintf(stderr, "bad instruction for BR24 reloc");
- }
- if ( reloc->r_extern() ) {
- fprintf(stderr, "PPC_RELOC_JBSR should not be using an external relocation");
- }
- break;
- default:
- warning("unknown relocation type %d", reloc->r_type());
- }
- }
- else {
- const macho_scattered_relocation_info<P>* sreloc = (macho_scattered_relocation_info<P>*)reloc;
- srcAddr = sect->addr() + sreloc->r_address();
- dstAddr = sreloc->r_value();
- uint32_t betterDstAddr;
- fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + sreloc->r_address());
- const macho_scattered_relocation_info<P>* nextSReloc = &sreloc[1];
- const macho_relocation_info<P>* nextReloc = &reloc[1];
- // file format allows pair to be scattered or not
- bool nextRelocIsPair = false;
- uint32_t nextRelocAddress = 0;
- uint32_t nextRelocValue = 0;
- if ( (nextReloc->r_address() & R_SCATTERED) == 0 ) {
- if ( nextReloc->r_type() == PPC_RELOC_PAIR ) {
- nextRelocIsPair = true;
- nextRelocAddress = nextReloc->r_address();
- result = true;
- }
- }
- else {
- if ( nextSReloc->r_type() == PPC_RELOC_PAIR ) {
- nextRelocIsPair = true;
- nextRelocAddress = nextSReloc->r_address();
- nextRelocValue = nextSReloc->r_value();
- result = true;
- }
- }
- switch (sreloc->r_type()) {
- case PPC_RELOC_VANILLA:
- {
- betterDstAddr = P::getP(*(pint_t*)fixUpPtr);
- //fprintf(stderr, "scattered pointer reloc: srcAddr=0x%08X, dstAddr=0x%08X, pointer=0x%08X\n", srcAddr, dstAddr, betterDstAddr);
- // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr)
- makeReferenceWithToBase(A::kPointer, srcAddr, betterDstAddr, dstAddr);
- }
- break;
- case PPC_RELOC_BR14:
- {
- instruction = BigEndian::get32(*fixUpPtr);
- displacement = (instruction & 0x0000FFFC);
- if ( (displacement & 0x00008000) != 0 )
- displacement |= 0xFFFF0000;
- betterDstAddr = srcAddr+displacement;
- //fprintf(stderr, "betterDstAddr=0x%08X, srcAddr=0x%08X, displacement=0x%08X\n", betterDstAddr, srcAddr, displacement);
- makeReferenceWithToBase(A::kBranch14, srcAddr, betterDstAddr, dstAddr);
- }
- break;
- case PPC_RELOC_BR24:
- {
- instruction = BigEndian::get32(*fixUpPtr);
- if ( (instruction & 0x4C000000) == 0x48000000 ) {
- displacement = (instruction & 0x03FFFFFC);
- if ( (displacement & 0x02000000) != 0 )
- displacement |= 0xFC000000;
- betterDstAddr = srcAddr+displacement;
- makeReferenceWithToBase(A::kBranch24, srcAddr, betterDstAddr, dstAddr);
- }
- }
- break;
- case PPC_RELOC_LO16_SECTDIFF:
- {
- if ( ! nextRelocIsPair ) {
- warning("PPC_RELOC_LO16_SECTDIFF missing following PAIR");
- break;
- }
- instruction = BigEndian::get32(*fixUpPtr);
- lowBits = (instruction & 0xFFFF);
- displacement = (nextRelocAddress << 16) | ((uint32_t)lowBits & 0x0000FFFF);
- makeReferenceWithToBase(A::kPICBaseLow16, srcAddr, nextRelocValue, nextRelocValue + displacement, dstAddr);
- }
- break;
- case PPC_RELOC_LO14_SECTDIFF:
- {
- if ( ! nextRelocIsPair ) {
- warning("PPC_RELOC_LO14_SECTDIFF missing following PAIR");
- break;
- }
- instruction = BigEndian::get32(*fixUpPtr);
- lowBits = (instruction & 0xFFFC);
- displacement = (nextRelocAddress << 16) | ((uint32_t)lowBits & 0x0000FFFF);
- makeReferenceWithToBase(A::kPICBaseLow14, srcAddr, nextRelocValue, nextRelocValue + displacement, dstAddr);
- }
- break;
- case PPC_RELOC_HA16_SECTDIFF:
- {
- if ( ! nextRelocIsPair ) {
- warning("PPC_RELOC_HA16_SECTDIFF missing following PAIR");
- break;
- }
- instruction = BigEndian::get32(*fixUpPtr);
- lowBits = (nextRelocAddress & 0x0000FFFF);
- displacement = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits;
- makeReferenceWithToBase(A::kPICBaseHigh16, srcAddr, nextRelocValue, nextRelocValue + displacement, dstAddr);
- }
- break;
- case PPC_RELOC_LO14:
- {
- if ( ! nextRelocIsPair ) {
- warning("PPC_RELOC_LO14 missing following PAIR");
- break;
- }
- instruction = BigEndian::get32(*fixUpPtr);
- lowBits = (instruction & 0xFFFC);
- betterDstAddr = (nextRelocAddress << 16) + ((uint32_t)lowBits & 0x0000FFFF);
- makeReferenceWithToBase(A::kAbsLow14, srcAddr, betterDstAddr, dstAddr);
- }
- break;
- case PPC_RELOC_LO16:
- {
- if ( ! nextRelocIsPair ) {
- warning("PPC_RELOC_LO16 missing following PAIR");
- break;
- }
- instruction = BigEndian::get32(*fixUpPtr);
- lowBits = (instruction & 0xFFFF);
- betterDstAddr = (nextRelocAddress << 16) + ((uint32_t)lowBits & 0x0000FFFF);
- makeReferenceWithToBase(A::kAbsLow16, srcAddr, betterDstAddr, dstAddr);
- }
- break;
- case PPC_RELOC_HA16:
- {
- if ( ! nextRelocIsPair ) {
- warning("PPC_RELOC_HA16 missing following PAIR");
- break;
- }
- instruction = BigEndian::get32(*fixUpPtr);
- lowBits = (nextRelocAddress & 0xFFFF);
- betterDstAddr = ((instruction & 0xFFFF) << 16) + (int32_t)lowBits;
- makeReferenceWithToBase(A::kAbsHigh16AddLow, srcAddr, betterDstAddr, dstAddr);
- }
- break;
- case PPC_RELOC_HI16:
- {
- if ( ! nextRelocIsPair ) {
- warning("PPC_RELOC_HI16 missing following PAIR");
- break;
- }
- instruction = BigEndian::get32(*fixUpPtr);
- lowBits = (nextRelocAddress & 0xFFFF);
- betterDstAddr = ((instruction & 0xFFFF) << 16) | (lowBits & 0x0000FFFF);
- makeReferenceWithToBase(A::kAbsHigh16, srcAddr, betterDstAddr, dstAddr);
- }
- break;
- case PPC_RELOC_SECTDIFF:
- case PPC_RELOC_LOCAL_SECTDIFF:
- {
- if ( ! nextRelocIsPair ) {
- warning("PPC_RELOC_SECTDIFF missing following pair");
- break;
- }
- Kinds kind = A::kPointerDiff32;;
- uint32_t contentAddr = 0;
- switch ( sreloc->r_length() ) {
- case 0:
- throw "bad diff relocations r_length (0) for ppc architecture";
- case 1:
- kind = A::kPointerDiff16;
- contentAddr = BigEndian::get16(*((uint16_t*)fixUpPtr));
- break;
- case 2:
- kind = A::kPointerDiff32;
- contentAddr = BigEndian::get32(*fixUpPtr);
- break;
- case 3:
- kind = A::kPointerDiff64;
- contentAddr = BigEndian::get64(*((uint64_t*)fixUpPtr));
- break;
- }
- AtomAndOffset srcao = findAtomAndOffset(srcAddr);
- AtomAndOffset fromao = findAtomAndOffset(nextRelocValue);
- AtomAndOffset toao = findAtomAndOffset(dstAddr);
- // check for addend encoded in the section content
- //fprintf(stderr, "addRef: dstAddr=0x%X, nextRelocValue=0x%X, contentAddr=0x%X\n",
- // dstAddr, nextRelocValue, contentAddr);
- if ( (dstAddr - nextRelocValue) != contentAddr ) {
- if ( toao.atom == srcao.atom )
- toao.offset += (contentAddr + nextRelocValue) - dstAddr;
- else if ( fromao.atom == srcao.atom )
- toao.offset += (contentAddr + nextRelocValue) - dstAddr;
- else
- fromao.offset += (dstAddr - contentAddr) - nextRelocValue;
- }
- //fprintf(stderr, "addRef: src=%s+0x%X, from=%s+0x%X, to=%s+0x%X\n",
- // srcao.atom->getDisplayName(), srcao.offset,
- // fromao.atom->getDisplayName(), fromao.offset,
- // toao.atom->getDisplayName(), toao.offset);
- new Reference<A>(kind, srcao, fromao, toao);
- }
- break;
- case PPC_RELOC_PAIR:
- break;
- case PPC_RELOC_HI16_SECTDIFF:
- warning("unexpected scattered relocation type PPC_RELOC_HI16_SECTDIFF");
- break;
- default:
- warning("unknown scattered relocation type %d", sreloc->r_type());
- }
- }
- return result;
-}
-
-
-template <>
-bool Reader<x86>::addRelocReference(const macho_section<x86::P>* sect, const macho_relocation_info<x86::P>* reloc)
-{
- uint32_t srcAddr;
- uint32_t dstAddr;
- uint32_t* fixUpPtr;
- bool result = false;
- if ( (reloc->r_address() & R_SCATTERED) == 0 ) {
- srcAddr = sect->addr() + reloc->r_address();
- fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address());
- switch ( reloc->r_type() ) {
- case GENERIC_RELOC_VANILLA:
- {
- x86::ReferenceKinds kind = x86::kPointer;
- uint32_t pointerValue = E::get32(*fixUpPtr);
- if ( reloc->r_pcrel() ) {
- switch( reloc->r_length() ) {
- case 0:
- kind = x86::kPCRel8;
- pointerValue = srcAddr + *((int8_t*)fixUpPtr) + sizeof(int8_t);
- break;
- case 1:
- kind = x86::kPCRel16;
- pointerValue = srcAddr + (int16_t)E::get16(*((uint16_t*)fixUpPtr)) + sizeof(uint16_t);
- break;
- case 2:
- kind = x86::kPCRel32;
- pointerValue += srcAddr + sizeof(uint32_t);
- break;
- case 3:
- throw "bad pc-rel vanilla relocation length";
- }
- }
- else if ( strcmp(sect->segname(), "__TEXT") == 0 ) {
- kind = x86::kAbsolute32;
- if ( reloc->r_length() != 2 )
- throw "bad vanilla relocation length";
- }
- else {
- kind = x86::kPointer;
- if ( reloc->r_length() != 2 )
- throw "bad vanilla relocation length";
- }
- if ( reloc->r_extern() ) {
- const macho_nlist<P>* targetSymbol = &fSymbols[reloc->r_symbolnum()];
- if ( this->isWeakImportSymbol(targetSymbol) ) {
- if ( reloc->r_pcrel() )
- kind = x86::kPCRel32WeakImport;
- else
- kind = x86::kPointerWeakImport;
- }
- const char* targetName = &fStrings[targetSymbol->n_strx()];
- if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) {
- makeByNameReference(x86::kDtraceProbeSite, srcAddr, targetName, 0);
- addDtraceExtraInfos(srcAddr, &targetName[16]);
- }
- else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) {
- makeByNameReference(x86::kDtraceIsEnabledSite, srcAddr, targetName, 0);
- addDtraceExtraInfos(srcAddr, &targetName[20]);
- }
- else
- makeByNameReference(kind, srcAddr, targetName, pointerValue);
- }
- else {
- // if this is a branch to a stub, we need to see if the stub is for a weak imported symbol
- ObjectFile::Atom* atom = findAtomAndOffset(pointerValue).atom;
- const char* targetName = atom->getName();
- if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_probe$", 16) == 0) ) {
- makeByNameReference(x86::kDtraceProbeSite, srcAddr, targetName, 0);
- addDtraceExtraInfos(srcAddr, &targetName[16]);
- }
- else if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_isenabled$", 20) == 0) ) {
- makeByNameReference(x86::kDtraceIsEnabledSite, srcAddr, targetName, 0);
- addDtraceExtraInfos(srcAddr, &targetName[20]);
- }
- else if ( reloc->r_pcrel() && (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn)
- && ((AnonymousAtom<x86>*)atom)->isWeakImportStub() )
- makeReference(x86::kPCRel32WeakImport, srcAddr, pointerValue);
- else if ( reloc->r_symbolnum() != R_ABS )
- makeReference(kind, srcAddr, pointerValue);
- else {
- // find absolute symbol that corresponds to pointerValue
- AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(pointerValue);
- if ( pos != fAddrToAbsoluteAtom.end() )
- makeByNameReference(kind, srcAddr, pos->second->getName(), 0);
- else
- throwf("R_ABS reloc but no absolute symbol at target address");
- }
- }
- }
- break;
- default:
- warning("unknown relocation type %d", reloc->r_type());
- }
- }
- else {
- const macho_scattered_relocation_info<P>* sreloc = (macho_scattered_relocation_info<P>*)reloc;
- srcAddr = sect->addr() + sreloc->r_address();
- dstAddr = sreloc->r_value();
- fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + sreloc->r_address());
- const macho_scattered_relocation_info<P>* nextSReloc = &sreloc[1];
- const macho_relocation_info<P>* nextReloc = &reloc[1];
- pint_t betterDstAddr;
- // file format allows pair to be scattered or not
- bool nextRelocIsPair = false;
- uint32_t nextRelocAddress = 0;
- uint32_t nextRelocValue = 0;
- if ( (nextReloc->r_address() & R_SCATTERED) == 0 ) {
- if ( nextReloc->r_type() == GENERIC_RELOC_PAIR ) {
- nextRelocIsPair = true;
- nextRelocAddress = nextReloc->r_address();
- result = true;
- }
- }
- else {
- if ( nextSReloc->r_type() == GENERIC_RELOC_PAIR ) {
- nextRelocIsPair = true;
- nextRelocAddress = nextSReloc->r_address();
- nextRelocValue = nextSReloc->r_value();
- }
- }
- switch (sreloc->r_type()) {
- case GENERIC_RELOC_VANILLA:
- betterDstAddr = LittleEndian::get32(*fixUpPtr);
- //fprintf(stderr, "pointer reloc: srcAddr=0x%08X, dstAddr=0x%08X, pointer=0x%08lX\n", srcAddr, dstAddr, betterDstAddr);
- // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr)
- if ( sreloc->r_pcrel() ) {
- betterDstAddr += srcAddr + 4;
- makeReferenceWithToBase(x86::kPCRel32, srcAddr, betterDstAddr, dstAddr);
- }
- else {
- if ( strcmp(sect->segname(), "__TEXT") == 0 )
- makeReferenceWithToBase(x86::kAbsolute32, srcAddr, betterDstAddr, dstAddr);
- else
- makeReferenceWithToBase(x86::kPointer, srcAddr, betterDstAddr, dstAddr);
- }
- break;
- case GENERIC_RELOC_SECTDIFF:
- case GENERIC_RELOC_LOCAL_SECTDIFF:
- {
- if ( !nextRelocIsPair ) {
- warning("GENERIC_RELOC_SECTDIFF missing following pair");
- break;
- }
- x86::ReferenceKinds kind = x86::kPointerDiff;
- uint32_t contentAddr = 0;
- switch ( sreloc->r_length() ) {
- case 0:
- case 3:
- throw "bad length for GENERIC_RELOC_SECTDIFF";
- case 1:
- kind = x86::kPointerDiff16;
- contentAddr = LittleEndian::get16(*((uint16_t*)fixUpPtr));
- break;
- case 2:
- kind = x86::kPointerDiff;
- contentAddr = LittleEndian::get32(*fixUpPtr);
- break;
- }
- AtomAndOffset srcao = findAtomAndOffset(srcAddr);
- AtomAndOffset fromao = findAtomAndOffset(nextRelocValue);
- AtomAndOffset toao = findAtomAndOffset(dstAddr);
- // check for addend encoded in the section content
- //fprintf(stderr, "addRef: dstAddr=0x%X, nextRelocValue=0x%X, contentAddr=0x%X\n",
- // dstAddr, nextRelocValue, contentAddr);
- if ( (dstAddr - nextRelocValue) != contentAddr ) {
- if ( toao.atom == srcao.atom )
- toao.offset += (contentAddr + nextRelocValue) - dstAddr;
- else if ( fromao.atom == srcao.atom )
- toao.offset += (contentAddr + nextRelocValue) - dstAddr;
- else
- fromao.offset += (dstAddr - contentAddr) - nextRelocValue;
- }
- //fprintf(stderr, "addRef: src=%s+0x%X, from=%s+0x%X, to=%s+0x%X\n",
- // srcao.atom->getDisplayName(), srcao.offset,
- // fromao.atom->getDisplayName(), fromao.offset,
- // toao.atom->getDisplayName(), toao.offset);
- new Reference<x86>(kind, srcao, fromao, toao);
- }
- break;
- case GENERIC_RELOC_PAIR:
- // do nothing, already used via a look ahead
- break;
- default:
- warning("unknown scattered relocation type %d", sreloc->r_type());
- }
- }
- 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 = x86_64::kNoFixUp;
- 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);
- // verify that dstAddr is in the section being targeted
- int sectNum = reloc->r_symbolnum();
- const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)fSegment + sizeof(macho_segment_command<P>));
- const macho_section<P>* const targetSection = §ionsStart[sectNum-1];
- if ( (dstAddr < targetSection->addr()) || (dstAddr > (targetSection->addr()+targetSection->size())) ) {
- throwf("local relocation for address 0x%08llX in section %s does not target section %s",
- srcAddr, sect->sectname(), targetSection->sectname());
- }
- }
- break;
- case X86_64_RELOC_SIGNED:
- case X86_64_RELOC_SIGNED_1:
- case X86_64_RELOC_SIGNED_2:
- case X86_64_RELOC_SIGNED_4:
- 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";
- addend = (int64_t)((int32_t)(E::get32(*fixUpPtr)));
- if ( reloc->r_extern() ) {
- switch ( reloc->r_type() ) {
- case X86_64_RELOC_SIGNED:
- kind = x86_64::kPCRel32;
- // begin support for old .o files before X86_64_RELOC_SIGNED_1 was created
- if ( addend == (uint64_t)(-1) ) {
- addend = 0;
- kind = x86_64::kPCRel32_1;
- }
- else if ( addend == (uint64_t)(-2) ) {
- addend = 0;
- kind = x86_64::kPCRel32_2;
- }
- else if ( addend == (uint64_t)(-4) ) {
- addend = 0;
- kind = x86_64::kPCRel32_4;
- }
- break;
- // end support for old .o files before X86_64_RELOC_SIGNED_1 was created
- case X86_64_RELOC_SIGNED_1:
- kind = x86_64::kPCRel32_1;
- addend += 1;
- break;
- case X86_64_RELOC_SIGNED_2:
- kind = x86_64::kPCRel32_2;
- addend += 2;
- break;
- case X86_64_RELOC_SIGNED_4:
- kind = x86_64::kPCRel32_4;
- addend += 4;
- break;
- }
- makeReferenceToSymbol(kind, srcAddr, targetSymbol, addend);
- }
- else {
- uint64_t ripRelativeOffset = addend;
- switch ( reloc->r_type() ) {
- case X86_64_RELOC_SIGNED:
- dstAddr = srcAddr + 4 + ripRelativeOffset;
- kind = x86_64::kPCRel32;
- break;
- case X86_64_RELOC_SIGNED_1:
- dstAddr = srcAddr + 5 + ripRelativeOffset;
- kind = x86_64::kPCRel32_1;
- break;
- case X86_64_RELOC_SIGNED_2:
- dstAddr = srcAddr + 6 + ripRelativeOffset;
- kind = x86_64::kPCRel32_2;
- break;
- case X86_64_RELOC_SIGNED_4:
- dstAddr = srcAddr + 8 + ripRelativeOffset;
- kind = x86_64::kPCRel32_4;
- break;
- }
- makeReference(kind, srcAddr, dstAddr);
- // verify that dstAddr is in the section being targeted
- int sectNum = reloc->r_symbolnum();
- const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)fSegment + sizeof(macho_segment_command<P>));
- const macho_section<P>* const targetSection = §ionsStart[sectNum-1];
- if ( (dstAddr < targetSection->addr()) || (dstAddr > (targetSection->addr()+targetSection->size())) ) {
- throwf("local relocation for address 0x%08llX in section %s does not target section %s",
- srcAddr, sect->sectname(), targetSection->sectname());
- }
- }
- 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 ) {
- dstAddr = (int64_t)((int32_t)(E::get32(*fixUpPtr)));
- if ( reloc->r_extern() ) {
- if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) {
- makeByNameReference(x86_64::kDtraceProbeSite, srcAddr, targetName, 0);
- addDtraceExtraInfos(srcAddr, &targetName[16]);
- }
- else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) {
- makeByNameReference(x86_64::kDtraceIsEnabledSite, srcAddr, targetName, 0);
- addDtraceExtraInfos(srcAddr, &targetName[16]);
- }
- else 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);
- }
- }
- else if ( reloc->r_length() == 0 ) {
- dstAddr = *((int8_t*)fixUpPtr);
- if ( reloc->r_extern() ) {
- makeReferenceToSymbol(x86_64::kBranchPCRel8, srcAddr, targetSymbol, dstAddr);
- }
- else {
- makeReference(x86_64::kBranchPCRel8, srcAddr, srcAddr+1+dstAddr);
- }
- }
- else {
- throwf("length=%d and X86_64_RELOC_BRANCH not supported", reloc->r_length());;
- }
- 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:
- warning("unknown relocation type %d", reloc->r_type());
- }
- return result;
-}
-
-
-/// Reader<arm>::addRelocReference -
-/// turns arm relocation entries into references. Returns true if the next
-/// relocation should be skipped, false otherwise.
-template <>
-bool Reader<arm>::addRelocReference(const macho_section<arm::P>* sect,
- const macho_relocation_info<arm::P>* reloc)
-{
- uint32_t * fixUpPtr;
- int32_t displacement;
- uint32_t instruction = 0;
- bool result = false;
- uint32_t srcAddr;
- uint32_t dstAddr;
- uint32_t pointerValue;
-
- if ( (reloc->r_address() & R_SCATTERED) == 0 ) {
- // non-scattered relocation
- const char* targetName = NULL;
- bool weakImport = false;
-
- srcAddr = sect->addr() + reloc->r_address();
- fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address());
- if ( reloc->r_type() != ARM_RELOC_PAIR )
- instruction = LittleEndian::get32(*fixUpPtr);
-
- if ( reloc->r_extern() ) {
- const macho_nlist<P>* targetSymbol = &fSymbols[reloc->r_symbolnum()];
- targetName = &fStrings[targetSymbol->n_strx()];
- weakImport = this->isWeakImportSymbol(targetSymbol);
- }
-
- switch ( reloc->r_type() ) {
- case ARM_RELOC_BR24:
- // Sign-extend displacement
- displacement = (instruction & 0x00FFFFFF) << 2;
- if ( (displacement & 0x02000000) != 0 )
- displacement |= 0xFC000000;
- // The pc added will be +8 from the pc
- displacement += 8;
- // If this is BLX add H << 1
- if ((instruction & 0xFE000000) == 0xFA000000)
- displacement += ((instruction & 0x01000000) >> 23);
-
- if ( reloc->r_extern() ) {
- uint32_t offsetInTarget = srcAddr + displacement;
- if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) {
- makeByNameReference(arm::kDtraceProbeSite, srcAddr, targetName, 0);
- addDtraceExtraInfos(srcAddr, &targetName[16]);
- }
- else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) {
- makeByNameReference(arm::kDtraceIsEnabledSite, srcAddr, targetName, 0);
- addDtraceExtraInfos(srcAddr, &targetName[20]);
- }
- else if ( weakImport )
- makeByNameReference(arm::kBranch24WeakImport, srcAddr, targetName, offsetInTarget);
- else
- makeByNameReference(arm::kBranch24, srcAddr, targetName, offsetInTarget);
- }
- else {
- dstAddr = srcAddr + displacement;
- ObjectFile::Atom* atom = findAtomAndOffset(dstAddr).atom;
- // check for dtrace probes and weak_import stubs
- const char* targetName = atom->getName();
- if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_probe$", 16) == 0) ) {
- makeByNameReference(arm::kDtraceProbeSite, srcAddr, targetName, 0);
- addDtraceExtraInfos(srcAddr, &targetName[16]);
- }
- else if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_isenabled$", 20) == 0) ) {
- makeByNameReference(arm::kDtraceIsEnabledSite, srcAddr, targetName, 0);
- addDtraceExtraInfos(srcAddr, &targetName[20]);
- }
- else if ( (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn)
- && ((AnonymousAtom<x86>*)atom)->isWeakImportStub() )
- makeReference(arm::kBranch24WeakImport, srcAddr, dstAddr);
- else if ( reloc->r_symbolnum() != R_ABS )
- makeReference(arm::kBranch24, srcAddr, dstAddr);
- else {
- // find absolute symbol that corresponds to pointerValue
- AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr);
- if ( pos != fAddrToAbsoluteAtom.end() )
- makeByNameReference(arm::kBranch24, srcAddr, pos->second->getName(), 0);
- else
- throwf("R_ABS reloc but no absolute symbol at target address");
- }
- }
- break;
-
- case ARM_THUMB_RELOC_BR22:
- // First instruction has upper 11 bits of the displacement.
- displacement = (instruction & 0x7FF) << 12;
- if ( (displacement & 0x400000) != 0 )
- displacement |= 0xFF800000;
- // Second instruction has lower eleven bits of the displacement.
- displacement += ((instruction >> 16) & 0x7FF) << 1;
- // The pc added will be +4 from the pc
- displacement += 4;
- // If the instruction was blx, force the low 2 bits to be clear
- dstAddr = srcAddr + displacement;
- if ((instruction & 0xF8000000) == 0xE8000000)
- dstAddr &= 0xFFFFFFFC;
-
- if ( reloc->r_extern() ) {
- uint32_t offsetInTarget = dstAddr;
- if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) {
- makeByNameReference(arm::kDtraceProbeSite, srcAddr, targetName, 0);
- addDtraceExtraInfos(srcAddr, &targetName[16]);
- }
- else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) {
- makeByNameReference(arm::kDtraceIsEnabledSite, srcAddr, targetName, 0);
- addDtraceExtraInfos(srcAddr, &targetName[20]);
- }
- else if ( weakImport )
- makeByNameReference(arm::kThumbBranch22WeakImport, srcAddr, targetName, offsetInTarget);
- else
- makeByNameReference(arm::kThumbBranch22, srcAddr, targetName, offsetInTarget);
- }
- else {
- ObjectFile::Atom* atom = findAtomAndOffset(dstAddr).atom;
- // check for dtrace probes and weak_import stubs
- const char* targetName = atom->getName();
- if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_probe$", 16) == 0) ) {
- makeByNameReference(arm::kDtraceProbeSite, srcAddr, targetName, 0);
- addDtraceExtraInfos(srcAddr, &targetName[16]);
- }
- else if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_isenabled$", 20) == 0) ) {
- makeByNameReference(arm::kDtraceIsEnabledSite, srcAddr, targetName, 0);
- addDtraceExtraInfos(srcAddr, &targetName[20]);
- }
- else if ( (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn)
- && ((AnonymousAtom<x86>*)atom)->isWeakImportStub() )
- makeReference(arm::kThumbBranch22WeakImport, srcAddr, dstAddr);
- else if ( reloc->r_symbolnum() != R_ABS )
- makeReference(arm::kThumbBranch22, srcAddr, dstAddr);
- else {
- // find absolute symbol that corresponds to pointerValue
- AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr);
- if ( pos != fAddrToAbsoluteAtom.end() )
- makeByNameReference(arm::kThumbBranch22, srcAddr, pos->second->getName(), 0);
- else
- throwf("R_ABS reloc but no absolute symbol at target address");
- }
- }
- break;
-
- case ARM_RELOC_VANILLA:
- if ( reloc->r_length() != 2 )
- throw "bad length for ARM_RELOC_VANILLA";
-
- pointerValue = instruction;
- if ( reloc->r_extern() ) {
- if ( weakImport )
- makeByNameReference(arm::kPointerWeakImport, srcAddr, targetName, pointerValue);
- else if ( strcmp(sect->segname(), "__TEXT") == 0 )
- makeByNameReference(arm::kReadOnlyPointer, srcAddr, targetName, pointerValue);
- else
- makeByNameReference(arm::kPointer, srcAddr, targetName, pointerValue);
- }
- else {
- if ( strcmp(sect->segname(), "__TEXT") == 0 )
- makeReference(arm::kReadOnlyPointer, srcAddr, pointerValue);
- else
- makeReference(arm::kPointer, srcAddr, pointerValue);
- }
- break;
-
- default:
- warning("unexpected relocation type %u", reloc->r_type());
- break;
- }
- }
- else {
- const macho_scattered_relocation_info<P>* sreloc = (macho_scattered_relocation_info<P>*)reloc;
- const macho_scattered_relocation_info<P>* nextSReloc = &sreloc[1];
- srcAddr = sect->addr() + sreloc->r_address();
- dstAddr = sreloc->r_value();
- uint32_t betterDstAddr;
- fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + sreloc->r_address());
- instruction = LittleEndian::get32(*fixUpPtr);
-
- // A ARM_RELOC_PAIR only follows ARM_RELOC_{SECTDIFF,LOCAL_SECTDIFF}
- // relocation types, and it is an error to see one otherwise.
- bool nextRelocIsPair = false;
- uint32_t nextRelocAddress = 0;
- uint32_t nextRelocValue = 0;
- if ( nextSReloc->r_type() == ARM_RELOC_PAIR ) {
- nextRelocIsPair = true;
- nextRelocAddress = nextSReloc->r_address();
- nextRelocValue = nextSReloc->r_value();
- result = true;
- }
-
- switch (sreloc->r_type()) {
- case ARM_RELOC_VANILLA:
- if ( sreloc->r_length() != 2 )
- throw "bad length for ARM_RELOC_VANILLA";
-
- betterDstAddr = LittleEndian::get32(*fixUpPtr);
- //fprintf(stderr, "scattered pointer reloc: srcAddr=0x%08X, dstAddr=0x%08X, pointer=0x%08X\n", srcAddr, dstAddr, betterDstAddr);
- // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr)
- if ( strcmp(sect->segname(), "__TEXT") == 0 )
- makeReferenceWithToBase(arm::kReadOnlyPointer, srcAddr, betterDstAddr, dstAddr);
- else
- makeReferenceWithToBase(arm::kPointer, srcAddr, betterDstAddr, dstAddr);
- break;
-
- case ARM_RELOC_BR24:
- // Sign-extend displacement
- displacement = (instruction & 0x00FFFFFF) << 2;
- if ( (displacement & 0x02000000) != 0 )
- displacement |= 0xFC000000;
- // The pc added will be +8 from the pc
- displacement += 8;
- // If this is BLX add H << 1
- if ((instruction & 0xFE000000) == 0xFA000000)
- displacement += ((instruction & 0x01000000) >> 23);
- betterDstAddr = srcAddr+displacement;
- makeReferenceWithToBase(arm::kBranch24, srcAddr, betterDstAddr, dstAddr);
- break;
-
- case ARM_THUMB_RELOC_BR22:
- // First instruction has upper 11 bits of the displacement.
- displacement = (instruction & 0x7FF) << 12;
- if ( (displacement & 0x400000) != 0 )
- displacement |= 0xFF800000;
- // Second instruction has lower eleven bits of the displacement.
- displacement += ((instruction >> 16) & 0x7FF) << 1;
- // The pc added will be +4 from the pc
- displacement += 4;
- betterDstAddr = srcAddr+displacement;
- // If the instruction was blx, force the low 2 bits to be clear
- if ((instruction & 0xF8000000) == 0xE8000000)
- betterDstAddr &= 0xFFFFFFFC;
- makeReferenceWithToBase(arm::kThumbBranch22, srcAddr, betterDstAddr, dstAddr);
- break;
-
- case ARM_RELOC_SECTDIFF:
- case ARM_RELOC_LOCAL_SECTDIFF:
- if ( !nextRelocIsPair ) {
- warning("ARM_RELOC_SECTDIFF missing following pair");
- break;
- }
- if ( sreloc->r_length() != 2 )
- throw "bad length for ARM_RELOC_SECTDIFF";
- {
- AtomAndOffset srcao = findAtomAndOffset(srcAddr);
- AtomAndOffset fromao = findAtomAndOffset(nextRelocValue);
- AtomAndOffset toao = findAtomAndOffset(dstAddr);
- // check for addend encoded in the section content
- pointerValue = LittleEndian::get32(*fixUpPtr);
- if ( (dstAddr - nextRelocValue) != pointerValue ) {
- if ( toao.atom == srcao.atom )
- toao.offset += (pointerValue + nextRelocValue) - dstAddr;
- else if ( fromao.atom == srcao.atom )
- toao.offset += (pointerValue + nextRelocValue) - dstAddr;
- else
- fromao.offset += (dstAddr - pointerValue) - nextRelocValue;
- }
- new Reference<arm>(arm::kPointerDiff, srcao, fromao, toao);
- }
- break;
-
- default:
- warning("unexpected srelocation type %u", sreloc->r_type());
- break;
- }
- }
- return result;
-}
-
-template <typename A>
-void Reader<A>::addReferencesForSection(const macho_section<P>* sect)
-{
- // ignore dwarf sections. If ld ever supports processing dwarf, this logic will need to change
- if ( (sect->flags() & S_ATTR_DEBUG) == 0 ) {
- switch ( sect->flags() & SECTION_TYPE ) {
- case S_SYMBOL_STUBS:
- case S_LAZY_SYMBOL_POINTERS:
- // we ignore compiler generated stubs, so ignore those relocs too
- break;
- default:
- const macho_relocation_info<P>* relocs = (macho_relocation_info<P>*)((char*)(fHeader) + sect->reloff());
- const uint32_t relocCount = sect->nreloc();
- //fprintf(stderr, "relocCount = %d in section %s\n", relocCount, sect->sectname());
- for (uint32_t r = 0; r < relocCount; ++r) {
- try {
- if ( addRelocReference(sect, &relocs[r]) )
- ++r; // skip next
- }
- catch (const char* msg) {
- throwf("in section %s,%s reloc %u: %s", sect->segname(), sect->sectname(), r, msg);
- }
- }
- }
- }
-}
-
-
-template <>
-const char* Reference<x86>::getDescription() const
-{
- static char temp[2048];
- switch( fKind ) {
- case x86::kNoFixUp:
- sprintf(temp, "reference to ");
- break;
- case x86::kFollowOn:
- sprintf(temp, "followed by ");
- break;
- case x86::kGroupSubordinate:
- sprintf(temp, "group subordinate ");
- break;
- case x86::kPointerWeakImport:
- sprintf(temp, "offset 0x%04X, weak import pointer to ", fFixUpOffsetInSrc);
- break;
- case x86::kPointer:
- sprintf(temp, "offset 0x%04X, pointer to ", fFixUpOffsetInSrc);
- break;
- case x86::kPointerDiff:
- {
- // by-name references have quoted names
- const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : "";
- const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : "";
- sprintf(temp, "offset 0x%04X, 32-bit pointer difference: (&%s%s%s + 0x%08X) - (&%s%s%s + 0x%08X)",
- fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset,
- fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset );
- return temp;
- }
- break;
- case x86::kPointerDiff16:
- {
- // by-name references have quoted names
- const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : "";
- const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : "";
- sprintf(temp, "offset 0x%04X, 16-bit pointer difference: (&%s%s%s + 0x%08X) - (&%s%s%s + 0x%08X)",
- fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset,
- fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset );
- return temp;
- }
- break;
- case x86::kPCRel32WeakImport:
- sprintf(temp, "offset 0x%04X, rel32 reference to weak imported ", fFixUpOffsetInSrc);
- break;
- case x86::kPCRel32:
- sprintf(temp, "offset 0x%04X, rel32 reference to ", fFixUpOffsetInSrc);
- break;
- case x86::kPCRel16:
- sprintf(temp, "offset 0x%04X, rel16 reference to ", fFixUpOffsetInSrc);
- break;
- case x86::kPCRel8:
- sprintf(temp, "offset 0x%04X, rel8 reference to ", fFixUpOffsetInSrc);
- break;
- case x86::kAbsolute32:
- sprintf(temp, "offset 0x%04X, absolute32 reference to ", fFixUpOffsetInSrc);
- break;
- case x86::kDtraceProbe:
- sprintf(temp, "offset 0x%04X, dtrace static probe ", fFixUpOffsetInSrc);
- break;
- case x86::kDtraceProbeSite:
- sprintf(temp, "offset 0x%04X, dtrace static probe site", fFixUpOffsetInSrc);
- break;
- case x86::kDtraceIsEnabledSite:
- sprintf(temp, "offset 0x%04X, dtrace static probe is-enabled site", fFixUpOffsetInSrc);
- break;
- case x86::kDtraceTypeReference:
- sprintf(temp, "offset 0x%04X, dtrace type/stability reference", fFixUpOffsetInSrc);
- break;
- }
- // always quote by-name references
- if ( fToTargetName != NULL ) {
- strcat(temp, "\"");
- strcat(temp, fToTargetName);
- strcat(temp, "\"");
- }
- else if ( fToTarget.atom != NULL ) {
- strcat(temp, fToTarget.atom->getDisplayName());
- }
- else {
- strcat(temp, "NULL target");
- }
- if ( fToTarget.offset != 0 )
- sprintf(&temp[strlen(temp)], " plus 0x%08X", fToTarget.offset);
-
- return temp;
-}
-
-
-template <>
-const char* Reference<ppc>::getDescription() const
-{
- static char temp[2048];
- switch( fKind ) {
- case ppc::kNoFixUp:
- sprintf(temp, "reference to ");
- break;
- case ppc::kFollowOn:
- sprintf(temp, "followed by ");
- break;
- case ppc::kGroupSubordinate:
- sprintf(temp, "group subordinate ");
- break;
- case ppc::kPointerWeakImport:
- sprintf(temp, "offset 0x%04X, weak import pointer to ", fFixUpOffsetInSrc);
- break;
- case ppc::kPointer:
- sprintf(temp, "offset 0x%04X, pointer to ", fFixUpOffsetInSrc);
- break;
- case ppc::kPointerDiff16:
- {
- // by-name references have quoted names
- const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : "";
- const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : "";
- sprintf(temp, "offset 0x%04X, 16-bit pointer difference: (&%s%s%s + %d) - (&%s%s%s + %d)",
- fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset,
- fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset );
- return temp;
- }
- case ppc::kPointerDiff32:
- {
- // by-name references have quoted names
- const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : "";
- const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : "";
- sprintf(temp, "offset 0x%04X, 32-bit pointer difference: (&%s%s%s + %d) - (&%s%s%s + %d)",
- fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset,
- fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset );
- return temp;
- }
- case ppc::kPointerDiff64:
- throw "unsupported refrence kind";
- break;
- case ppc::kBranch24WeakImport:
- sprintf(temp, "offset 0x%04X, pc-rel branch fixup to weak imported ", fFixUpOffsetInSrc);
- break;
- case ppc::kBranch24:
- case ppc::kBranch14:
- sprintf(temp, "offset 0x%04X, pc-rel branch fixup to ", fFixUpOffsetInSrc);
- break;
- case ppc::kPICBaseLow16:
- sprintf(temp, "offset 0x%04X, low 16 fixup from pic-base of %s plus 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.atom->getDisplayName(), fFromTarget.offset);
- break;
- case ppc::kPICBaseLow14:
- sprintf(temp, "offset 0x%04X, low 14 fixup from pic-base of %s plus 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.atom->getDisplayName(), fFromTarget.offset);
- break;
- case ppc::kPICBaseHigh16:
- sprintf(temp, "offset 0x%04X, high 16 fixup from pic-base of %s plus 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.atom->getDisplayName(), fFromTarget.offset);
- break;
- case ppc::kAbsLow16:
- sprintf(temp, "offset 0x%04X, low 16 fixup to absolute address of ", fFixUpOffsetInSrc);
- break;
- case ppc::kAbsLow14:
- sprintf(temp, "offset 0x%04X, low 14 fixup to absolute address of ", fFixUpOffsetInSrc);
- break;
- case ppc::kAbsHigh16:
- sprintf(temp, "offset 0x%04X, high 16 fixup or to absolute address of ", fFixUpOffsetInSrc);
- break;
- case ppc::kAbsHigh16AddLow:
- sprintf(temp, "offset 0x%04X, high 16 fixup add to absolute address of ", fFixUpOffsetInSrc);
- break;
- case ppc::kDtraceProbe:
- sprintf(temp, "offset 0x%04X, dtrace static probe ", fFixUpOffsetInSrc);
- break;
- case ppc::kDtraceProbeSite:
- sprintf(temp, "offset 0x%04X, dtrace static probe site", fFixUpOffsetInSrc);
- break;
- case ppc::kDtraceIsEnabledSite:
- sprintf(temp, "offset 0x%04X, dtrace static probe is-enabled site", fFixUpOffsetInSrc);
- break;
- case ppc::kDtraceTypeReference:
- sprintf(temp, "offset 0x%04X, dtrace type/stability reference", fFixUpOffsetInSrc);
- break;
- }
- // always quote by-name references
- if ( fToTargetName != NULL ) {
- strcat(temp, "\"");
- strcat(temp, fToTargetName);
- strcat(temp, "\"");
- }
- else if ( fToTarget.atom != NULL ) {
- strcat(temp, fToTarget.atom->getDisplayName());
- }
- else {
- strcat(temp, "NULL target");
- }
- if ( fToTarget.offset != 0 )
- sprintf(&temp[strlen(temp)], " plus 0x%08X", fToTarget.offset);
-
- return temp;
-}
-
-template <>
-const char* Reference<ppc64>::getDescription() const
-{
- static char temp[2048];
- switch( fKind ) {
- case ppc64::kNoFixUp:
- sprintf(temp, "reference to ");
- break;
- case ppc64::kFollowOn:
- sprintf(temp, "followed by ");
- break;
- case ppc64::kGroupSubordinate:
- sprintf(temp, "group subordinate ");
- break;
- case ppc64::kPointerWeakImport:
- sprintf(temp, "offset 0x%04llX, weak import pointer to ", fFixUpOffsetInSrc);
- break;
- case ppc64::kPointer:
- sprintf(temp, "offset 0x%04llX, pointer to ", fFixUpOffsetInSrc);
- break;
- case ppc64::kPointerDiff64:
- {
- // by-name references have quoted names
- const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : "";
- const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : "";
- sprintf(temp, "offset 0x%04llX, 64-bit pointer difference: (&%s%s%s + %u) - (&%s%s%s + %u)",
- fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset,
- fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset );
- return temp;
- }
- case ppc64::kPointerDiff32:
- {
- // by-name references have quoted names
- const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : "";
- const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : "";
- sprintf(temp, "offset 0x%04llX, 32-bit pointer difference: (&%s%s%s + %u) - (&%s%s%s + %u)",
- fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset,
- fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset );
- return temp;
- }
- case ppc64::kPointerDiff16:
- {
- // by-name references have quoted names
- const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : "";
- const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : "";
- sprintf(temp, "offset 0x%04llX, 16-bit pointer difference: (&%s%s%s + %u) - (&%s%s%s + %u)",
- fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset,
- fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset );
- return temp;
- }
- case ppc64::kBranch24WeakImport:
- sprintf(temp, "offset 0x%04llX, pc-rel branch fixup to weak imported ", fFixUpOffsetInSrc);
- break;
- case ppc64::kBranch24:
- case ppc64::kBranch14:
- sprintf(temp, "offset 0x%04llX, pc-rel branch fixup to ", fFixUpOffsetInSrc);
- break;
- case ppc64::kPICBaseLow16:
- sprintf(temp, "offset 0x%04llX, low 16 fixup from pic-base offset 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.offset);
- break;
- case ppc64::kPICBaseLow14:
- sprintf(temp, "offset 0x%04llX, low 14 fixup from pic-base offset 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.offset);
- break;
- case ppc64::kPICBaseHigh16:
- sprintf(temp, "offset 0x%04llX, high 16 fixup from pic-base offset 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.offset);
- break;
- case ppc64::kAbsLow16:
- sprintf(temp, "offset 0x%04llX, low 16 fixup to absolute address of ", fFixUpOffsetInSrc);
- break;
- case ppc64::kAbsLow14:
- sprintf(temp, "offset 0x%04llX, low 14 fixup to absolute address of ", fFixUpOffsetInSrc);
- break;
- case ppc64::kAbsHigh16:
- sprintf(temp, "offset 0x%04llX, high 16 fixup or to absolute address of ", fFixUpOffsetInSrc);
- break;
- case ppc64::kAbsHigh16AddLow:
- sprintf(temp, "offset 0x%04llX, high 16 fixup add to absolute address of ", fFixUpOffsetInSrc);
- break;
- case ppc64::kDtraceProbe:
- sprintf(temp, "offset 0x%04llX, dtrace static probe ", fFixUpOffsetInSrc);
- break;
- case ppc64::kDtraceProbeSite:
- sprintf(temp, "offset 0x%04llX, dtrace static probe site", fFixUpOffsetInSrc);
- break;
- case ppc64::kDtraceIsEnabledSite:
- sprintf(temp, "offset 0x%04llX, dtrace static probe is-enabled site", fFixUpOffsetInSrc);
- break;
- case ppc64::kDtraceTypeReference:
- sprintf(temp, "offset 0x%04llX, dtrace type/stability reference", 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;
-}
-
-
-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::kGroupSubordinate:
- sprintf(temp, "group subordinate ");
- 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;
- case x86_64::kBranchPCRel8:
- sprintf(temp, "offset 0x%04llX, branch rel8 reference to ", fFixUpOffsetInSrc);
- break;
- case x86_64::kDtraceProbe:
- sprintf(temp, "offset 0x%04llX, dtrace static probe ", fFixUpOffsetInSrc);
- break;
- case x86_64::kDtraceProbeSite:
- sprintf(temp, "offset 0x%04llX, dtrace static probe site", fFixUpOffsetInSrc);
- break;
- case x86_64::kDtraceIsEnabledSite:
- sprintf(temp, "offset 0x%04llX, dtrace static probe is-enabled site", fFixUpOffsetInSrc);
- break;
- case x86_64::kDtraceTypeReference:
- sprintf(temp, "offset 0x%04llX, dtrace type/stability reference", 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;
-}
-
-template <>
-const char* Reference<arm>::getDescription() const
-{
- static char temp[2048];
- switch( fKind ) {
- case arm::kNoFixUp:
- sprintf(temp, "reference to ");
- break;
- case arm::kFollowOn:
- sprintf(temp, "followed by ");
- break;
- case arm::kGroupSubordinate:
- sprintf(temp, "group subordinate ");
- break;
- case arm::kPointer:
- sprintf(temp, "offset 0x%04X, pointer to ", fFixUpOffsetInSrc);
- break;
- case arm::kPointerWeakImport:
- sprintf(temp, "offset 0x%04X, weak import pointer to ", fFixUpOffsetInSrc);
- break;
- case arm::kPointerDiff:
- {
- // by-name references have quoted names
- const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : "";
- const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : "";
- sprintf(temp, "offset 0x%04X, 32-bit pointer difference: (&%s%s%s + %d) - (&%s%s%s + %d)",
- fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset,
- fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset );
- return temp;
- }
- case arm::kReadOnlyPointer:
- sprintf(temp, "offset 0x%04X, read-only pointer to ", fFixUpOffsetInSrc);
- break;
- case arm::kBranch24:
- case arm::kThumbBranch22:
- sprintf(temp, "offset 0x%04X, pc-rel branch fixup to ", fFixUpOffsetInSrc);
- break;
- case arm::kBranch24WeakImport:
- case arm::kThumbBranch22WeakImport:
- sprintf(temp, "offset 0x%04X, pc-rel branch fixup to weak imported ", fFixUpOffsetInSrc);
- break;
- case arm::kDtraceProbe:
- sprintf(temp, "offset 0x%04X, dtrace static probe ", fFixUpOffsetInSrc);
- break;
- case arm::kDtraceProbeSite:
- sprintf(temp, "offset 0x%04X, dtrace static probe site", fFixUpOffsetInSrc);
- break;
- case arm::kDtraceIsEnabledSite:
- sprintf(temp, "offset 0x%04X, dtrace static probe is-enabled site", fFixUpOffsetInSrc);
- break;
- case arm::kDtraceTypeReference:
- sprintf(temp, "offset 0x%04X, dtrace type/stability reference", fFixUpOffsetInSrc);
- break;
- }
- // always quote by-name references
- if ( fToTargetName != NULL ) {
- strcat(temp, "\"");
- strcat(temp, fToTargetName);
- strcat(temp, "\"");
- }
- else if ( fToTarget.atom != NULL ) {
- strcat(temp, fToTarget.atom->getDisplayName());
- }
- else {
- strcat(temp, "NULL target");
- }
- if ( fToTarget.offset != 0 )
- sprintf(&temp[strlen(temp)], " plus 0x%08X", fToTarget.offset);
-
- return temp;
-}
-
-}; // namespace relocatable
-}; // namespace mach_o
-
-#endif // __OBJECT_FILE_MACH_O__
+++ /dev/null
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
- *
- * Copyright (c) 2005-2008 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#ifndef __EXECUTABLE_MACH_O__
-#define __EXECUTABLE_MACH_O__
-
-#include <stdint.h>
-#include <stddef.h>
-#include <fcntl.h>
-#include <sys/time.h>
-#include <uuid/uuid.h>
-#include <mach/i386/thread_status.h>
-#include <mach/ppc/thread_status.h>
-#include <CommonCrypto/CommonDigest.h>
-
-#include <vector>
-#include <algorithm>
-#include <map>
-#include <set>
-#include <ext/hash_map>
-
-#include "ObjectFile.h"
-#include "ExecutableFile.h"
-#include "Options.h"
-
-#include "MachOFileAbstraction.hpp"
-
-
-//
-//
-// To implement architecture xxx, you must write template specializations for the following methods:
-// MachHeaderAtom<xxx>::setHeaderInfo()
-// ThreadsLoadCommandsAtom<xxx>::getSize()
-// ThreadsLoadCommandsAtom<xxx>::copyRawContent()
-// Writer<xxx>::addObjectRelocs()
-// Writer<xxx>::fixUpReferenceRelocatable()
-// Writer<xxx>::fixUpReferenceFinal()
-// Writer<xxx>::stubableReference()
-// Writer<xxx>::weakImportReferenceKind()
-// Writer<xxx>::GOTReferenceKind()
-//
-
-
-namespace mach_o {
-namespace executable {
-
-// forward references
-template <typename A> class WriterAtom;
-template <typename A> class PageZeroAtom;
-template <typename A> class CustomStackAtom;
-template <typename A> class MachHeaderAtom;
-template <typename A> class SegmentLoadCommandsAtom;
-template <typename A> class EncryptionLoadCommandsAtom;
-template <typename A> class SymbolTableLoadCommandsAtom;
-template <typename A> class ThreadsLoadCommandsAtom;
-template <typename A> class DylibIDLoadCommandsAtom;
-template <typename A> class RoutinesLoadCommandsAtom;
-template <typename A> class DyldLoadCommandsAtom;
-template <typename A> class UUIDLoadCommandAtom;
-template <typename A> class LinkEditAtom;
-template <typename A> class SectionRelocationsLinkEditAtom;
-template <typename A> class LocalRelocationsLinkEditAtom;
-template <typename A> class ExternalRelocationsLinkEditAtom;
-template <typename A> class SymbolTableLinkEditAtom;
-template <typename A> class SegmentSplitInfoLoadCommandsAtom;
-template <typename A> class SegmentSplitInfoContentAtom;
-template <typename A> class IndirectTableLinkEditAtom;
-template <typename A> class ModuleInfoLinkEditAtom;
-template <typename A> class StringsLinkEditAtom;
-template <typename A> class LoadCommandsPaddingAtom;
-template <typename A> class StubAtom;
-template <typename A> class StubHelperAtom;
-template <typename A> class LazyPointerAtom;
-template <typename A> class NonLazyPointerAtom;
-template <typename A> class DylibLoadCommandsAtom;
-
-
-// SectionInfo should be nested inside Writer, but I can't figure out how to make the type accessible to the Atom classes
-class SectionInfo : public ObjectFile::Section {
-public:
- SectionInfo() : fFileOffset(0), fSize(0), fRelocCount(0), fRelocOffset(0),
- fIndirectSymbolOffset(0), fAlignment(0), fAllLazyPointers(false),
- fAllLazyDylibPointers(false),fAllNonLazyPointers(false), fAllStubs(false),
- fAllSelfModifyingStubs(false), fAllZeroFill(false), fVirtualSection(false),
- fHasTextLocalRelocs(false), fHasTextExternalRelocs(false)
- { fSegmentName[0] = '\0'; fSectionName[0] = '\0'; }
- void setIndex(unsigned int index) { fIndex=index; }
- std::vector<ObjectFile::Atom*> fAtoms;
- char fSegmentName[20];
- char fSectionName[20];
- uint64_t fFileOffset;
- uint64_t fSize;
- uint32_t fRelocCount;
- uint32_t fRelocOffset;
- uint32_t fIndirectSymbolOffset;
- uint8_t fAlignment;
- bool fAllLazyPointers;
- bool fAllLazyDylibPointers;
- bool fAllNonLazyPointers;
- bool fAllStubs;
- bool fAllSelfModifyingStubs;
- bool fAllZeroFill;
- bool fVirtualSection;
- bool fHasTextLocalRelocs;
- bool fHasTextExternalRelocs;
-};
-
-// SegmentInfo should be nested inside Writer, but I can't figure out how to make the type accessible to the Atom classes
-class SegmentInfo
-{
-public:
- SegmentInfo() : fInitProtection(0), fMaxProtection(0), fFileOffset(0), fFileSize(0),
- fBaseAddress(0), fSize(0), fFixedAddress(false),
- fIndependentAddress(false) { fName[0] = '\0'; }
- std::vector<class SectionInfo*> fSections;
- char fName[20];
- uint32_t fInitProtection;
- uint32_t fMaxProtection;
- uint64_t fFileOffset;
- uint64_t fFileSize;
- uint64_t fBaseAddress;
- uint64_t fSize;
- bool fFixedAddress;
- bool fIndependentAddress;
-};
-
-template <typename A>
-class Writer : public ExecutableFile::Writer
-{
-public:
- Writer(const char* path, Options& options, std::vector<ExecutableFile::DyLibUsed>& dynamicLibraries);
- virtual ~Writer();
-
- virtual const char* getPath() { return fFilePath; }
- virtual time_t getModificationTime() { return 0; }
- virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; }
- virtual std::vector<class ObjectFile::Atom*>& getAtoms() { return fWriterSynthesizedAtoms; }
- virtual std::vector<class ObjectFile::Atom*>* getJustInTimeAtomsFor(const char* name) { return NULL; }
- virtual std::vector<Stab>* getStabs() { return NULL; }
-
- virtual ObjectFile::Atom& makeObjcInfoAtom(ObjectFile::Reader::ObjcConstraint objcContraint,
- bool objcReplacementClasses);
- virtual class ObjectFile::Atom* getUndefinedProxyAtom(const char* name);
- virtual uint64_t write(std::vector<class ObjectFile::Atom*>& atoms,
- std::vector<class ObjectFile::Reader::Stab>& stabs,
- class ObjectFile::Atom* entryPointAtom,
- class ObjectFile::Atom* dyldHelperAtom,
- class ObjectFile::Atom* dyldLazyDylibHelperAtom,
- bool createUUID, bool canScatter,
- ObjectFile::Reader::CpuConstraint cpuConstraint,
- bool biggerThanTwoGigs, bool overridesDylibWeakDefines);
-
-private:
- typedef typename A::P P;
- typedef typename A::P::uint_t pint_t;
-
- enum RelocKind { kRelocNone, kRelocInternal, kRelocExternal };
-
- void assignFileOffsets();
- void synthesizeStubs();
- void insertDummyStubs();
- void partitionIntoSections();
- bool addBranchIslands();
- bool addPPCBranchIslands();
- bool isBranch24Reference(uint8_t kind);
- void adjustLoadCommandsAndPadding();
- void createDynamicLinkerCommand();
- void createDylibCommands();
- void buildLinkEdit();
- const char* getArchString();
- void writeMap();
- uint64_t writeAtoms();
- void writeNoOps(int fd, uint32_t from, uint32_t to);
- void copyNoOps(uint8_t* from, uint8_t* to);
- bool segmentsCanSplitApart(const ObjectFile::Atom& from, const ObjectFile::Atom& to);
- void addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref);
- void collectExportedAndImportedAndLocalAtoms();
- void setNlistRange(std::vector<class ObjectFile::Atom*>& atoms, uint32_t startIndex, uint32_t count);
- void addLocalLabel(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* name);
- void addGlobalLabel(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* name);
- void buildSymbolTable();
- const char* symbolTableName(const ObjectFile::Atom* atom);
- void setExportNlist(const ObjectFile::Atom* atom, macho_nlist<P>* entry);
- void setImportNlist(const ObjectFile::Atom* atom, macho_nlist<P>* entry);
- void setLocalNlist(const ObjectFile::Atom* atom, macho_nlist<P>* entry);
- void copyNlistRange(const std::vector<macho_nlist<P> >& entries, uint32_t startIndex);
- uint64_t getAtomLoadAddress(const ObjectFile::Atom* atom);
- uint8_t ordinalForLibrary(ObjectFile::Reader* file);
- bool shouldExport(const ObjectFile::Atom& atom) const;
- void buildFixups();
- void adjustLinkEditSections();
- void buildObjectFileFixups();
- void buildExecutableFixups();
- bool preboundLazyPointerType(uint8_t* type);
- uint64_t relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const;
- void fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const;
- void fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const;
- void fixUpReference_powerpc(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom,
- uint8_t buffer[], bool finalLinkedImage) const;
- uint32_t symbolIndex(ObjectFile::Atom& atom);
- bool makesExternalRelocatableReference(ObjectFile::Atom& target) const;
- uint32_t addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref);
- uint32_t addObjectRelocs_powerpc(ObjectFile::Atom* atom, ObjectFile::Reference* ref);
- uint8_t getRelocPointerSize();
- uint64_t maxAddress();
- bool stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref);
- bool GOTReferenceKind(uint8_t kind);
- bool optimizableGOTReferenceKind(uint8_t kind);
- bool weakImportReferenceKind(uint8_t kind);
- unsigned int collectStabs();
- uint64_t valueForStab(const ObjectFile::Reader::Stab& stab);
- uint32_t stringOffsetForStab(const ObjectFile::Reader::Stab& stab);
- uint8_t sectionIndexForStab(const ObjectFile::Reader::Stab& stab);
- void addStabs(uint32_t startIndex);
- RelocKind relocationNeededInFinalLinkedImage(const ObjectFile::Atom& target) const;
- bool illegalRelocInFinalLinkedImage(const ObjectFile::Reference&);
- bool generatesLocalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection);
- bool generatesExternalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection);
- bool mightNeedPadSegment();
- void scanForAbsoluteReferences();
- bool needsModuleTable();
- void optimizeDylibReferences();
- bool indirectSymbolIsLocal(const ObjectFile::Reference* ref) const;
-
- struct DirectLibrary {
- class ObjectFile::Reader* fLibrary;
- bool fWeak;
- bool fReExport;
- };
-
- friend class WriterAtom<A>;
- friend class PageZeroAtom<A>;
- friend class CustomStackAtom<A>;
- friend class MachHeaderAtom<A>;
- friend class SegmentLoadCommandsAtom<A>;
- friend class EncryptionLoadCommandsAtom<A>;
- friend class SymbolTableLoadCommandsAtom<A>;
- friend class ThreadsLoadCommandsAtom<A>;
- friend class DylibIDLoadCommandsAtom<A>;
- friend class RoutinesLoadCommandsAtom<A>;
- friend class DyldLoadCommandsAtom<A>;
- friend class UUIDLoadCommandAtom<A>;
- friend class LinkEditAtom<A>;
- friend class SectionRelocationsLinkEditAtom<A>;
- friend class LocalRelocationsLinkEditAtom<A>;
- friend class ExternalRelocationsLinkEditAtom<A>;
- friend class SymbolTableLinkEditAtom<A>;
- friend class SegmentSplitInfoLoadCommandsAtom<A>;
- friend class SegmentSplitInfoContentAtom<A>;
-// friend class IndirectTableLinkEditAtom<A>;
- friend class ModuleInfoLinkEditAtom<A>;
- friend class StringsLinkEditAtom<A>;
- friend class LoadCommandsPaddingAtom<A>;
- friend class StubAtom<A>;
- friend class StubHelperAtom<A>;
- friend class LazyPointerAtom<A>;
- friend class NonLazyPointerAtom<A>;
- friend class DylibLoadCommandsAtom<A>;
-
- const char* fFilePath;
- Options& fOptions;
- std::vector<class ObjectFile::Atom*>* fAllAtoms;
- std::vector<class ObjectFile::Reader::Stab>* fStabs;
- class SectionInfo* fLoadCommandsSection;
- class SegmentInfo* fLoadCommandsSegment;
- class EncryptionLoadCommandsAtom<A>* fEncryptionLoadCommand;
- class SegmentLoadCommandsAtom<A>* fSegmentCommands;
- class SymbolTableLoadCommandsAtom<A>* fSymbolTableCommands;
- class LoadCommandsPaddingAtom<A>* fHeaderPadding;
- 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;
- class ObjectFile::Atom* fDyldLazyDylibHelper;
- std::map<class ObjectFile::Reader*, DylibLoadCommandsAtom<A>*> fLibraryToLoadCommand;
- std::map<class ObjectFile::Reader*, uint32_t> fLibraryToOrdinal;
- std::map<class ObjectFile::Reader*, class ObjectFile::Reader*> fLibraryAliases;
- std::vector<class ObjectFile::Atom*> fExportedAtoms;
- std::vector<class ObjectFile::Atom*> fImportedAtoms;
- std::vector<class ObjectFile::Atom*> fLocalSymbolAtoms;
- std::vector<macho_nlist<P> > fLocalExtraLabels;
- std::vector<macho_nlist<P> > fGlobalExtraLabels;
- class SectionRelocationsLinkEditAtom<A>* fSectionRelocationsAtom;
- class LocalRelocationsLinkEditAtom<A>* fLocalRelocationsAtom;
- class ExternalRelocationsLinkEditAtom<A>* fExternalRelocationsAtom;
- class SymbolTableLinkEditAtom<A>* fSymbolTableAtom;
- class SegmentSplitInfoContentAtom<A>* fSplitCodeToDataContentAtom;
- class IndirectTableLinkEditAtom<A>* fIndirectTableAtom;
- class ModuleInfoLinkEditAtom<A>* fModuleInfoAtom;
- 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;
- std::vector<macho_relocation_info<P> > fExternalRelocs;
- std::map<const ObjectFile::Atom*,ObjectFile::Atom*> fStubsMap;
- std::map<ObjectFile::Atom*,ObjectFile::Atom*> fGOTMap;
- std::vector<class StubAtom<A>*> fAllSynthesizedStubs;
- std::vector<ObjectFile::Atom*> fAllSynthesizedStubHelpers;
- std::vector<class LazyPointerAtom<A>*> fAllSynthesizedLazyPointers;
- std::vector<class LazyPointerAtom<A>*> fAllSynthesizedLazyDylibPointers;
- std::vector<class NonLazyPointerAtom<A>*> fAllSynthesizedNonLazyPointers;
- uint32_t fSymbolTableCount;
- uint32_t fSymbolTableStabsCount;
- uint32_t fSymbolTableStabsStartIndex;
- uint32_t fSymbolTableLocalCount;
- uint32_t fSymbolTableLocalStartIndex;
- uint32_t fSymbolTableExportCount;
- uint32_t fSymbolTableExportStartIndex;
- uint32_t fSymbolTableImportCount;
- uint32_t fSymbolTableImportStartIndex;
- uint32_t fLargestAtomSize;
- bool fEmitVirtualSections;
- bool fHasWeakExports;
- bool fReferencesWeakImports;
- bool fCanScatter;
- bool fWritableSegmentPastFirst4GB;
- bool fNoReExportedDylibs;
- bool fBiggerThanTwoGigs;
- bool fSlideable;
- std::map<const ObjectFile::Atom*,bool> fWeakImportMap;
- std::set<const ObjectFile::Reader*> fDylibReadersWithNonWeakImports;
- std::set<const ObjectFile::Reader*> fDylibReadersWithWeakImports;
- SegmentInfo* fFirstWritableSegment;
- ObjectFile::Reader::CpuConstraint fCpuConstraint;
- uint32_t fAnonNameIndex;
-};
-
-
-class Segment : public ObjectFile::Segment
-{
-public:
- Segment(const char* name, bool readable, bool writable, bool executable, bool fixedAddress)
- : fName(name), fReadable(readable), fWritable(writable), fExecutable(executable), fFixedAddress(fixedAddress) {}
- virtual const char* getName() const { return fName; }
- virtual bool isContentReadable() const { return fReadable; }
- virtual bool isContentWritable() const { return fWritable; }
- virtual bool isContentExecutable() const { return fExecutable; }
- virtual bool hasFixedAddress() const { return fFixedAddress; }
-
- static Segment fgTextSegment;
- static Segment fgPageZeroSegment;
- static Segment fgLinkEditSegment;
- static Segment fgStackSegment;
- static Segment fgImportSegment;
- static Segment fgROImportSegment;
- static Segment fgDataSegment;
- static Segment fgObjCSegment;
-
-
-private:
- const char* fName;
- const bool fReadable;
- const bool fWritable;
- const bool fExecutable;
- const bool fFixedAddress;
-};
-
-Segment Segment::fgPageZeroSegment("__PAGEZERO", false, false, false, true);
-Segment Segment::fgTextSegment("__TEXT", true, false, true, false);
-Segment Segment::fgLinkEditSegment("__LINKEDIT", true, false, false, false);
-Segment Segment::fgStackSegment("__UNIXSTACK", true, true, false, true);
-Segment Segment::fgImportSegment("__IMPORT", true, true, true, false);
-Segment Segment::fgROImportSegment("__IMPORT", true, false, true, false);
-Segment Segment::fgDataSegment("__DATA", true, true, false, false);
-Segment Segment::fgObjCSegment("__OBJC", true, true, false, false);
-
-
-template <typename A>
-class WriterAtom : public ObjectFile::Atom
-{
-public:
- enum Kind { zeropage, machHeaderApp, machHeaderDylib, machHeaderBundle, machHeaderObject, loadCommands, undefinedProxy };
- 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 const char* getName() const { return NULL; }
- virtual const char* getDisplayName() const { return this->getName(); }
- virtual Scope getScope() const { return ObjectFile::Atom::scopeTranslationUnit; }
- virtual DefinitionKind getDefinitionKind() const { return kRegularDefinition; }
- virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; }
- virtual bool dontDeadStrip() const { return true; }
- virtual bool isZeroFill() const { return false; }
- virtual bool isThumb() const { return false; }
- virtual std::vector<ObjectFile::Reference*>& getReferences() const { return fgEmptyReferenceList; }
- virtual bool mustRemainInSection() const { return true; }
- virtual ObjectFile::Segment& getSegment() const { return fSegment; }
- virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); }
- virtual uint32_t getOrdinal() const { return 0; }
- virtual std::vector<ObjectFile::LineInfo>* getLineInfo() const { return NULL; }
- virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(2); }
- virtual void copyRawContent(uint8_t buffer[]) const { throw "don't use copyRawContent"; }
- virtual void setScope(Scope) { }
-
-
-protected:
- virtual ~WriterAtom() {}
- typedef typename A::P P;
- typedef typename A::P::E E;
-
- static std::vector<ObjectFile::Reference*> fgEmptyReferenceList;
-
- Writer<A>& fWriter;
- Segment& fSegment;
-};
-
-template <typename A> std::vector<ObjectFile::Reference*> WriterAtom<A>::fgEmptyReferenceList;
-
-
-template <typename A>
-class PageZeroAtom : public WriterAtom<A>
-{
-public:
- 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 fSize; }
- virtual const char* getSectionName() const { return "._zeropage"; }
- virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(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>
-{
-public:
- DsoHandleAtom(Writer<A>& writer) : WriterAtom<A>(writer, Segment::fgTextSegment) {}
- virtual const char* getName() const { return "___dso_handle"; }
- virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; }
- virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; }
- virtual uint64_t getSize() const { return 0; }
- virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(12); }
- virtual const char* getSectionName() const { return "._mach_header"; }
- virtual void copyRawContent(uint8_t buffer[]) const {}
-};
-
-
-template <typename A>
-class MachHeaderAtom : public WriterAtom<A>
-{
-public:
- MachHeaderAtom(Writer<A>& writer) : WriterAtom<A>(writer, Segment::fgTextSegment) {}
- virtual const char* getName() const;
- virtual const char* getDisplayName() const;
- virtual ObjectFile::Atom::Scope getScope() const;
- virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const;
- virtual uint64_t getSize() const { return sizeof(macho_header<typename A::P>); }
- virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(12); }
- virtual const char* getSectionName() const { return "._mach_header"; }
- virtual uint32_t getOrdinal() const { return 1; }
- virtual void copyRawContent(uint8_t buffer[]) const;
-private:
- using WriterAtom<A>::fWriter;
- typedef typename A::P P;
- void setHeaderInfo(macho_header<typename A::P>& header) const;
-};
-
-template <typename A>
-class CustomStackAtom : public WriterAtom<A>
-{
-public:
- CustomStackAtom(Writer<A>& writer);
- virtual const char* getDisplayName() const { return "custom stack content"; }
- virtual bool isZeroFill() const { return true; }
- virtual uint64_t getSize() const { return fWriter.fOptions.customStackSize(); }
- virtual const char* getSectionName() const { return "._stack"; }
- virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(12); }
-private:
- using WriterAtom<A>::fWriter;
- typedef typename A::P P;
- static bool stackGrowsDown();
-};
-
-template <typename A>
-class LoadCommandAtom : public WriterAtom<A>
-{
-protected:
- LoadCommandAtom(Writer<A>& writer, Segment& segment) : WriterAtom<A>(writer, segment), fOrdinal(fgCurrentOrdinal++) {}
- virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(log2(sizeof(typename A::P::uint_t))); }
- virtual const char* getSectionName() const { return "._load_commands"; }
- virtual uint32_t getOrdinal() const { return fOrdinal; }
- static uint64_t alignedSize(uint64_t size);
-protected:
- uint32_t fOrdinal;
- static uint32_t fgCurrentOrdinal;
-};
-
-template <typename A> uint32_t LoadCommandAtom<A>::fgCurrentOrdinal = 0;
-
-template <typename A>
-class SegmentLoadCommandsAtom : public LoadCommandAtom<A>
-{
-public:
- SegmentLoadCommandsAtom(Writer<A>& writer)
- : LoadCommandAtom<A>(writer, Segment::fgTextSegment), fCommandCount(0), fSize(0)
- { writer.fSegmentCommands = this; }
- virtual const char* getDisplayName() const { return "segment load commands"; }
- virtual uint64_t getSize() const { return fSize; }
- virtual void copyRawContent(uint8_t buffer[]) const;
-
- void computeSize();
- void setup();
- unsigned int commandCount() { return fCommandCount; }
-private:
- using WriterAtom<A>::fWriter;
- typedef typename A::P P;
- unsigned int fCommandCount;
- uint32_t fSize;
-};
-
-
-template <typename A>
-class SymbolTableLoadCommandsAtom : public LoadCommandAtom<A>
-{
-public:
- SymbolTableLoadCommandsAtom(Writer<A>&);
- virtual const char* getDisplayName() const { return "symbol table load commands"; }
- virtual uint64_t getSize() const;
- virtual void copyRawContent(uint8_t buffer[]) const;
- unsigned int commandCount();
- void needDynamicTable();
-private:
- using WriterAtom<A>::fWriter;
- typedef typename A::P P;
- bool fNeedsDynamicSymbolTable;
- macho_symtab_command<typename A::P> fSymbolTable;
- macho_dysymtab_command<typename A::P> fDynamicSymbolTable;
-};
-
-template <typename A>
-class ThreadsLoadCommandsAtom : public LoadCommandAtom<A>
-{
-public:
- ThreadsLoadCommandsAtom(Writer<A>& writer)
- : LoadCommandAtom<A>(writer, Segment::fgTextSegment) {}
- virtual const char* getDisplayName() const { return "thread load commands"; }
- virtual uint64_t getSize() const;
- virtual void copyRawContent(uint8_t buffer[]) const;
-private:
- using WriterAtom<A>::fWriter;
- typedef typename A::P P;
- uint8_t* fBuffer;
- uint32_t fBufferSize;
-};
-
-template <typename A>
-class DyldLoadCommandsAtom : public LoadCommandAtom<A>
-{
-public:
- DyldLoadCommandsAtom(Writer<A>& writer) : LoadCommandAtom<A>(writer, Segment::fgTextSegment) {}
- virtual const char* getDisplayName() const { return "dyld load command"; }
- virtual uint64_t getSize() const;
- virtual void copyRawContent(uint8_t buffer[]) const;
-private:
- using WriterAtom<A>::fWriter;
- typedef typename A::P P;
-};
-
-template <typename A>
-class SegmentSplitInfoLoadCommandsAtom : public LoadCommandAtom<A>
-{
-public:
- SegmentSplitInfoLoadCommandsAtom(Writer<A>& writer) : LoadCommandAtom<A>(writer, Segment::fgTextSegment) {}
- virtual const char* getDisplayName() const { return "segment split info load command"; }
- virtual uint64_t getSize() const;
- virtual void copyRawContent(uint8_t buffer[]) const;
-private:
- using WriterAtom<A>::fWriter;
- typedef typename A::P P;
-};
-
-template <typename A>
-class AllowableClientLoadCommandsAtom : public LoadCommandAtom<A>
-{
-public:
- AllowableClientLoadCommandsAtom(Writer<A>& writer, const char* client) :
- LoadCommandAtom<A>(writer, Segment::fgTextSegment), clientString(client) {}
- virtual const char* getDisplayName() const { return "allowable_client load command"; }
- virtual uint64_t getSize() const;
- virtual void copyRawContent(uint8_t buffer[]) const;
-private:
- using WriterAtom<A>::fWriter;
- typedef typename A::P P;
- const char* clientString;
-};
-
-template <typename A>
-class DylibLoadCommandsAtom : public LoadCommandAtom<A>
-{
-public:
- DylibLoadCommandsAtom(Writer<A>& writer, ExecutableFile::DyLibUsed& info)
- : LoadCommandAtom<A>(writer, Segment::fgTextSegment), fInfo(info),
- fOptimizedAway(false) { if (fInfo.options.fLazyLoad) this->fOrdinal += 256; }
- virtual const char* getDisplayName() const { return "dylib load command"; }
- virtual uint64_t getSize() const;
- virtual void copyRawContent(uint8_t buffer[]) const;
- virtual void optimizeAway() { fOptimizedAway = true; }
- bool linkedWeak() { return fInfo.options.fWeakImport; }
-private:
- using WriterAtom<A>::fWriter;
- typedef typename A::P P;
- ExecutableFile::DyLibUsed fInfo;
- bool fOptimizedAway;
-};
-
-template <typename A>
-class DylibIDLoadCommandsAtom : public LoadCommandAtom<A>
-{
-public:
- DylibIDLoadCommandsAtom(Writer<A>& writer) : LoadCommandAtom<A>(writer, Segment::fgTextSegment) {}
- virtual const char* getDisplayName() const { return "dylib ID load command"; }
- virtual uint64_t getSize() const;
- virtual void copyRawContent(uint8_t buffer[]) const;
-private:
- using WriterAtom<A>::fWriter;
- typedef typename A::P P;
-};
-
-template <typename A>
-class RoutinesLoadCommandsAtom : public LoadCommandAtom<A>
-{
-public:
- RoutinesLoadCommandsAtom(Writer<A>& writer) : LoadCommandAtom<A>(writer, Segment::fgTextSegment) {}
- virtual const char* getDisplayName() const { return "routines load command"; }
- virtual uint64_t getSize() const { return sizeof(macho_routines_command<typename A::P>); }
- virtual void copyRawContent(uint8_t buffer[]) const;
-private:
- using WriterAtom<A>::fWriter;
- typedef typename A::P P;
-};
-
-template <typename A>
-class SubUmbrellaLoadCommandsAtom : public LoadCommandAtom<A>
-{
-public:
- SubUmbrellaLoadCommandsAtom(Writer<A>& writer, const char* name)
- : LoadCommandAtom<A>(writer, Segment::fgTextSegment), fName(name) {}
- virtual const char* getDisplayName() const { return "sub-umbrella load command"; }
- virtual uint64_t getSize() const;
- virtual void copyRawContent(uint8_t buffer[]) const;
-private:
- typedef typename A::P P;
- const char* fName;
-};
-
-template <typename A>
-class SubLibraryLoadCommandsAtom : public LoadCommandAtom<A>
-{
-public:
- SubLibraryLoadCommandsAtom(Writer<A>& writer, const char* nameStart, int nameLen)
- : LoadCommandAtom<A>(writer, Segment::fgTextSegment), fNameStart(nameStart), fNameLength(nameLen) {}
- virtual const char* getDisplayName() const { return "sub-library load command"; }
- virtual uint64_t getSize() const;
- virtual void copyRawContent(uint8_t buffer[]) const;
-private:
- using WriterAtom<A>::fWriter;
- typedef typename A::P P;
- const char* fNameStart;
- int fNameLength;
-};
-
-template <typename A>
-class UmbrellaLoadCommandsAtom : public LoadCommandAtom<A>
-{
-public:
- UmbrellaLoadCommandsAtom(Writer<A>& writer, const char* name)
- : LoadCommandAtom<A>(writer, Segment::fgTextSegment), fName(name) {}
- virtual const char* getDisplayName() const { return "umbrella load command"; }
- virtual uint64_t getSize() const;
- virtual void copyRawContent(uint8_t buffer[]) const;
-private:
- using WriterAtom<A>::fWriter;
- typedef typename A::P P;
- const char* fName;
-};
-
-template <typename A>
-class UUIDLoadCommandAtom : public LoadCommandAtom<A>
-{
-public:
- UUIDLoadCommandAtom(Writer<A>& writer)
- : LoadCommandAtom<A>(writer, Segment::fgTextSegment), fEmit(false) {}
- virtual const char* getDisplayName() const { return "uuid load command"; }
- virtual uint64_t getSize() const { return fEmit ? sizeof(macho_uuid_command<typename A::P>) : 0; }
- virtual void copyRawContent(uint8_t buffer[]) const;
- virtual void generate();
- void setContent(const uint8_t uuid[16]);
- const uint8_t* getUUID() { return fUUID; }
-private:
- using WriterAtom<A>::fWriter;
- typedef typename A::P P;
- uuid_t fUUID;
- bool fEmit;
-};
-
-
-template <typename A>
-class RPathLoadCommandsAtom : public LoadCommandAtom<A>
-{
-public:
- RPathLoadCommandsAtom(Writer<A>& writer, const char* path)
- : LoadCommandAtom<A>(writer, Segment::fgTextSegment), fPath(path) {}
- virtual const char* getDisplayName() const { return "rpath load command"; }
- virtual uint64_t getSize() const;
- virtual void copyRawContent(uint8_t buffer[]) const;
-private:
- using WriterAtom<A>::fWriter;
- typedef typename A::P P;
- const char* fPath;
-};
-
-template <typename A>
-class EncryptionLoadCommandsAtom : public LoadCommandAtom<A>
-{
-public:
- EncryptionLoadCommandsAtom(Writer<A>& writer)
- : LoadCommandAtom<A>(writer, Segment::fgTextSegment), fStartOffset(0),
- fEndOffset(0) {}
- virtual const char* getDisplayName() const { return "encryption info load command"; }
- virtual uint64_t getSize() const { return sizeof(macho_encryption_info_command<typename A::P>); }
- virtual void copyRawContent(uint8_t buffer[]) const;
- void setStartEncryptionOffset(uint32_t off) { fStartOffset = off; }
- void setEndEncryptionOffset(uint32_t off) { fEndOffset = off; }
-private:
- using WriterAtom<A>::fWriter;
- typedef typename A::P P;
- uint32_t fStartOffset;
- uint32_t fEndOffset;
-};
-
-template <typename A>
-class LoadCommandsPaddingAtom : public WriterAtom<A>
-{
-public:
- LoadCommandsPaddingAtom(Writer<A>& writer)
- : WriterAtom<A>(writer, Segment::fgTextSegment), fSize(0) {}
- virtual const char* getDisplayName() const { return "header padding"; }
- virtual uint64_t getSize() const { return fSize; }
- virtual const char* getSectionName() const { return "._load_cmds_pad"; }
- virtual void copyRawContent(uint8_t buffer[]) const;
-
- void setSize(uint64_t newSize);
-private:
- using WriterAtom<A>::fWriter;
- typedef typename A::P P;
- uint64_t fSize;
-};
-
-template <typename A>
-class LinkEditAtom : public WriterAtom<A>
-{
-public:
- LinkEditAtom(Writer<A>& writer) : WriterAtom<A>(writer, Segment::fgLinkEditSegment), fOrdinal(fgCurrentOrdinal++) {}
- uint64_t getFileOffset() const;
- virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(log2(sizeof(typename A::P::uint_t))); }
- virtual uint32_t getOrdinal() const { return fOrdinal; }
-private:
- uint32_t fOrdinal;
- static uint32_t fgCurrentOrdinal;
-private:
- typedef typename A::P P;
-};
-
-template <typename A> uint32_t LinkEditAtom<A>::fgCurrentOrdinal = 0;
-
-template <typename A>
-class SectionRelocationsLinkEditAtom : public LinkEditAtom<A>
-{
-public:
- SectionRelocationsLinkEditAtom(Writer<A>& writer) : LinkEditAtom<A>(writer) { }
- virtual const char* getDisplayName() const { return "section relocations"; }
- virtual uint64_t getSize() const;
- virtual const char* getSectionName() const { return "._section_relocs"; }
- virtual void copyRawContent(uint8_t buffer[]) const;
-private:
- using WriterAtom<A>::fWriter;
- typedef typename A::P P;
-};
-
-template <typename A>
-class LocalRelocationsLinkEditAtom : public LinkEditAtom<A>
-{
-public:
- LocalRelocationsLinkEditAtom(Writer<A>& writer) : LinkEditAtom<A>(writer) { }
- virtual const char* getDisplayName() const { return "local relocations"; }
- virtual uint64_t getSize() const;
- virtual const char* getSectionName() const { return "._local_relocs"; }
- virtual void copyRawContent(uint8_t buffer[]) const;
-private:
- using WriterAtom<A>::fWriter;
- typedef typename A::P P;
-};
-
-template <typename A>
-class SymbolTableLinkEditAtom : public LinkEditAtom<A>
-{
-public:
- SymbolTableLinkEditAtom(Writer<A>& writer) : LinkEditAtom<A>(writer) { }
- virtual const char* getDisplayName() const { return "symbol table"; }
- virtual uint64_t getSize() const;
- virtual const char* getSectionName() const { return "._symbol_table"; }
- virtual void copyRawContent(uint8_t buffer[]) const;
-private:
- using WriterAtom<A>::fWriter;
- typedef typename A::P P;
-};
-
-template <typename A>
-class ExternalRelocationsLinkEditAtom : public LinkEditAtom<A>
-{
-public:
- ExternalRelocationsLinkEditAtom(Writer<A>& writer) : LinkEditAtom<A>(writer) { }
- virtual const char* getDisplayName() const { return "external relocations"; }
- virtual uint64_t getSize() const;
- virtual const char* getSectionName() const { return "._extern_relocs"; }
- virtual void copyRawContent(uint8_t buffer[]) const;
-private:
- using WriterAtom<A>::fWriter;
- typedef typename A::P P;
-};
-
-struct IndirectEntry {
- uint32_t indirectIndex;
- uint32_t symbolIndex;
-};
-
-
-template <typename A>
-class SegmentSplitInfoContentAtom : public LinkEditAtom<A>
-{
-public:
- SegmentSplitInfoContentAtom(Writer<A>& writer) : LinkEditAtom<A>(writer), fCantEncode(false) { }
- virtual const char* getDisplayName() const { return "split segment info"; }
- virtual uint64_t getSize() const;
- virtual const char* getSectionName() const { return "._split_info"; }
- virtual void copyRawContent(uint8_t buffer[]) const;
- bool canEncode() { return !fCantEncode; }
- void setCantEncode() { fCantEncode = true; }
- void add32bitPointerLocation(const ObjectFile::Atom* atom, uint32_t offset) { fKind1Locations.push_back(AtomAndOffset(atom, offset)); }
- void add64bitPointerLocation(const ObjectFile::Atom* atom, uint32_t offset) { fKind2Locations.push_back(AtomAndOffset(atom, offset)); }
- void addPPCHi16Location(const ObjectFile::Atom* atom, uint32_t offset) { fKind3Locations.push_back(AtomAndOffset(atom, offset)); }
- void add32bitImportLocation(const ObjectFile::Atom* atom, uint32_t offset) { fKind4Locations.push_back(AtomAndOffset(atom, offset)); }
- void encode();
-
-private:
- using WriterAtom<A>::fWriter;
- typedef typename A::P P;
- typedef typename A::P::uint_t pint_t;
- struct AtomAndOffset {
- AtomAndOffset(const ObjectFile::Atom* a, uint32_t off) : atom(a), offset(off) {}
- const ObjectFile::Atom* atom;
- uint32_t offset;
- };
- void uleb128EncodeAddresses(const std::vector<AtomAndOffset>& locations);
-
- std::vector<AtomAndOffset> fKind1Locations;
- std::vector<AtomAndOffset> fKind2Locations;
- std::vector<AtomAndOffset> fKind3Locations;
- std::vector<AtomAndOffset> fKind4Locations;
- std::vector<uint8_t> fEncodedData;
- bool fCantEncode;
-};
-
-template <typename A>
-class IndirectTableLinkEditAtom : public LinkEditAtom<A>
-{
-public:
- IndirectTableLinkEditAtom(Writer<A>& writer) : LinkEditAtom<A>(writer) { }
- virtual const char* getDisplayName() const { return "indirect symbol table"; }
- virtual uint64_t getSize() const;
- virtual const char* getSectionName() const { return "._indirect_syms"; }
- virtual void copyRawContent(uint8_t buffer[]) const;
-
- std::vector<IndirectEntry> fTable;
-
-private:
- using WriterAtom<A>::fWriter;
- typedef typename A::P P;
-};
-
-template <typename A>
-class ModuleInfoLinkEditAtom : public LinkEditAtom<A>
-{
-public:
- ModuleInfoLinkEditAtom(Writer<A>& writer) : LinkEditAtom<A>(writer), fModuleNameOffset(0) { }
- virtual const char* getDisplayName() const { return "module table"; }
- virtual uint64_t getSize() const;
- virtual const char* getSectionName() const { return "._module_info"; }
- virtual void copyRawContent(uint8_t buffer[]) const;
-
- void setName() { fModuleNameOffset = fWriter.fStringsAtom->add("single module"); }
- uint32_t getTableOfContentsFileOffset() const;
- uint32_t getModuleTableFileOffset() const;
- uint32_t getReferencesFileOffset() const;
- uint32_t getReferencesCount() const;
-
-private:
- using WriterAtom<A>::fWriter;
- typedef typename A::P P;
- uint32_t fModuleNameOffset;
-};
-
-
-class CStringEquals
-{
-public:
- bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
-};
-
-template <typename A>
-class StringsLinkEditAtom : public LinkEditAtom<A>
-{
-public:
- StringsLinkEditAtom(Writer<A>& writer);
- virtual const char* getDisplayName() const { return "string pool"; }
- virtual uint64_t getSize() const;
- virtual const char* getSectionName() const { return "._string_pool"; }
- virtual void copyRawContent(uint8_t buffer[]) const;
-
- int32_t add(const char* name);
- int32_t addUnique(const char* name);
- int32_t emptyString() { return 1; }
- const char* stringForIndex(int32_t) const;
-
-private:
- using WriterAtom<A>::fWriter;
- typedef typename A::P P;
- enum { kBufferSize = 0x01000000 };
- typedef __gnu_cxx::hash_map<const char*, int32_t, __gnu_cxx::hash<const char*>, CStringEquals> StringToOffset;
-
- std::vector<char*> fFullBuffers;
- char* fCurrentBuffer;
- uint32_t fCurrentBufferUsed;
- StringToOffset fUniqueStrings;
-};
-
-
-
-template <typename A>
-class UndefinedSymbolProxyAtom : public WriterAtom<A>
-{
-public:
- UndefinedSymbolProxyAtom(Writer<A>& writer, const char* name) : WriterAtom<A>(writer, Segment::fgLinkEditSegment), fName(name) {}
- virtual const char* getName() const { return fName; }
- virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeGlobal; }
- virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ObjectFile::Atom::kExternalDefinition; }
- virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableIn; }
- virtual uint64_t getSize() const { return 0; }
- virtual const char* getSectionName() const { return "._imports"; }
-private:
- using WriterAtom<A>::fWriter;
- typedef typename A::P P;
- const char* fName;
-};
-
-template <typename A>
-class BranchIslandAtom : public WriterAtom<A>
-{
-public:
- BranchIslandAtom(Writer<A>& writer, const char* name, int islandRegion, ObjectFile::Atom& target, uint32_t targetOffset);
- virtual const char* getName() const { return fName; }
- virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; }
- virtual uint64_t getSize() const;
- virtual const char* getSectionName() const { return "__text"; }
- virtual void copyRawContent(uint8_t buffer[]) const;
-private:
- using WriterAtom<A>::fWriter;
- const char* fName;
- ObjectFile::Atom& fTarget;
- uint32_t fTargetOffset;
-};
-
-template <typename A>
-class StubAtom : public WriterAtom<A>
-{
-public:
- StubAtom(Writer<A>& writer, ObjectFile::Atom& target, bool forLazyDylib);
- virtual const char* getName() const { return fName; }
- virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; }
- virtual uint64_t getSize() const;
- virtual ObjectFile::Alignment getAlignment() const;
- virtual const char* getSectionName() const { return "__symbol_stub1"; }
- 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);
- bool pic() const { return fWriter.fSlideable; }
- using WriterAtom<A>::fWriter;
- const char* fName;
- ObjectFile::Atom& fTarget;
- std::vector<ObjectFile::Reference*> fReferences;
- bool fForLazyDylib;
-};
-
-template <typename A>
-class StubHelperAtom : public WriterAtom<A>
-{
-public:
- StubHelperAtom(Writer<A>& writer, ObjectFile::Atom& target, ObjectFile::Atom& lazyPointer, bool forLazyDylib);
- virtual const char* getName() const { return fName; }
- virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; }
- virtual uint64_t getSize() const;
- virtual const char* getSectionName() const { return "__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>
-{
-public:
- LazyPointerAtom(Writer<A>& writer, ObjectFile::Atom& target,
- StubAtom<A>& stub, bool forLazyDylib);
- virtual const char* getName() const { return fName; }
- virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; }
- virtual uint64_t getSize() const { return sizeof(typename A::P::uint_t); }
- virtual const char* getSectionName() const { return fForLazyDylib ? "__ld_symbol_ptr" : "__la_symbol_ptr"; }
- virtual std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(fReferences); }
- virtual void copyRawContent(uint8_t buffer[]) const;
- ObjectFile::Atom* getTarget() { return &fExternalTarget; }
-private:
- using WriterAtom<A>::fWriter;
- static const char* lazyPointerName(const char* importName);
- const char* fName;
- ObjectFile::Atom& fTarget;
- ObjectFile::Atom& fExternalTarget;
- std::vector<ObjectFile::Reference*> fReferences;
- bool fForLazyDylib;
-};
-
-
-template <typename A>
-class NonLazyPointerAtom : public WriterAtom<A>
-{
-public:
- NonLazyPointerAtom(Writer<A>& writer, ObjectFile::Atom& target);
- virtual const char* getName() const { return fName; }
- virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; }
- virtual uint64_t getSize() const { return sizeof(typename A::P::uint_t); }
- virtual const char* getSectionName() const { return "__nl_symbol_ptr"; }
- virtual std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(fReferences); }
- virtual void copyRawContent(uint8_t buffer[]) const;
- ObjectFile::Atom* getTarget() { return &fTarget; }
-private:
- using WriterAtom<A>::fWriter;
- static const char* nonlazyPointerName(const char* importName);
- const char* fName;
- ObjectFile::Atom& fTarget;
- std::vector<ObjectFile::Reference*> fReferences;
-};
-
-
-template <typename A>
-class ObjCInfoAtom : public WriterAtom<A>
-{
-public:
- ObjCInfoAtom(Writer<A>& writer, ObjectFile::Reader::ObjcConstraint objcContraint,
- bool objcReplacementClasses);
- virtual const char* getName() const { return "objc$info"; }
- virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; }
- virtual uint64_t getSize() const { return 8; }
- virtual const char* getSectionName() const;
- virtual void copyRawContent(uint8_t buffer[]) const;
-private:
- Segment& getInfoSegment() const;
- uint32_t fContent[2];
-};
-
-
-template <typename A>
-class WriterReference : public ObjectFile::Reference
-{
-public:
- typedef typename A::ReferenceKinds Kinds;
-
- WriterReference(uint32_t offset, Kinds kind, ObjectFile::Atom* target,
- uint32_t toOffset=0, ObjectFile::Atom* fromTarget=NULL, uint32_t fromOffset=0)
- : fKind(kind), fFixUpOffsetInSrc(offset), fTarget(target),
- fTargetOffset(toOffset), fFromTarget(fromTarget), fFromTargetOffset(fromOffset) {}
-
- virtual ~WriterReference() {}
-
- virtual ObjectFile::Reference::TargetBinding getTargetBinding() const { return ObjectFile::Reference::kBoundDirectly; }
- virtual ObjectFile::Reference::TargetBinding getFromTargetBinding() const { return (fFromTarget != NULL) ? ObjectFile::Reference::kBoundDirectly : ObjectFile::Reference::kDontBind; }
- virtual uint8_t getKind() const { return (uint8_t)fKind; }
- virtual uint64_t getFixUpOffset() const { return fFixUpOffsetInSrc; }
- virtual const char* getTargetName() const { return fTarget->getName(); }
- virtual ObjectFile::Atom& getTarget() const { return *fTarget; }
- virtual uint64_t getTargetOffset() const { return fTargetOffset; }
- virtual ObjectFile::Atom& getFromTarget() const { return *fFromTarget; }
- virtual const char* getFromTargetName() const { return fFromTarget->getName(); }
- virtual void setTarget(ObjectFile::Atom& target, uint64_t offset) { fTarget = ⌖ fTargetOffset = offset; }
- virtual void setFromTarget(ObjectFile::Atom& target) { fFromTarget = ⌖ }
- virtual void setFromTargetName(const char* name) { }
- virtual void setFromTargetOffset(uint64_t offset) { fFromTargetOffset = offset; }
- virtual const char* getDescription() const { return "writer reference"; }
- virtual uint64_t getFromTargetOffset() const { return fFromTargetOffset; }
-
-private:
- Kinds fKind;
- uint32_t fFixUpOffsetInSrc;
- ObjectFile::Atom* fTarget;
- uint32_t fTargetOffset;
- ObjectFile::Atom* fFromTarget;
- uint32_t fFromTargetOffset;
-};
-
-
-
-template <>
-StubHelperAtom<x86_64>::StubHelperAtom(Writer<x86_64>& writer, ObjectFile::Atom& target,
- ObjectFile::Atom& lazyPointer, bool forLazyDylib)
- : 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));
- if ( forLazyDylib ) {
- if ( writer.fDyldLazyDylibHelper == NULL )
- throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)";
- fReferences.push_back(new WriterReference<x86_64>(8, x86_64::kPCRel32, writer.fDyldLazyDylibHelper));
- }
- else {
- if ( writer.fDyldHelper == NULL )
- throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)";
- fReferences.push_back(new WriterReference<x86_64>(8, x86_64::kPCRel32, writer.fDyldHelper));
- }
-}
-
-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, StubAtom<x86_64>& stub, bool forLazyDylib)
- : WriterAtom<x86_64>(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target),
- fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib)
-{
- if ( forLazyDylib )
- writer.fAllSynthesizedLazyDylibPointers.push_back(this);
- else
- writer.fAllSynthesizedLazyPointers.push_back(this);
-
- StubHelperAtom<x86_64>* helper = new StubHelperAtom<x86_64>(writer, target, *this, forLazyDylib);
- fReferences.push_back(new WriterReference<x86_64>(0, x86_64::kPointer, helper));
-}
-
-// specialize lazy pointer for x86 to initially pointer to second half of stub
-template <>
-LazyPointerAtom<x86>::LazyPointerAtom(Writer<x86>& writer, ObjectFile::Atom& target, StubAtom<x86>& stub, bool forLazyDylib)
- : WriterAtom<x86>(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target),
- fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib)
-{
- if ( forLazyDylib )
- writer.fAllSynthesizedLazyDylibPointers.push_back(this);
- else
- writer.fAllSynthesizedLazyPointers.push_back(this);
-
- // helper part of stub is 14 or 6 bytes into stub
- fReferences.push_back(new WriterReference<x86>(0, x86::kPointer, &stub, writer.fSlideable ? 14 : 6));
-}
-
-template <typename A>
-LazyPointerAtom<A>::LazyPointerAtom(Writer<A>& writer, ObjectFile::Atom& target, StubAtom<A>& stub, bool forLazyDylib)
- : WriterAtom<A>(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target),
- fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib)
-{
- if ( forLazyDylib )
- writer.fAllSynthesizedLazyDylibPointers.push_back(this);
- else
- writer.fAllSynthesizedLazyPointers.push_back(this);
-
- fReferences.push_back(new WriterReference<A>(0, A::kPointer, &target));
-}
-
-
-
-template <typename A>
-const char* LazyPointerAtom<A>::lazyPointerName(const char* name)
-{
- char* buf;
- asprintf(&buf, "%s$lazy_pointer", name);
- return buf;
-}
-
-template <typename A>
-void LazyPointerAtom<A>::copyRawContent(uint8_t buffer[]) const
-{
- bzero(buffer, getSize());
-}
-
-
-template <typename A>
-NonLazyPointerAtom<A>::NonLazyPointerAtom(Writer<A>& writer, ObjectFile::Atom& target)
- : WriterAtom<A>(writer, Segment::fgDataSegment), fName(nonlazyPointerName(target.getName())), fTarget(target)
-{
- writer.fAllSynthesizedNonLazyPointers.push_back(this);
-
- fReferences.push_back(new WriterReference<A>(0, A::kPointer, &target));
-}
-
-template <typename A>
-const char* NonLazyPointerAtom<A>::nonlazyPointerName(const char* name)
-{
- char* buf;
- asprintf(&buf, "%s$non_lazy_pointer", name);
- return buf;
-}
-
-template <typename A>
-void NonLazyPointerAtom<A>::copyRawContent(uint8_t buffer[]) const
-{
- bzero(buffer, getSize());
-}
-
-
-
-template <>
-bool StubAtom<ppc64>::pic() const
-{
- // no-pic stubs for ppc64 don't work if lazy pointer is above low 2GB.
- // Usually that only happens if page zero is very large
- return ( fWriter.fSlideable || ((fWriter.fPageZeroAtom != NULL) && (fWriter.fPageZeroAtom->getSize() > 4096)) );
-}
-
-
-template <>
-bool StubAtom<arm>::pic() const
-{
- return fWriter.fSlideable;
-}
-
-template <>
-ObjectFile::Alignment StubAtom<ppc>::getAlignment() const
-{
- return 2;
-}
-
-template <>
-ObjectFile::Alignment StubAtom<ppc64>::getAlignment() const
-{
- return 2;
-}
-
-template <>
-ObjectFile::Alignment StubAtom<arm>::getAlignment() const
-{
- return 2;
-}
-
-template <>
-StubAtom<ppc>::StubAtom(Writer<ppc>& writer, ObjectFile::Atom& target, bool forLazyDylib)
- : WriterAtom<ppc>(writer, Segment::fgTextSegment), fName(stubName(target.getName())),
- fTarget(target), fForLazyDylib(forLazyDylib)
-{
- writer.fAllSynthesizedStubs.push_back(this);
- LazyPointerAtom<ppc>* lp;
- if ( fWriter.fOptions.prebind() ) {
- // for prebound ppc, lazy pointer starts out pointing to target symbol's address
- // if target is a weak definition within this linkage unit or zero if in some dylib
- lp = new LazyPointerAtom<ppc>(writer, target, *this, forLazyDylib);
- }
- else {
- // for non-prebound ppc, lazy pointer starts out pointing to dyld_stub_binding_helper glue code
- if ( forLazyDylib ) {
- if ( writer.fDyldLazyDylibHelper == NULL )
- throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)";
- lp = new LazyPointerAtom<ppc>(writer, *writer.fDyldLazyDylibHelper, *this, forLazyDylib);
- }
- else {
- if ( writer.fDyldHelper == NULL )
- throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)";
- lp = new LazyPointerAtom<ppc>(writer, *writer.fDyldHelper, *this, forLazyDylib);
- }
- }
- if ( pic() ) {
- // picbase is 8 bytes into atom
- fReferences.push_back(new WriterReference<ppc>(12, ppc::kPICBaseHigh16, lp, 0, this, 8));
- fReferences.push_back(new WriterReference<ppc>(20, ppc::kPICBaseLow16, lp, 0, this, 8));
- }
- else {
- fReferences.push_back(new WriterReference<ppc>(0, ppc::kAbsHigh16AddLow, lp));
- fReferences.push_back(new WriterReference<ppc>(4, ppc::kAbsLow16, lp));
- }
-}
-
-template <>
-StubAtom<ppc64>::StubAtom(Writer<ppc64>& writer, ObjectFile::Atom& target, bool forLazyDylib)
- : WriterAtom<ppc64>(writer, Segment::fgTextSegment), fName(stubName(target.getName())),
- fTarget(target), fForLazyDylib(forLazyDylib)
-{
- writer.fAllSynthesizedStubs.push_back(this);
-
- LazyPointerAtom<ppc64>* lp;
- if ( forLazyDylib ) {
- if ( writer.fDyldLazyDylibHelper == NULL )
- throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)";
- lp = new LazyPointerAtom<ppc64>(writer, *writer.fDyldLazyDylibHelper, *this, forLazyDylib);
- }
- else {
- if ( writer.fDyldHelper == NULL )
- throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)";
- lp = new LazyPointerAtom<ppc64>(writer, *writer.fDyldHelper, *this, forLazyDylib);
- }
- if ( pic() ) {
- // picbase is 8 bytes into atom
- fReferences.push_back(new WriterReference<ppc64>(12, ppc64::kPICBaseHigh16, lp, 0, this, 8));
- fReferences.push_back(new WriterReference<ppc64>(20, ppc64::kPICBaseLow14, lp, 0, this, 8));
- }
- else {
- fReferences.push_back(new WriterReference<ppc64>(0, ppc64::kAbsHigh16AddLow, lp));
- fReferences.push_back(new WriterReference<ppc64>(4, ppc64::kAbsLow14, lp));
- }
-}
-
-// specialize to put x86 fast stub in __IMPORT segment with no lazy pointer
-template <>
-StubAtom<x86>::StubAtom(Writer<x86>& writer, ObjectFile::Atom& target, bool forLazyDylib)
- : WriterAtom<x86>(writer, (writer.fOptions.slowx86Stubs() || forLazyDylib) ? Segment::fgTextSegment :
- ( writer.fOptions.readOnlyx86Stubs() ? Segment::fgROImportSegment : Segment::fgImportSegment)),
- fTarget(target), fForLazyDylib(forLazyDylib)
-{
- if ( writer.fOptions.slowx86Stubs() || forLazyDylib ) {
- fName = stubName(target.getName());
- writer.fAllSynthesizedStubs.push_back(this);
- LazyPointerAtom<x86>* lp = new LazyPointerAtom<x86>(writer, target, *this, forLazyDylib);
- ObjectFile::Atom* helper;
- if ( forLazyDylib ) {
- if ( writer.fDyldLazyDylibHelper == NULL )
- throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)";
- helper = writer.fDyldLazyDylibHelper;
- }
- else {
- if ( writer.fDyldHelper == NULL )
- throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)";
- helper = writer.fDyldHelper;
- }
- if ( pic() ) {
- // picbase is 5 bytes into atom
- fReferences.push_back(new WriterReference<x86>(8, x86::kPointerDiff, lp, 0, this, 5));
- fReferences.push_back(new WriterReference<x86>(16, x86::kPCRel32, helper));
- }
- else {
- fReferences.push_back(new WriterReference<x86>(2, x86::kAbsolute32, lp));
- fReferences.push_back(new WriterReference<x86>(7, x86::kAbsolute32, lp));
- fReferences.push_back(new WriterReference<x86>(12, x86::kPCRel32, helper));
- }
- }
- else {
- if ( &target == NULL )
- fName = "cache-line-crossing-stub";
- else {
- fName = stubName(target.getName());
- writer.fAllSynthesizedStubs.push_back(this);
- }
- }
-}
-
-template <>
-StubAtom<x86_64>::StubAtom(Writer<x86_64>& writer, ObjectFile::Atom& target, bool forLazyDylib)
- : 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, *this, forLazyDylib);
- fReferences.push_back(new WriterReference<x86_64>(2, x86_64::kPCRel32, lp));
-}
-
-template <>
-StubAtom<arm>::StubAtom(Writer<arm>& writer, ObjectFile::Atom& target, bool forLazyDylib)
- : WriterAtom<arm>(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target)
-{
- writer.fAllSynthesizedStubs.push_back(this);
-
- LazyPointerAtom<arm>* lp;
- if ( fWriter.fOptions.prebind() && !forLazyDylib ) {
- // for prebound arm, lazy pointer starts out pointing to target symbol's address
- // if target is a weak definition within this linkage unit or zero if in some dylib
- lp = new LazyPointerAtom<arm>(writer, target, *this, forLazyDylib);
- }
- else {
- // for non-prebound arm, lazy pointer starts out pointing to dyld_stub_binding_helper glue code
- ObjectFile::Atom* helper;
- if ( forLazyDylib ) {
- if ( writer.fDyldLazyDylibHelper == NULL )
- throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)";
- helper = writer.fDyldLazyDylibHelper;
- }
- else {
- if ( writer.fDyldHelper == NULL )
- throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)";
- helper = writer.fDyldHelper;
- }
- lp = new LazyPointerAtom<arm>(writer, *helper, *this, forLazyDylib);
- }
- if ( pic() )
- fReferences.push_back(new WriterReference<arm>(12, arm::kPointerDiff, lp, 0, this, 12));
- else
- fReferences.push_back(new WriterReference<arm>(8, arm::kPointer, lp));
-}
-
-template <typename A>
-const char* StubAtom<A>::stubName(const char* name)
-{
- char* buf;
- asprintf(&buf, "%s$stub", name);
- return buf;
-}
-
-template <>
-uint64_t StubAtom<ppc>::getSize() const
-{
- return ( pic() ? 32 : 16 );
-}
-
-template <>
-uint64_t StubAtom<ppc64>::getSize() const
-{
- return ( pic() ? 32 : 16 );
-}
-
-
-template <>
-uint64_t StubAtom<arm>::getSize() const
-{
- return ( pic() ? 16 : 12 );
-}
-
-template <>
-uint64_t StubAtom<x86>::getSize() const
-{
- if ( fWriter.fOptions.slowx86Stubs() || fForLazyDylib ) {
- if ( pic() )
- return 20;
- else
- return 16;
- }
- return 5;
-}
-
-template <>
-uint64_t StubAtom<x86_64>::getSize() const
-{
- return 6;
-}
-
-template <>
-ObjectFile::Alignment StubAtom<x86>::getAlignment() const
-{
- if ( fWriter.fOptions.slowx86Stubs() || fForLazyDylib )
- return 2;
- else
- return 0; // special case x86 fast stubs to be byte aligned
-}
-
-template <>
-void StubAtom<ppc64>::copyRawContent(uint8_t buffer[]) const
-{
- if ( pic() ) {
- OSWriteBigInt32(&buffer [0], 0, 0x7c0802a6); // mflr r0
- OSWriteBigInt32(&buffer[ 4], 0, 0x429f0005); // bcl 20,31,Lpicbase
- OSWriteBigInt32(&buffer[ 8], 0, 0x7d6802a6); // Lpicbase: mflr r11
- OSWriteBigInt32(&buffer[12], 0, 0x3d6b0000); // addis r11,r11,ha16(L_fwrite$lazy_ptr-Lpicbase)
- OSWriteBigInt32(&buffer[16], 0, 0x7c0803a6); // mtlr r0
- OSWriteBigInt32(&buffer[20], 0, 0xe98b0001); // ldu r12,lo16(L_fwrite$lazy_ptr-Lpicbase)(r11)
- OSWriteBigInt32(&buffer[24], 0, 0x7d8903a6); // mtctr r12
- OSWriteBigInt32(&buffer[28], 0, 0x4e800420); // bctr
- }
- else {
- OSWriteBigInt32(&buffer[ 0], 0, 0x3d600000); // lis r11,ha16(L_fwrite$lazy_ptr)
- OSWriteBigInt32(&buffer[ 4], 0, 0xe98b0001); // ldu r12,lo16(L_fwrite$lazy_ptr)(r11)
- OSWriteBigInt32(&buffer[ 8], 0, 0x7d8903a6); // mtctr r12
- OSWriteBigInt32(&buffer[12], 0, 0x4e800420); // bctr
- }
-}
-
-template <>
-void StubAtom<ppc>::copyRawContent(uint8_t buffer[]) const
-{
- if ( pic() ) {
- OSWriteBigInt32(&buffer[ 0], 0, 0x7c0802a6); // mflr r0
- OSWriteBigInt32(&buffer[ 4], 0, 0x429f0005); // bcl 20,31,Lpicbase
- OSWriteBigInt32(&buffer[ 8], 0, 0x7d6802a6); // Lpicbase: mflr r11
- OSWriteBigInt32(&buffer[12], 0, 0x3d6b0000); // addis r11,r11,ha16(L_fwrite$lazy_ptr-Lpicbase)
- OSWriteBigInt32(&buffer[16], 0, 0x7c0803a6); // mtlr r0
- OSWriteBigInt32(&buffer[20], 0, 0x858b0000); // lwzu r12,lo16(L_fwrite$lazy_ptr-Lpicbase)(r11)
- OSWriteBigInt32(&buffer[24], 0, 0x7d8903a6); // mtctr r12
- OSWriteBigInt32(&buffer[28], 0, 0x4e800420); // bctr
- }
- else {
- OSWriteBigInt32(&buffer[ 0], 0, 0x3d600000); // lis r11,ha16(L_fwrite$lazy_ptr)
- OSWriteBigInt32(&buffer[ 4], 0, 0x858b0000); // lwzu r12,lo16(L_fwrite$lazy_ptr)(r11)
- OSWriteBigInt32(&buffer[ 8], 0, 0x7d8903a6); // mtctr r12
- OSWriteBigInt32(&buffer[12], 0, 0x4e800420); // bctr
- }
-}
-
-template <>
-void StubAtom<x86>::copyRawContent(uint8_t buffer[]) const
-{
- if ( fWriter.fOptions.slowx86Stubs() || fForLazyDylib ) {
- if ( pic() ) {
- buffer[0] = 0xE8; // call picbase
- buffer[1] = 0x00;
- buffer[2] = 0x00;
- buffer[3] = 0x00;
- buffer[4] = 0x00;
- buffer[5] = 0x58; // pop eax
- buffer[6] = 0x8D; // lea foo$lazy_pointer-picbase(eax),eax
- buffer[7] = 0x80;
- buffer[8] = 0x00;
- buffer[9] = 0x00;
- buffer[10] = 0x00;
- buffer[11] = 0x00;
- buffer[12] = 0xFF; // jmp *(eax)
- buffer[13] = 0x20;
- buffer[14] = 0x50; // push eax
- buffer[15] = 0xE9; // jump dyld_stub_binding_helper
- buffer[16] = 0x00;
- buffer[17] = 0x00;
- buffer[18] = 0x00;
- buffer[19] = 0x00;
- }
- else {
- buffer[0] = 0xFF; // jmp *foo$lazy_pointer
- buffer[1] = 0x25;
- buffer[2] = 0x00;
- buffer[3] = 0x00;
- buffer[4] = 0x00;
- buffer[5] = 0x00;
- buffer[6] = 0x68; // pushl $foo$lazy_pointer
- buffer[7] = 0x00;
- buffer[8] = 0x00;
- buffer[9] = 0x00;
- buffer[10] = 0x00;
- buffer[11] = 0xE9; // jump dyld_stub_binding_helper
- buffer[12] = 0x00;
- buffer[13] = 0x00;
- buffer[14] = 0x00;
- buffer[15] = 0x00;
- }
- }
- else {
- if ( fWriter.fOptions.prebind() ) {
- uint32_t address = this->getAddress();
- int32_t rel32 = 0 - (address+5);
- buffer[0] = 0xE9;
- buffer[1] = rel32 & 0xFF;
- buffer[2] = (rel32 >> 8) & 0xFF;
- buffer[3] = (rel32 >> 16) & 0xFF;
- buffer[4] = (rel32 >> 24) & 0xFF;
- }
- else {
- buffer[0] = 0xF4;
- buffer[1] = 0xF4;
- buffer[2] = 0xF4;
- buffer[3] = 0xF4;
- 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;
-}
-
-template <>
-void StubAtom<arm>::copyRawContent(uint8_t buffer[]) const
-{
- if ( pic() ) {
- OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc004); // ldr ip, pc + 12
- OSWriteLittleInt32(&buffer[ 4], 0, 0xe08fc00c); // add ip, pc, ip
- OSWriteLittleInt32(&buffer[ 8], 0, 0xe59cf000); // ldr pc, [ip]
- OSWriteLittleInt32(&buffer[12], 0, 0x00000000); // .long L_foo$lazy_ptr - (L1$scv + 8)
- }
- else {
- OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc000); // ldr ip, [pc, #0]
- OSWriteLittleInt32(&buffer[ 4], 0, 0xe59cf000); // ldr pc, [ip]
- OSWriteLittleInt32(&buffer[ 8], 0, 0x00000000); // .long L_foo$lazy_ptr
- }
-}
-
-// x86_64 stubs are 7 bytes and need no alignment
-template <>
-ObjectFile::Alignment StubAtom<x86_64>::getAlignment() const
-{
- return 0;
-}
-
-template <>
-const char* StubAtom<ppc>::getSectionName() const
-{
- return ( pic() ? "__picsymbolstub1" : "__symbol_stub1");
-}
-
-template <>
-const char* StubAtom<ppc64>::getSectionName() const
-{
- return ( pic() ? "__picsymbolstub1" : "__symbol_stub1");
-}
-
-template <>
-const char* StubAtom<arm>::getSectionName() const
-{
- return ( pic() ? "__picsymbolstub4" : "__symbol_stub4");
-}
-
-template <>
-const char* StubAtom<x86>::getSectionName() const
-{
- if ( fWriter.fOptions.slowx86Stubs() || fForLazyDylib ) {
- if ( pic() )
- return "__picsymbol_stub";
- else
- return "__symbol_stub";
- }
- return "__jump_table";
-}
-
-
-
-
-struct AtomByNameSorter
-{
- bool operator()(ObjectFile::Atom* left, ObjectFile::Atom* right)
- {
- return (strcmp(left->getName(), right->getName()) < 0);
- }
-};
-
-template <typename P>
-struct ExternalRelocSorter
-{
- bool operator()(const macho_relocation_info<P>& left, const macho_relocation_info<P>& right)
- {
- // sort first by symbol number
- if ( left.r_symbolnum() != right.r_symbolnum() )
- return (left.r_symbolnum() < right.r_symbolnum());
- // then sort all uses of the same symbol by address
- return (left.r_address() < right.r_address());
- }
-};
-
-
-template <typename A>
-Writer<A>::Writer(const char* path, Options& options, std::vector<ExecutableFile::DyLibUsed>& dynamicLibraries)
- : ExecutableFile::Writer(dynamicLibraries), fFilePath(strdup(path)), fOptions(options),
- fAllAtoms(NULL), fStabs(NULL), fLoadCommandsSection(NULL),
- fLoadCommandsSegment(NULL), fEncryptionLoadCommand(NULL), fSegmentCommands(NULL),
- fSymbolTableCommands(NULL), fHeaderPadding(NULL),
- fUUIDAtom(NULL), fPadSegmentInfo(NULL), fEntryPoint( NULL), fDyldHelper(NULL), fDyldLazyDylibHelper(NULL),
- fSectionRelocationsAtom(NULL), fLocalRelocationsAtom(NULL), fExternalRelocationsAtom(NULL),
- fSymbolTableAtom(NULL), fSplitCodeToDataContentAtom(NULL), fIndirectTableAtom(NULL), fModuleInfoAtom(NULL),
- fStringsAtom(NULL), fPageZeroAtom(NULL), fSymbolTable(NULL), fSymbolTableCount(0), fSymbolTableStabsCount(0),
- fSymbolTableLocalCount(0), fSymbolTableExportCount(0), fSymbolTableImportCount(0),
- fLargestAtomSize(1),
- fEmitVirtualSections(false), fHasWeakExports(false), fReferencesWeakImports(false),
- fCanScatter(false), fWritableSegmentPastFirst4GB(false), fNoReExportedDylibs(false),
- fBiggerThanTwoGigs(false), fSlideable(false),
- fFirstWritableSegment(NULL), fAnonNameIndex(1000)
-{
- switch ( fOptions.outputKind() ) {
- case Options::kDynamicExecutable:
- case Options::kStaticExecutable:
- if ( fOptions.zeroPageSize() != 0 )
- 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));
- fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom<A>(*this));
- fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom<A>(*this));
- if ( fOptions.outputKind() == Options::kDynamicExecutable )
- fWriterSynthesizedAtoms.push_back(new DyldLoadCommandsAtom<A>(*this));
- fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom<A>(*this));
- fWriterSynthesizedAtoms.push_back(new ThreadsLoadCommandsAtom<A>(*this));
- if ( fOptions.hasCustomStack() )
- fWriterSynthesizedAtoms.push_back(new CustomStackAtom<A>(*this));
- fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom<A>(*this));
- fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom<A>(*this));
- fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom<A>(*this));
- fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom<A>(*this));
- fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom<A>(*this));
- fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom<A>(*this));
- fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom<A>(*this));
- break;
- case Options::kDynamicLibrary:
- case Options::kDynamicBundle:
- fWriterSynthesizedAtoms.push_back(new DsoHandleAtom<A>(*this));
- // fall through
- case Options::kObjectFile:
- fWriterSynthesizedAtoms.push_back(new MachHeaderAtom<A>(*this));
- fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom<A>(*this));
- if ( fOptions.outputKind() == Options::kDynamicLibrary ) {
- fWriterSynthesizedAtoms.push_back(new DylibIDLoadCommandsAtom<A>(*this));
- if ( fOptions.initFunctionName() != NULL )
- fWriterSynthesizedAtoms.push_back(new RoutinesLoadCommandsAtom<A>(*this));
- }
- fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom<A>(*this));
- fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom<A>(*this));
- if ( fOptions.sharedRegionEligible() )
- fWriterSynthesizedAtoms.push_back(new SegmentSplitInfoLoadCommandsAtom<A>(*this));
- fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom<A>(*this));
- fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom<A>(*this));
- fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom<A>(*this));
- if ( fOptions.sharedRegionEligible() ) {
- fWriterSynthesizedAtoms.push_back(fSplitCodeToDataContentAtom = new SegmentSplitInfoContentAtom<A>(*this));
- }
- fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom<A>(*this));
- fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom<A>(*this));
- fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom<A>(*this));
- if ( this->needsModuleTable() )
- fWriterSynthesizedAtoms.push_back(fModuleInfoAtom = new ModuleInfoLinkEditAtom<A>(*this));
- fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom<A>(*this));
- break;
- case Options::kDyld:
- fWriterSynthesizedAtoms.push_back(new DsoHandleAtom<A>(*this));
- fWriterSynthesizedAtoms.push_back(new MachHeaderAtom<A>(*this));
- fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom<A>(*this));
- fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom<A>(*this));
- fWriterSynthesizedAtoms.push_back(new DyldLoadCommandsAtom<A>(*this));
- fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom<A>(*this));
- fWriterSynthesizedAtoms.push_back(new ThreadsLoadCommandsAtom<A>(*this));
- fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom<A>(*this));
- fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom<A>(*this));
- fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom<A>(*this));
- fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom<A>(*this));
- fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom<A>(*this));
- fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom<A>(*this));
- break;
- }
-
- // add extra commmands
- bool hasReExports = false;
- uint32_t ordinal = 1;
- switch ( fOptions.outputKind() ) {
- case Options::kDynamicExecutable:
- if ( fOptions.makeEncryptable() ) {
- fEncryptionLoadCommand = new EncryptionLoadCommandsAtom<A>(*this);
- fWriterSynthesizedAtoms.push_back(fEncryptionLoadCommand);
- }
- // fall through
- case Options::kDynamicLibrary:
- case Options::kDynamicBundle:
- {
- // add dylib load command atoms for all dynamic libraries
- const unsigned int libCount = dynamicLibraries.size();
- for (unsigned int i=0; i < libCount; ++i) {
- ExecutableFile::DyLibUsed& dylibInfo = dynamicLibraries[i];
- //fprintf(stderr, "dynamicLibraries[%d]: reader=%p, %s, install=%s\n", i, dylibInfo.reader, dylibInfo.reader->getPath(), dylibInfo.reader->getInstallPath() );
-
- if ( dylibInfo.options.fReExport ) {
- hasReExports = true;
- }
- else {
- const char* parentUmbrella = dylibInfo.reader->parentUmbrella();
- if ( (parentUmbrella != NULL) && (fOptions.outputKind() == Options::kDynamicLibrary) ) {
- const char* thisIDLastSlash = strrchr(fOptions.installPath(), '/');
- if ( (thisIDLastSlash != NULL) && (strcmp(&thisIDLastSlash[1], parentUmbrella) == 0) )
- hasReExports = true;
- }
- }
-
- if ( dylibInfo.options.fBundleLoader ) {
- fLibraryToOrdinal[dylibInfo.reader] = EXECUTABLE_ORDINAL;
- }
- else {
- // see if a DylibLoadCommandsAtom has already been created for this install path
- bool newDylib = true;
- const char* dylibInstallPath = dylibInfo.reader->getInstallPath();
- for (unsigned int seenLib=0; seenLib < i; ++seenLib) {
- ExecutableFile::DyLibUsed& seenDylibInfo = dynamicLibraries[seenLib];
- if ( !seenDylibInfo.options.fBundleLoader ) {
- const char* seenDylibInstallPath = seenDylibInfo.reader->getInstallPath();
- if ( strcmp(seenDylibInstallPath, dylibInstallPath) == 0 ) {
- fLibraryToOrdinal[dylibInfo.reader] = fLibraryToOrdinal[seenDylibInfo.reader];
- fLibraryToLoadCommand[dylibInfo.reader] = fLibraryToLoadCommand[seenDylibInfo.reader];
- fLibraryAliases[dylibInfo.reader] = seenDylibInfo.reader;
- newDylib = false;
- break;
- }
- }
- }
-
- if ( newDylib ) {
- // assign new ordinal and check for other paired load commands
- fLibraryToOrdinal[dylibInfo.reader] = ordinal++;
- DylibLoadCommandsAtom<A>* dyliblc = new DylibLoadCommandsAtom<A>(*this, dylibInfo);
- fLibraryToLoadCommand[dylibInfo.reader] = dyliblc;
- fWriterSynthesizedAtoms.push_back(dyliblc);
- if ( dylibInfo.options.fReExport
- && (fOptions.macosxVersionMin() < ObjectFile::ReaderOptions::k10_5)
- && (fOptions.outputKind() == Options::kDynamicLibrary) ) {
- // see if child has sub-framework that is this
- bool isSubFramework = false;
- const char* childInUmbrella = dylibInfo.reader->parentUmbrella();
- if ( childInUmbrella != NULL ) {
- const char* myLeaf = strrchr(fOptions.installPath(), '/');
- if ( myLeaf != NULL ) {
- if ( strcmp(childInUmbrella, &myLeaf[1]) == 0 )
- isSubFramework = true;
- }
- }
- // LC_SUB_FRAMEWORK is in child, so do nothing in parent
- if ( ! isSubFramework ) {
- // this dylib also needs a sub_x load command
- bool isFrameworkReExport = false;
- const char* lastSlash = strrchr(dylibInstallPath, '/');
- if ( lastSlash != NULL ) {
- char frameworkName[strlen(lastSlash)+20];
- sprintf(frameworkName, "/%s.framework/", &lastSlash[1]);
- isFrameworkReExport = (strstr(dylibInstallPath, frameworkName) != NULL);
- }
- if ( isFrameworkReExport ) {
- // needs a LC_SUB_UMBRELLA command
- fWriterSynthesizedAtoms.push_back(new SubUmbrellaLoadCommandsAtom<A>(*this, &lastSlash[1]));
- }
- else {
- // needs a LC_SUB_LIBRARY command
- const char* nameStart = &lastSlash[1];
- if ( lastSlash == NULL )
- nameStart = dylibInstallPath;
- int len = strlen(nameStart);
- const char* dot = strchr(nameStart, '.');
- if ( dot != NULL )
- len = dot - nameStart;
- fWriterSynthesizedAtoms.push_back(new SubLibraryLoadCommandsAtom<A>(*this, nameStart, len));
- }
- }
- }
- }
- }
- }
- // add umbrella command if needed
- if ( fOptions.umbrellaName() != NULL ) {
- fWriterSynthesizedAtoms.push_back(new UmbrellaLoadCommandsAtom<A>(*this, fOptions.umbrellaName()));
- }
- // add allowable client commands if used
- std::vector<const char*>& allowableClients = fOptions.allowableClients();
- for (std::vector<const char*>::iterator it=allowableClients.begin(); it != allowableClients.end(); ++it)
- fWriterSynthesizedAtoms.push_back(new AllowableClientLoadCommandsAtom<A>(*this, *it));
- }
- break;
- case Options::kStaticExecutable:
- case Options::kObjectFile:
- case Options::kDyld:
- break;
- }
- fNoReExportedDylibs = !hasReExports;
-
- // add any rpath load commands
- for(std::vector<const char*>::const_iterator it=fOptions.rpaths().begin(); it != fOptions.rpaths().end(); ++it) {
- fWriterSynthesizedAtoms.push_back(new RPathLoadCommandsAtom<A>(*this, *it));
- }
-
- // set up fSlideable
- switch ( fOptions.outputKind() ) {
- case Options::kObjectFile:
- case Options::kStaticExecutable:
- fSlideable = false;
- break;
- case Options::kDynamicExecutable:
- fSlideable = fOptions.positionIndependentExecutable();
- break;
- case Options::kDyld:
- case Options::kDynamicLibrary:
- case Options::kDynamicBundle:
- fSlideable = true;
- break;
- }
-
- //fprintf(stderr, "ordinals table:\n");
- //for (std::map<class ObjectFile::Reader*, uint32_t>::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) {
- // fprintf(stderr, "%d <== %s\n", it->second, it->first->getPath());
- //}
-}
-
-template <typename A>
-Writer<A>::~Writer()
-{
- if ( fFilePath != NULL )
- free((void*)fFilePath);
- if ( fSymbolTable != NULL )
- delete [] fSymbolTable;
-}
-
-
-// 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)
-{
- if ( fOptions.outputKind() == Options::kObjectFile ) {
- // when doing -r -exported_symbols_list, don't creat proxy for a symbol
- // that is supposed to be exported. We want an error instead
- // <rdar://problem/5062685> ld does not report error when -r is used and exported symbols are not defined.
- if ( fOptions.hasExportRestrictList() && fOptions.shouldExport(name) )
- return NULL;
- else
- return new UndefinedSymbolProxyAtom<A>(*this, name);
- }
- else if ( (fOptions.undefinedTreatment() != Options::kUndefinedError) || fOptions.allowedUndefined(name) )
- return new UndefinedSymbolProxyAtom<A>(*this, name);
- else
- return NULL;
-}
-
-template <typename A>
-uint8_t Writer<A>::ordinalForLibrary(ObjectFile::Reader* lib)
-{
- // flat namespace images use zero for all ordinals
- if ( fOptions.nameSpace() != Options::kTwoLevelNameSpace )
- return 0;
-
- // is an UndefinedSymbolProxyAtom
- if ( lib == this )
- if ( fOptions.nameSpace() == Options::kTwoLevelNameSpace )
- return DYNAMIC_LOOKUP_ORDINAL;
-
- std::map<class ObjectFile::Reader*, uint32_t>::iterator pos = fLibraryToOrdinal.find(lib);
- if ( pos != fLibraryToOrdinal.end() )
- return pos->second;
-
- throw "can't find ordinal for imported symbol";
-}
-
-template <typename A>
-ObjectFile::Atom& Writer<A>::makeObjcInfoAtom(ObjectFile::Reader::ObjcConstraint objcContraint, bool objcReplacementClasses)
-{
- return *(new ObjCInfoAtom<A>(*this, objcContraint, objcReplacementClasses));
-}
-
-
-template <typename A>
-uint64_t Writer<A>::write(std::vector<class ObjectFile::Atom*>& atoms,
- std::vector<class ObjectFile::Reader::Stab>& stabs,
- class ObjectFile::Atom* entryPointAtom, class ObjectFile::Atom* dyldHelperAtom,
- class ObjectFile::Atom* dyldLazyDylibHelperAtom,
- bool createUUID, bool canScatter, ObjectFile::Reader::CpuConstraint cpuConstraint,
- bool biggerThanTwoGigs, bool overridesDylibWeakDefines)
-{
- fAllAtoms = &atoms;
- fStabs = &stabs;
- fEntryPoint = entryPointAtom;
- fDyldHelper = dyldHelperAtom;
- fDyldLazyDylibHelper = dyldLazyDylibHelperAtom;
- fCanScatter = canScatter;
- fCpuConstraint = cpuConstraint;
- fBiggerThanTwoGigs = biggerThanTwoGigs;
- fHasWeakExports = overridesDylibWeakDefines; // dyld needs to search this image as if it had weak exports
-
- try {
- // Set for create UUID
- if (createUUID)
- fUUIDAtom->generate();
-
- // remove uneeded dylib load commands
- optimizeDylibReferences();
-
- // check for mdynamic-no-pic codegen
- scanForAbsoluteReferences();
-
- // create inter-library stubs
- synthesizeStubs();
-
- // create SegmentInfo and SectionInfo objects and assign all atoms to a section
- partitionIntoSections();
-
- // segment load command can now be sized and padding can be set
- adjustLoadCommandsAndPadding();
-
- // assign each section a file offset
- assignFileOffsets();
-
- // if need to add branch islands, reassign file offsets
- if ( addBranchIslands() )
- assignFileOffsets();
-
- // build symbol table and relocations
- buildLinkEdit();
-
- // write map file if requested
- writeMap();
-
- // write everything
- return writeAtoms();
- } catch (...) {
- // clean up if any errors
- (void)unlink(fFilePath);
- throw;
- }
-}
-
-template <typename A>
-void Writer<A>::buildLinkEdit()
-{
- this->collectExportedAndImportedAndLocalAtoms();
- this->buildSymbolTable();
- this->buildFixups();
- this->adjustLinkEditSections();
-}
-
-
-
-template <typename A>
-uint64_t Writer<A>::getAtomLoadAddress(const ObjectFile::Atom* atom)
-{
- return atom->getAddress();
-// SectionInfo* info = (SectionInfo*)atom->getSection();
-// return info->getBaseAddress() + atom->getSectionOffset();
-}
-
-
-template <>
-const char* Writer<x86_64>::symbolTableName(const ObjectFile::Atom* atom)
-{
- static unsigned int counter = 0;
- const char* name = atom->getName();
- if ( strncmp(name, "cstring=", 8) == 0 )
- asprintf((char**)&name, "LC%u", counter++);
- return name;
-}
-
-template <typename A>
-const char* Writer<A>::symbolTableName(const ObjectFile::Atom* atom)
-{
- return atom->getName();
-}
-
-template <typename A>
-void Writer<A>::setExportNlist(const ObjectFile::Atom* atom, macho_nlist<P>* entry)
-{
- // set n_strx
- entry->set_n_strx(this->fStringsAtom->add(this->symbolTableName(atom)));
-
- // set n_type
- 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);
-
- // the __mh_execute_header is magic and must be an absolute symbol
- if ( (sectionIndex==0)
- && (fOptions.outputKind() == Options::kDynamicExecutable)
- && (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip ))
- entry->set_n_type(N_EXT | N_ABS);
-
- // set n_desc
- uint16_t desc = 0;
- if ( atom->isThumb() )
- desc |= N_ARM_THUMB_DEF;
- if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip )
- desc |= REFERENCED_DYNAMICALLY;
- if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) {
- desc |= N_WEAK_DEF;
- fHasWeakExports = true;
- }
- entry->set_n_desc(desc);
-
- // set n_value ( address this symbol will be at if this executable is loaded at it preferred address )
- if ( atom->getDefinitionKind() == ObjectFile::Atom::kAbsoluteSymbol )
- entry->set_n_value(atom->getSectionOffset());
- else
- entry->set_n_value(this->getAtomLoadAddress(atom));
-}
-
-template <typename A>
-void Writer<A>::setImportNlist(const ObjectFile::Atom* atom, macho_nlist<P>* entry)
-{
- // set n_strx
- entry->set_n_strx(this->fStringsAtom->add(atom->getName()));
-
- // set n_type
- if ( (fOptions.outputKind() == Options::kObjectFile)
- && (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit)
- && (atom->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition) )
- entry->set_n_type(N_UNDF | N_EXT | N_PEXT);
- else if ( fOptions.prebind() )
- entry->set_n_type(N_PBUD | N_EXT);
- else
- entry->set_n_type(N_UNDF | N_EXT);
-
- // set n_sect
- entry->set_n_sect(0);
-
- uint16_t desc = 0;
- if ( fOptions.outputKind() != Options::kObjectFile ) {
- // set n_desc ( high byte is library ordinal, low byte is reference type )
- std::map<const ObjectFile::Atom*,ObjectFile::Atom*>::iterator pos = fStubsMap.find(atom);
- if ( pos != fStubsMap.end() || ( strncmp(atom->getName(), ".objc_class_name_", 17) == 0) )
- desc = REFERENCE_FLAG_UNDEFINED_LAZY;
- else
- desc = REFERENCE_FLAG_UNDEFINED_NON_LAZY;
- try {
- uint8_t ordinal = this->ordinalForLibrary(atom->getFile());
- //fprintf(stderr, "ordinal=%u from reader=%p for symbol=%s\n", ordinal, atom->getFile(), atom->getName());
- SET_LIBRARY_ORDINAL(desc, ordinal);
- }
- catch (const char* msg) {
- throwf("%s %s from %s", msg, atom->getDisplayName(), atom->getFile()->getPath());
- }
- }
- else if ( atom->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition ) {
- uint8_t align = atom->getAlignment().powerOf2;
- // always record custom alignment of common symbols to match what compiler does
- SET_COMM_ALIGN(desc, align);
- }
- if ( atom->isThumb() )
- desc |= N_ARM_THUMB_DEF;
- if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip )
- desc |= REFERENCED_DYNAMICALLY;
- if ( ( fOptions.outputKind() != Options::kObjectFile) && (atom->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) {
- desc |= N_REF_TO_WEAK;
- fReferencesWeakImports = true;
- }
- // set weak_import attribute
- if ( fWeakImportMap[atom] )
- desc |= N_WEAK_REF;
- entry->set_n_desc(desc);
-
- // set n_value, zero for import proxy and size for tentative definition
- entry->set_n_value(atom->getSize());
-}
-
-
-template <typename A>
-void Writer<A>::setLocalNlist(const ObjectFile::Atom* atom, macho_nlist<P>* entry)
-{
- // set n_strx
- const char* symbolName = this->symbolTableName(atom);
- char anonName[32];
- if ( (fOptions.outputKind() == Options::kObjectFile) && !fOptions.keepLocalSymbol(symbolName) ) {
- sprintf(anonName, "l%u", fAnonNameIndex++);
- symbolName = anonName;
- }
- entry->set_n_strx(this->fStringsAtom->add(symbolName));
-
- // set n_type
- uint8_t type = N_SECT;
- if ( atom->getDefinitionKind() == ObjectFile::Atom::kAbsoluteSymbol )
- type = N_ABS;
- if ( atom->getScope() == ObjectFile::Atom::scopeLinkageUnit )
- type |= N_PEXT;
- entry->set_n_type(type);
-
- // set n_sect (section number of implementation )
- uint8_t sectIndex = atom->getSection()->getIndex();
- if ( sectIndex == 0 ) {
- // see <mach-o/ldsyms.h> synthesized lable for mach_header needs special section number...
- if ( strcmp(atom->getSectionName(), "._mach_header") == 0 )
- sectIndex = 1;
- }
- entry->set_n_sect(sectIndex);
-
- // set n_desc
- uint16_t desc = 0;
- if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition )
- desc |= N_WEAK_DEF;
- if ( atom->isThumb() )
- desc |= N_ARM_THUMB_DEF;
- entry->set_n_desc(desc);
-
- // set n_value ( address this symbol will be at if this executable is loaded at it preferred address )
- if ( atom->getDefinitionKind() == ObjectFile::Atom::kAbsoluteSymbol )
- entry->set_n_value(atom->getSectionOffset());
- else
- entry->set_n_value(this->getAtomLoadAddress(atom));
-}
-
-
-template <typename A>
-void Writer<A>::addLocalLabel(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* name)
-{
- macho_nlist<P> entry;
-
- // set n_strx
- entry.set_n_strx(fStringsAtom->add(name));
-
- // set n_type
- entry.set_n_type(N_SECT);
-
- // set n_sect (section number of implementation )
- entry.set_n_sect(atom.getSection()->getIndex());
-
- // set n_desc
- entry.set_n_desc(0);
-
- // set n_value ( address this symbol will be at if this executable is loaded at it preferred address )
- entry.set_n_value(this->getAtomLoadAddress(&atom) + offsetInAtom);
-
- // add
- fLocalExtraLabels.push_back(entry);
-}
-
-
-
-template <typename A>
-void Writer<A>::addGlobalLabel(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* name)
-{
- macho_nlist<P> entry;
-
- // set n_strx
- entry.set_n_strx(fStringsAtom->add(name));
-
- // set n_type
- entry.set_n_type(N_SECT|N_EXT);
-
- // set n_sect (section number of implementation )
- entry.set_n_sect(atom.getSection()->getIndex());
-
- // set n_desc
- entry.set_n_desc(0);
-
- // set n_value ( address this symbol will be at if this executable is loaded at it preferred address )
- entry.set_n_value(this->getAtomLoadAddress(&atom) + offsetInAtom);
-
- // add
- fGlobalExtraLabels.push_back(entry);
-}
-
-template <typename A>
-void Writer<A>::setNlistRange(std::vector<class ObjectFile::Atom*>& atoms, uint32_t startIndex, uint32_t count)
-{
- macho_nlist<P>* entry = &fSymbolTable[startIndex];
- for (uint32_t i=0; i < count; ++i, ++entry) {
- ObjectFile::Atom* atom = atoms[i];
- if ( &atoms == &fExportedAtoms ) {
- this->setExportNlist(atom, entry);
- }
- else if ( &atoms == &fImportedAtoms ) {
- this->setImportNlist(atom, entry);
- }
- else {
- this->setLocalNlist(atom, entry);
- }
- }
-}
-
-template <typename A>
-void Writer<A>::copyNlistRange(const std::vector<macho_nlist<P> >& entries, uint32_t startIndex)
-{
- for ( typename std::vector<macho_nlist<P> >::const_iterator it = entries.begin(); it != entries.end(); ++it)
- fSymbolTable[startIndex++] = *it;
-}
-
-
-template <typename A>
-struct NListNameSorter
-{
- NListNameSorter(StringsLinkEditAtom<A>* pool) : fStringPool(pool) {}
-
- bool operator()(const macho_nlist<typename A::P>& left, const macho_nlist<typename A::P>& right)
- {
- return (strcmp(fStringPool->stringForIndex(left.n_strx()), fStringPool->stringForIndex(right.n_strx())) < 0);
- }
-private:
- StringsLinkEditAtom<A>* fStringPool;
-};
-
-
-template <typename A>
-void Writer<A>::buildSymbolTable()
-{
- fSymbolTableStabsStartIndex = 0;
- fSymbolTableStabsCount = fStabs->size();
- fSymbolTableLocalStartIndex = fSymbolTableStabsStartIndex + fSymbolTableStabsCount;
- fSymbolTableLocalCount = fLocalSymbolAtoms.size() + fLocalExtraLabels.size();
- fSymbolTableExportStartIndex = fSymbolTableLocalStartIndex + fSymbolTableLocalCount;
- fSymbolTableExportCount = fExportedAtoms.size() + fGlobalExtraLabels.size();
- fSymbolTableImportStartIndex = fSymbolTableExportStartIndex + fSymbolTableExportCount;
- fSymbolTableImportCount = fImportedAtoms.size();
-
- // allocate symbol table
- fSymbolTableCount = fSymbolTableStabsCount + fSymbolTableLocalCount + fSymbolTableExportCount + fSymbolTableImportCount;
- fSymbolTable = new macho_nlist<P>[fSymbolTableCount];
-
- // fill in symbol table and string pool (do stabs last so strings are at end of pool)
- setNlistRange(fLocalSymbolAtoms, fSymbolTableLocalStartIndex, fLocalSymbolAtoms.size());
- if ( fLocalExtraLabels.size() != 0 )
- copyNlistRange(fLocalExtraLabels, fSymbolTableLocalStartIndex+fLocalSymbolAtoms.size());
- setNlistRange(fExportedAtoms, fSymbolTableExportStartIndex, fExportedAtoms.size());
- if ( fGlobalExtraLabels.size() != 0 ) {
- copyNlistRange(fGlobalExtraLabels, fSymbolTableExportStartIndex+fExportedAtoms.size());
- // re-sort combined range
- std::sort( &fSymbolTable[fSymbolTableExportStartIndex],
- &fSymbolTable[fSymbolTableExportStartIndex+fSymbolTableExportCount],
- NListNameSorter<A>(fStringsAtom) );
- }
- setNlistRange(fImportedAtoms, fSymbolTableImportStartIndex, fSymbolTableImportCount);
- addStabs(fSymbolTableStabsStartIndex);
-
- // set up module table
- if ( fModuleInfoAtom != NULL )
- fModuleInfoAtom->setName();
-}
-
-
-
-template <typename A>
-bool Writer<A>::shouldExport(const ObjectFile::Atom& atom) const
-{
- switch ( atom.getSymbolTableInclusion() ) {
- case ObjectFile::Atom::kSymbolTableNotIn:
- return false;
- case ObjectFile::Atom::kSymbolTableInAndNeverStrip:
- return true;
- case ObjectFile::Atom::kSymbolTableInAsAbsolute:
- case ObjectFile::Atom::kSymbolTableIn:
- switch ( atom.getScope() ) {
- case ObjectFile::Atom::scopeGlobal:
- return true;
- case ObjectFile::Atom::scopeLinkageUnit:
- return ( (fOptions.outputKind() == Options::kObjectFile) && fOptions.keepPrivateExterns() );
- default:
- return false;
- }
- break;
- }
- return false;
-}
-
-template <typename A>
-void Writer<A>::collectExportedAndImportedAndLocalAtoms()
-{
- const int atomCount = fAllAtoms->size();
- // guess at sizes of each bucket to minimize re-allocations
- fImportedAtoms.reserve(100);
- fExportedAtoms.reserve(atomCount/2);
- fLocalSymbolAtoms.reserve(atomCount);
- for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) {
- ObjectFile::Atom* atom = *it;
- // only named atoms go in symbol table
- if ( atom->getName() != NULL ) {
- // put atom into correct bucket: imports, exports, locals
- //fprintf(stderr, "collectExportedAndImportedAndLocalAtoms() name=%s\n", atom->getDisplayName());
- switch ( atom->getDefinitionKind() ) {
- case ObjectFile::Atom::kExternalDefinition:
- case ObjectFile::Atom::kExternalWeakDefinition:
- fImportedAtoms.push_back(atom);
- break;
- case ObjectFile::Atom::kTentativeDefinition:
- if ( (fOptions.outputKind() == Options::kObjectFile) && !fOptions.readerOptions().fMakeTentativeDefinitionsReal ) {
- fImportedAtoms.push_back(atom);
- break;
- }
- // else fall into
- case ObjectFile::Atom::kRegularDefinition:
- case ObjectFile::Atom::kWeakDefinition:
- case ObjectFile::Atom::kAbsoluteSymbol:
- if ( this->shouldExport(*atom) )
- fExportedAtoms.push_back(atom);
- else if ( (atom->getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn)
- && ((fOptions.outputKind() == Options::kObjectFile) || fOptions.keepLocalSymbol(atom->getName())) )
- fLocalSymbolAtoms.push_back(atom);
- break;
- }
- }
- // when geneating a .o file, dtrace static probes become local labels
- if ( (fOptions.outputKind() == Options::kObjectFile) && !fOptions.readerOptions().fForStatic ) {
- std::vector<ObjectFile::Reference*>& references = atom->getReferences();
- for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) {
- ObjectFile::Reference* ref = *rit;
- if ( ref->getKind() == A::kDtraceProbe ) {
- // dtrace probe points to be add back into generated .o file
- this->addLocalLabel(*atom, ref->getFixUpOffset(), ref->getTargetName());
- }
- }
- }
- // when linking kernel, old style dtrace static probes become global labels
- else if ( fOptions.readerOptions().fForStatic ) {
- std::vector<ObjectFile::Reference*>& references = atom->getReferences();
- for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) {
- ObjectFile::Reference* ref = *rit;
- if ( ref->getKind() == A::kDtraceProbe ) {
- // dtrace probe points to be add back into generated .o file
- this->addGlobalLabel(*atom, ref->getFixUpOffset(), ref->getTargetName());
- }
- }
- }
- }
-
- // sort exported atoms by name
- std::sort(fExportedAtoms.begin(), fExportedAtoms.end(), AtomByNameSorter());
- // sort imported atoms by name (not required by runtime, but helps make generated files binary diffable)
- std::sort(fImportedAtoms.begin(), fImportedAtoms.end(), AtomByNameSorter());
-}
-
-
-template <typename A>
-uint64_t Writer<A>::valueForStab(const ObjectFile::Reader::Stab& stab)
-{
- switch ( stab.type ) {
- case N_FUN:
- if ( (stab.string == NULL) || (strlen(stab.string) == 0) ) {
- // end of function N_FUN has size
- return stab.atom->getSize();
- }
- else {
- // start of function N_FUN has address
- return getAtomLoadAddress(stab.atom);
- }
- case N_LBRAC:
- case N_RBRAC:
- case N_SLINE:
- if ( stab.atom == NULL )
- // some weird assembly files have slines not associated with a function
- return stab.value;
- else
- // all these stab types need their value changed from an offset in the atom to an address
- return getAtomLoadAddress(stab.atom) + stab.value;
- case N_STSYM:
- case N_LCSYM:
- case N_BNSYM:
- // all these need address of atom
- return getAtomLoadAddress(stab.atom);;
- case N_ENSYM:
- return stab.atom->getSize();
- case N_SO:
- 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>
-uint32_t Writer<A>::stringOffsetForStab(const ObjectFile::Reader::Stab& stab)
-{
- switch (stab.type) {
- case N_SO:
- if ( (stab.string == NULL) || stab.string[0] == '\0' ) {
- return this->fStringsAtom->emptyString();
- break;
- }
- // fall into uniquing case
- case N_SOL:
- case N_BINCL:
- case N_EXCL:
- return this->fStringsAtom->addUnique(stab.string);
- break;
- default:
- if ( stab.string == NULL )
- return 0;
- else if ( stab.string[0] == '\0' )
- return this->fStringsAtom->emptyString();
- else
- return this->fStringsAtom->add(stab.string);
- }
- return 0;
-}
-
-template <typename A>
-uint8_t Writer<A>::sectionIndexForStab(const ObjectFile::Reader::Stab& stab)
-{
- // 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 <typename A>
-void Writer<A>::addStabs(uint32_t startIndex)
-{
- macho_nlist<P>* entry = &fSymbolTable[startIndex];
- for(std::vector<ObjectFile::Reader::Stab>::iterator it = fStabs->begin(); it != fStabs->end(); ++it, ++entry) {
- const ObjectFile::Reader::Stab& stab = *it;
- entry->set_n_type(stab.type);
- entry->set_n_sect(sectionIndexForStab(stab));
- entry->set_n_desc(stab.desc);
- entry->set_n_value(valueForStab(stab));
- entry->set_n_strx(stringOffsetForStab(stab));
- }
-}
-
-
-
-template <typename A>
-uint32_t Writer<A>::symbolIndex(ObjectFile::Atom& atom)
-{
- // search imports
- int i = 0;
- for(std::vector<ObjectFile::Atom*>::iterator it=fImportedAtoms.begin(); it != fImportedAtoms.end(); ++it) {
- if ( &atom == *it )
- return i + fSymbolTableImportStartIndex;
- ++i;
- }
-
- // search locals
- i = 0;
- for(std::vector<ObjectFile::Atom*>::iterator it=fLocalSymbolAtoms.begin(); it != fLocalSymbolAtoms.end(); ++it) {
- if ( &atom == *it )
- return i + fSymbolTableLocalStartIndex;
- ++i;
- }
-
- // search exports
- i = 0;
- for(std::vector<ObjectFile::Atom*>::iterator it=fExportedAtoms.begin(); it != fExportedAtoms.end(); ++it) {
- if ( &atom == *it )
- return i + fSymbolTableExportStartIndex;
- ++i;
- }
-
- throwf("atom not found in symbolIndex(%s) for %s", atom.getDisplayName(), atom.getFile()->getPath());
-}
-
-
-template <>
-bool Writer<x86_64>::makesExternalRelocatableReference(ObjectFile::Atom& target) const
-{
- switch ( target.getSymbolTableInclusion() ) {
- case ObjectFile::Atom::kSymbolTableNotIn:
- return false;
- case ObjectFile::Atom::kSymbolTableInAsAbsolute:
- case ObjectFile::Atom::kSymbolTableIn:
- case ObjectFile::Atom::kSymbolTableInAndNeverStrip:
- return true;
- };
- return false;
-}
-
-template <typename A>
-bool Writer<A>::makesExternalRelocatableReference(ObjectFile::Atom& target) const
-{
- switch ( target.getDefinitionKind() ) {
- case ObjectFile::Atom::kRegularDefinition:
- case ObjectFile::Atom::kWeakDefinition:
- case ObjectFile::Atom::kAbsoluteSymbol:
- return false;
- case ObjectFile::Atom::kTentativeDefinition:
- if ( fOptions.readerOptions().fMakeTentativeDefinitionsReal )
- return false;
- else
- return (target.getScope() != ObjectFile::Atom::scopeTranslationUnit);
- case ObjectFile::Atom::kExternalDefinition:
- case ObjectFile::Atom::kExternalWeakDefinition:
- return shouldExport(target);
- }
- return false;
-}
-
-template <typename A>
-void Writer<A>::buildFixups()
-{
- if ( fOptions.outputKind() == Options::kObjectFile ) {
- this->buildObjectFileFixups();
- }
- else {
- if ( fOptions.keepRelocations() )
- this->buildObjectFileFixups();
- this->buildExecutableFixups();
- }
-}
-
-template <>
-uint32_t Writer<x86_64>::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref)
-{
- ObjectFile::Atom& target = ref->getTarget();
- bool external = this->makesExternalRelocatableReference(target);
- 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:
- case x86_64::kGroupSubordinate:
- 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.push_back(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.push_back(reloc1);
- fSectionRelocs.push_back(reloc2);
- return 2;
- }
-
- case x86_64::kBranchPCRel32:
- case x86_64::kBranchPCRel32WeakImport:
- case x86_64::kDtraceProbeSite:
- case x86_64::kDtraceIsEnabledSite:
- 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.push_back(reloc1);
- return 1;
-
- case x86_64::kPCRel32:
- 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.push_back(reloc1);
- return 1;
-
- case x86_64::kPCRel32_1:
- 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_1);
- fSectionRelocs.push_back(reloc1);
- return 1;
-
- case x86_64::kPCRel32_2:
- 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_2);
- fSectionRelocs.push_back(reloc1);
- return 1;
-
- 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_4);
- fSectionRelocs.push_back(reloc1);
- return 1;
-
- case x86_64::kBranchPCRel8:
- reloc1.set_r_address(address);
- reloc1.set_r_symbolnum(symbolIndex);
- reloc1.set_r_pcrel(true);
- reloc1.set_r_length(0);
- reloc1.set_r_extern(external);
- reloc1.set_r_type(X86_64_RELOC_BRANCH);
- fSectionRelocs.push_back(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.push_back(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.push_back(reloc1);
- return 1;
-
- case x86_64::kDtraceTypeReference:
- case x86_64::kDtraceProbe:
- // generates no relocs
- return 0;
- }
- return 0;
-}
-
-
-template <>
-uint32_t Writer<x86>::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref)
-{
- ObjectFile::Atom& target = ref->getTarget();
- bool isExtern = this->makesExternalRelocatableReference(target);
- uint32_t symbolIndex = 0;
- if ( isExtern )
- symbolIndex = this->symbolIndex(target);
- uint32_t sectionNum = target.getSection()->getIndex();
- uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset();
- macho_relocation_info<P> reloc1;
- macho_relocation_info<P> reloc2;
- macho_scattered_relocation_info<P>* sreloc1 = (macho_scattered_relocation_info<P>*)&reloc1;
- macho_scattered_relocation_info<P>* sreloc2 = (macho_scattered_relocation_info<P>*)&reloc2;
- x86::ReferenceKinds kind = (x86::ReferenceKinds)ref->getKind();
-
- if ( !isExtern && (sectionNum == 0) && (target.getDefinitionKind() != ObjectFile::Atom::kAbsoluteSymbol) )
- warning("section index == 0 for %s (kind=%d, scope=%d, inclusion=%d) in %s",
- target.getDisplayName(), target.getDefinitionKind(), target.getScope(), target.getSymbolTableInclusion(), target.getFile()->getPath());
-
-
- switch ( kind ) {
- case x86::kNoFixUp:
- case x86::kFollowOn:
- case x86::kGroupSubordinate:
- return 0;
-
- 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);
- sreloc1->set_r_pcrel(false);
- sreloc1->set_r_length(2);
- sreloc1->set_r_type(GENERIC_RELOC_VANILLA);
- sreloc1->set_r_address(address);
- sreloc1->set_r_value(target.getAddress());
- }
- else {
- reloc1.set_r_address(address);
- reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum);
- reloc1.set_r_pcrel(false);
- reloc1.set_r_length(2);
- reloc1.set_r_extern(isExtern);
- reloc1.set_r_type(GENERIC_RELOC_VANILLA);
- }
- fSectionRelocs.push_back(reloc1);
- return 1;
-
- case x86::kPointerDiff16:
- case x86::kPointerDiff:
- {
- //pint_t fromAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset();
- //fprintf(stderr, "addObjectRelocs(): refFromTarget=%s, refTarget=%s, refFromTargetAddr=0x%llX, refFromTargetOffset=0x%llX\n",
- // ref->getFromTarget().getDisplayName(), ref->getTarget().getDisplayName(),
- // ref->getFromTarget().getAddress(), ref->getFromTargetOffset());
- sreloc1->set_r_scattered(true);
- sreloc1->set_r_pcrel(false);
- sreloc1->set_r_length( (kind==x86::kPointerDiff) ? 2 : 1 );
- if ( ref->getTarget().getScope() == ObjectFile::Atom::scopeTranslationUnit )
- sreloc1->set_r_type(GENERIC_RELOC_LOCAL_SECTDIFF);
- else
- sreloc1->set_r_type(GENERIC_RELOC_SECTDIFF);
- sreloc1->set_r_address(address);
- sreloc1->set_r_value(target.getAddress());
- sreloc2->set_r_scattered(true);
- sreloc2->set_r_pcrel(false);
- sreloc2->set_r_length( (kind==x86::kPointerDiff) ? 2 : 1 );
- sreloc2->set_r_type(GENERIC_RELOC_PAIR);
- sreloc2->set_r_address(0);
- sreloc2->set_r_value(ref->getFromTarget().getAddress()+ref->getFromTargetOffset());
- fSectionRelocs.push_back(reloc2);
- fSectionRelocs.push_back(reloc1);
- return 2;
- }
-
- case x86::kPCRel32WeakImport:
- case x86::kPCRel32:
- case x86::kPCRel16:
- case x86::kPCRel8:
- case x86::kDtraceProbeSite:
- case x86::kDtraceIsEnabledSite:
- if ( !isExtern && (ref->getTargetOffset() != 0) ) {
- // use scattered reloc is target offset is non-zero
- sreloc1->set_r_scattered(true);
- sreloc1->set_r_pcrel(true);
- sreloc1->set_r_length( (kind==x86::kPCRel8) ? 0 : ((kind==x86::kPCRel16) ? 1 : 2) );
- sreloc1->set_r_type(GENERIC_RELOC_VANILLA);
- sreloc1->set_r_address(address);
- sreloc1->set_r_value(target.getAddress());
- }
- else {
- reloc1.set_r_address(address);
- reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum);
- reloc1.set_r_pcrel(true);
- reloc1.set_r_length( (kind==x86::kPCRel8) ? 0 : ((kind==x86::kPCRel16) ? 1 : 2) );
- reloc1.set_r_extern(isExtern);
- reloc1.set_r_type(GENERIC_RELOC_VANILLA);
- }
- fSectionRelocs.push_back(reloc1);
- return 1;
-
- case x86::kDtraceTypeReference:
- case x86::kDtraceProbe:
- // generates no relocs
- return 0;
-
- }
- return 0;
-}
-
-template <>
-uint32_t Writer<arm>::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref)
-{
- ObjectFile::Atom& target = ref->getTarget();
- bool isExtern = this->makesExternalRelocatableReference(target);
- uint32_t symbolIndex = 0;
- if ( isExtern )
- symbolIndex = this->symbolIndex(target);
- uint32_t sectionNum = target.getSection()->getIndex();
- uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset();
- macho_relocation_info<P> reloc1;
- macho_relocation_info<P> reloc2;
- macho_scattered_relocation_info<P>* sreloc1 = (macho_scattered_relocation_info<P>*)&reloc1;
- macho_scattered_relocation_info<P>* sreloc2 = (macho_scattered_relocation_info<P>*)&reloc2;
- arm::ReferenceKinds kind = (arm::ReferenceKinds)ref->getKind();
-
- if ( !isExtern && (sectionNum == 0) && (target.getDefinitionKind() != ObjectFile::Atom::kAbsoluteSymbol) )
- warning("section index == 0 for %s (kind=%d, scope=%d, inclusion=%d) in %s",
- target.getDisplayName(), target.getDefinitionKind(), target.getScope(), target.getSymbolTableInclusion(), target.getFile()->getPath());
-
-
- switch ( kind ) {
- case arm::kNoFixUp:
- case arm::kFollowOn:
- case arm::kGroupSubordinate:
- return 0;
-
- case arm::kPointer:
- case arm::kReadOnlyPointer:
- case arm::kPointerWeakImport:
- if ( !isExtern && (ref->getTargetOffset() != 0) ) {
- // use scattered reloc is target offset is non-zero
- sreloc1->set_r_scattered(true);
- sreloc1->set_r_pcrel(false);
- sreloc1->set_r_length(2);
- sreloc1->set_r_type(ARM_RELOC_VANILLA);
- sreloc1->set_r_address(address);
- sreloc1->set_r_value(target.getAddress());
- }
- else {
- reloc1.set_r_address(address);
- reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum);
- reloc1.set_r_pcrel(false);
- reloc1.set_r_length(2);
- reloc1.set_r_extern(isExtern);
- reloc1.set_r_type(ARM_RELOC_VANILLA);
- }
- fSectionRelocs.push_back(reloc1);
- return 1;
-
- case arm::kPointerDiff:
- {
- sreloc1->set_r_scattered(true);
- sreloc1->set_r_pcrel(false);
- sreloc1->set_r_length(2);
- if ( ref->getTarget().getScope() == ObjectFile::Atom::scopeTranslationUnit )
- sreloc1->set_r_type(ARM_RELOC_LOCAL_SECTDIFF);
- else
- sreloc1->set_r_type(ARM_RELOC_SECTDIFF);
- sreloc1->set_r_address(address);
- sreloc1->set_r_value(target.getAddress());
- sreloc2->set_r_scattered(true);
- sreloc2->set_r_pcrel(false);
- sreloc2->set_r_length(2);
- sreloc2->set_r_type(ARM_RELOC_PAIR);
- sreloc2->set_r_address(0);
- sreloc2->set_r_value(ref->getFromTarget().getAddress()+ref->getFromTargetOffset());
- fSectionRelocs.push_back(reloc2);
- fSectionRelocs.push_back(reloc1);
- return 2;
- }
-
- case arm::kBranch24WeakImport:
- case arm::kBranch24:
- case arm::kDtraceProbeSite:
- case arm::kDtraceIsEnabledSite:
- if ( !isExtern && (ref->getTargetOffset() != 0) ) {
- // use scattered reloc is target offset is non-zero
- sreloc1->set_r_scattered(true);
- sreloc1->set_r_pcrel(true);
- sreloc1->set_r_length(2);
- sreloc1->set_r_type(ARM_RELOC_BR24);
- sreloc1->set_r_address(address);
- sreloc1->set_r_value(target.getAddress());
- }
- else {
- reloc1.set_r_address(address);
- reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum);
- reloc1.set_r_pcrel(true);
- reloc1.set_r_length(2);
- reloc1.set_r_extern(isExtern);
- reloc1.set_r_type(ARM_RELOC_BR24);
- }
- fSectionRelocs.push_back(reloc1);
- return 1;
-
- case arm::kThumbBranch22WeakImport:
- case arm::kThumbBranch22:
- if ( !isExtern && (ref->getTargetOffset() != 0) ) {
- // use scattered reloc is target offset is non-zero
- sreloc1->set_r_scattered(true);
- sreloc1->set_r_pcrel(true);
- sreloc1->set_r_length(2);
- sreloc1->set_r_type(ARM_THUMB_RELOC_BR22);
- sreloc1->set_r_address(address);
- sreloc1->set_r_value(target.getAddress());
- }
- else {
- reloc1.set_r_address(address);
- reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum);
- reloc1.set_r_pcrel(true);
- reloc1.set_r_length(2);
- reloc1.set_r_extern(isExtern);
- reloc1.set_r_type(ARM_THUMB_RELOC_BR22);
- }
- fSectionRelocs.push_back(reloc1);
- return 1;
-
- case arm::kDtraceTypeReference:
- case arm::kDtraceProbe:
- // generates no relocs
- return 0;
-
- }
- return 0;
-}
-
-template <> uint64_t Writer<ppc>::maxAddress() { return 0xFFFFFFFFULL; }
-template <> uint64_t Writer<ppc64>::maxAddress() { return 0xFFFFFFFFFFFFFFFFULL; }
-template <> uint64_t Writer<x86>::maxAddress() { return 0xFFFFFFFFULL; }
-template <> uint64_t Writer<x86_64>::maxAddress() { return 0xFFFFFFFFFFFFFFFFULL; }
-template <> uint64_t Writer<arm>::maxAddress() { return 0xFFFFFFFFULL; }
-
-template <>
-uint8_t Writer<ppc>::getRelocPointerSize()
-{
- return 2;
-}
-
-template <>
-uint8_t Writer<ppc64>::getRelocPointerSize()
-{
- return 3;
-}
-
-template <>
-uint32_t Writer<ppc>::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref)
-{
- return addObjectRelocs_powerpc(atom, ref);
-}
-
-template <>
-uint32_t Writer<ppc64>::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref)
-{
- return addObjectRelocs_powerpc(atom, ref);
-}
-
-//
-// addObjectRelocs<ppc> and addObjectRelocs<ppc64> are almost exactly the same, so
-// they use a common addObjectRelocs_powerpc() method.
-//
-template <typename A>
-uint32_t Writer<A>::addObjectRelocs_powerpc(ObjectFile::Atom* atom, ObjectFile::Reference* ref)
-{
- ObjectFile::Atom& target = ref->getTarget();
- bool isExtern = this->makesExternalRelocatableReference(target);
- uint32_t symbolIndex = 0;
- if ( isExtern )
- symbolIndex = this->symbolIndex(target);
- uint32_t sectionNum = target.getSection()->getIndex();
- uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset();
- macho_relocation_info<P> reloc1;
- macho_relocation_info<P> reloc2;
- macho_scattered_relocation_info<P>* sreloc1 = (macho_scattered_relocation_info<P>*)&reloc1;
- macho_scattered_relocation_info<P>* sreloc2 = (macho_scattered_relocation_info<P>*)&reloc2;
- typename A::ReferenceKinds kind = (typename A::ReferenceKinds)ref->getKind();
-
- switch ( kind ) {
- case A::kNoFixUp:
- case A::kFollowOn:
- case A::kGroupSubordinate:
- return 0;
-
- case A::kPointer:
- case A::kPointerWeakImport:
- if ( !isExtern && (ref->getTargetOffset() >= target.getSize()) ) {
- // use scattered reloc is target offset is outside target
- sreloc1->set_r_scattered(true);
- sreloc1->set_r_pcrel(false);
- sreloc1->set_r_length(getRelocPointerSize());
- sreloc1->set_r_type(GENERIC_RELOC_VANILLA);
- sreloc1->set_r_address(address);
- sreloc1->set_r_value(target.getAddress());
- }
- else {
- reloc1.set_r_address(address);
- if ( isExtern )
- reloc1.set_r_symbolnum(symbolIndex);
- else
- reloc1.set_r_symbolnum(sectionNum);
- reloc1.set_r_pcrel(false);
- reloc1.set_r_length(getRelocPointerSize());
- reloc1.set_r_extern(isExtern);
- reloc1.set_r_type(GENERIC_RELOC_VANILLA);
- }
- fSectionRelocs.push_back(reloc1);
- return 1;
-
- case A::kPointerDiff16:
- case A::kPointerDiff32:
- case A::kPointerDiff64:
- {
- sreloc1->set_r_scattered(true);
- sreloc1->set_r_pcrel(false);
- sreloc1->set_r_length( (kind == A::kPointerDiff32) ? 2 : ((kind == A::kPointerDiff64) ? 3 : 1));
- if ( ref->getTarget().getScope() == ObjectFile::Atom::scopeTranslationUnit )
- sreloc1->set_r_type(PPC_RELOC_LOCAL_SECTDIFF);
- else
- sreloc1->set_r_type(PPC_RELOC_SECTDIFF);
- sreloc1->set_r_address(address);
- sreloc1->set_r_value(target.getAddress());
- sreloc2->set_r_scattered(true);
- sreloc2->set_r_pcrel(false);
- sreloc2->set_r_length(sreloc1->r_length());
- sreloc2->set_r_type(PPC_RELOC_PAIR);
- sreloc2->set_r_address(0);
- sreloc2->set_r_value(ref->getFromTarget().getAddress()+ref->getFromTargetOffset());
- fSectionRelocs.push_back(reloc2);
- fSectionRelocs.push_back(reloc1);
- return 2;
- }
-
- case A::kBranch24WeakImport:
- case A::kBranch24:
- case A::kDtraceProbeSite:
- case A::kDtraceIsEnabledSite:
- if ( (ref->getTargetOffset() == 0) || isExtern ) {
- reloc1.set_r_address(address);
- if ( isExtern )
- reloc1.set_r_symbolnum(symbolIndex);
- else
- reloc1.set_r_symbolnum(sectionNum);
- reloc1.set_r_pcrel(true);
- reloc1.set_r_length(2);
- reloc1.set_r_type(PPC_RELOC_BR24);
- reloc1.set_r_extern(isExtern);
- }
- else {
- sreloc1->set_r_scattered(true);
- sreloc1->set_r_pcrel(true);
- sreloc1->set_r_length(2);
- sreloc1->set_r_type(PPC_RELOC_BR24);
- sreloc1->set_r_address(address);
- sreloc1->set_r_value(target.getAddress());
- }
- fSectionRelocs.push_back(reloc1);
- return 1;
-
- case A::kBranch14:
- if ( (ref->getTargetOffset() == 0) || isExtern ) {
- reloc1.set_r_address(address);
- if ( isExtern )
- reloc1.set_r_symbolnum(symbolIndex);
- else
- reloc1.set_r_symbolnum(sectionNum);
- reloc1.set_r_pcrel(true);
- reloc1.set_r_length(2);
- reloc1.set_r_type(PPC_RELOC_BR14);
- reloc1.set_r_extern(isExtern);
- }
- else {
- sreloc1->set_r_scattered(true);
- sreloc1->set_r_pcrel(true);
- sreloc1->set_r_length(2);
- sreloc1->set_r_type(PPC_RELOC_BR14);
- sreloc1->set_r_address(address);
- sreloc1->set_r_value(target.getAddress());
- }
- fSectionRelocs.push_back(reloc1);
- return 1;
-
- case A::kPICBaseLow16:
- case A::kPICBaseLow14:
- {
- pint_t fromAddr = atom->getAddress() + ref->getFromTargetOffset();
- pint_t toAddr = target.getAddress() + ref->getTargetOffset();
- sreloc1->set_r_scattered(true);
- sreloc1->set_r_pcrel(false);
- sreloc1->set_r_length(2);
- sreloc1->set_r_type(kind == A::kPICBaseLow16 ? PPC_RELOC_LO16_SECTDIFF : PPC_RELOC_LO14_SECTDIFF);
- sreloc1->set_r_address(address);
- sreloc1->set_r_value(target.getAddress());
- sreloc2->set_r_scattered(true);
- sreloc2->set_r_pcrel(false);
- sreloc2->set_r_length(2);
- sreloc2->set_r_type(PPC_RELOC_PAIR);
- sreloc2->set_r_address(((toAddr-fromAddr) >> 16) & 0xFFFF);
- sreloc2->set_r_value(fromAddr);
- fSectionRelocs.push_back(reloc2);
- fSectionRelocs.push_back(reloc1);
- return 2;
- }
-
- case A::kPICBaseHigh16:
- {
- pint_t fromAddr = atom->getAddress() + ref->getFromTargetOffset();
- pint_t toAddr = target.getAddress() + ref->getTargetOffset();
- sreloc1->set_r_scattered(true);
- sreloc1->set_r_pcrel(false);
- sreloc1->set_r_length(2);
- sreloc1->set_r_type(PPC_RELOC_HA16_SECTDIFF);
- sreloc1->set_r_address(address);
- sreloc1->set_r_value(target.getAddress());
- sreloc2->set_r_scattered(true);
- sreloc2->set_r_pcrel(false);
- sreloc2->set_r_length(2);
- sreloc2->set_r_type(PPC_RELOC_PAIR);
- sreloc2->set_r_address((toAddr-fromAddr) & 0xFFFF);
- sreloc2->set_r_value(fromAddr);
- fSectionRelocs.push_back(reloc2);
- fSectionRelocs.push_back(reloc1);
- return 2;
- }
-
- case A::kAbsLow14:
- case A::kAbsLow16:
- {
- pint_t toAddr = target.getAddress() + ref->getTargetOffset();
- if ( (ref->getTargetOffset() == 0) || isExtern ) {
- reloc1.set_r_address(address);
- if ( isExtern )
- reloc1.set_r_symbolnum(symbolIndex);
- else
- reloc1.set_r_symbolnum(sectionNum);
- reloc1.set_r_pcrel(false);
- reloc1.set_r_length(2);
- reloc1.set_r_extern(isExtern);
- reloc1.set_r_type(kind==A::kAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14);
- }
- else {
- sreloc1->set_r_scattered(true);
- sreloc1->set_r_pcrel(false);
- sreloc1->set_r_length(2);
- sreloc1->set_r_type(kind==A::kAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14);
- sreloc1->set_r_address(address);
- sreloc1->set_r_value(target.getAddress());
- }
- if ( isExtern )
- reloc2.set_r_address(ref->getTargetOffset() >> 16);
- else
- reloc2.set_r_address(toAddr >> 16);
- reloc2.set_r_symbolnum(0);
- reloc2.set_r_pcrel(false);
- reloc2.set_r_length(2);
- reloc2.set_r_extern(false);
- reloc2.set_r_type(PPC_RELOC_PAIR);
- fSectionRelocs.push_back(reloc2);
- fSectionRelocs.push_back(reloc1);
- return 2;
- }
-
- case A::kAbsHigh16:
- {
- pint_t toAddr = target.getAddress() + ref->getTargetOffset();
- if ( (ref->getTargetOffset() == 0) || isExtern ) {
- reloc1.set_r_address(address);
- if ( isExtern )
- reloc1.set_r_symbolnum(symbolIndex);
- else
- reloc1.set_r_symbolnum(sectionNum);
- reloc1.set_r_pcrel(false);
- reloc1.set_r_length(2);
- reloc1.set_r_extern(isExtern);
- reloc1.set_r_type(PPC_RELOC_HI16);
- }
- else {
- sreloc1->set_r_scattered(true);
- sreloc1->set_r_pcrel(false);
- sreloc1->set_r_length(2);
- sreloc1->set_r_type(PPC_RELOC_HI16);
- sreloc1->set_r_address(address);
- sreloc1->set_r_value(target.getAddress());
- }
- if ( isExtern )
- reloc2.set_r_address(ref->getTargetOffset() & 0xFFFF);
- else
- reloc2.set_r_address(toAddr & 0xFFFF);
- reloc2.set_r_symbolnum(0);
- reloc2.set_r_pcrel(false);
- reloc2.set_r_length(2);
- reloc2.set_r_extern(false);
- reloc2.set_r_type(PPC_RELOC_PAIR);
- fSectionRelocs.push_back(reloc2);
- fSectionRelocs.push_back(reloc1);
- return 2;
- }
-
- case A::kAbsHigh16AddLow:
- {
- pint_t toAddr = target.getAddress() + ref->getTargetOffset();
- uint32_t overflow = 0;
- if ( (toAddr & 0x00008000) != 0 )
- overflow = 0x10000;
- if ( (ref->getTargetOffset() == 0) || isExtern ) {
- reloc1.set_r_address(address);
- if ( isExtern )
- reloc1.set_r_symbolnum(symbolIndex);
- else
- reloc1.set_r_symbolnum(sectionNum);
- reloc1.set_r_pcrel(false);
- reloc1.set_r_length(2);
- reloc1.set_r_extern(isExtern);
- reloc1.set_r_type(PPC_RELOC_HA16);
- }
- else {
- sreloc1->set_r_scattered(true);
- sreloc1->set_r_pcrel(false);
- sreloc1->set_r_length(2);
- sreloc1->set_r_type(PPC_RELOC_HA16);
- sreloc1->set_r_address(address);
- sreloc1->set_r_value(target.getAddress());
- }
- if ( isExtern )
- reloc2.set_r_address(ref->getTargetOffset() & 0xFFFF);
- else
- reloc2.set_r_address(toAddr & 0xFFFF);
- reloc2.set_r_symbolnum(0);
- reloc2.set_r_pcrel(false);
- reloc2.set_r_length(2);
- reloc2.set_r_extern(false);
- reloc2.set_r_type(PPC_RELOC_PAIR);
- fSectionRelocs.push_back(reloc2);
- fSectionRelocs.push_back(reloc1);
- return 2;
- }
-
- case A::kDtraceTypeReference:
- case A::kDtraceProbe:
- // generates no relocs
- return 0;
- }
- return 0;
-}
-
-
-
-//
-// There are cases when an entry in the indirect symbol table is the magic value
-// INDIRECT_SYMBOL_LOCAL instead of being a symbol index. When that happens
-// the content of the corresponding part of the __nl_symbol_pointer section
-// must also change.
-//
-template <typename A>
-bool Writer<A>::indirectSymbolIsLocal(const ObjectFile::Reference* ref) const
-{
- // use INDIRECT_SYMBOL_LOCAL in non-lazy-pointers for atoms that won't be in symbol table or have an addend
- return ( !this->shouldExport(ref->getTarget()) || (ref->getTargetOffset() != 0) );
-}
-
-
-template <typename A>
-void Writer<A>::buildObjectFileFixups()
-{
- uint32_t relocIndex = 0;
- std::vector<SegmentInfo*>& segmentInfos = fSegmentInfos;
- const int segCount = segmentInfos.size();
- for(int i=0; i < segCount; ++i) {
- SegmentInfo* curSegment = segmentInfos[i];
- std::vector<SectionInfo*>& sectionInfos = curSegment->fSections;
- const int sectionCount = sectionInfos.size();
- for(int j=0; j < sectionCount; ++j) {
- SectionInfo* curSection = sectionInfos[j];
- //fprintf(stderr, "buildObjectFileFixups(): starting section %s\n", curSection->fSectionName);
- std::vector<ObjectFile::Atom*>& sectionAtoms = curSection->fAtoms;
- if ( ! curSection->fAllZeroFill ) {
- if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers
- || curSection->fAllLazyDylibPointers || curSection->fAllStubs )
- curSection->fIndirectSymbolOffset = fIndirectTableAtom->fTable.size();
- curSection->fRelocOffset = relocIndex;
- const int atomCount = sectionAtoms.size();
- for (int k=0; k < atomCount; ++k) {
- ObjectFile::Atom* atom = sectionAtoms[k];
- //fprintf(stderr, "buildObjectFileFixups(): atom %s\n", atom->getDisplayName());
- std::vector<ObjectFile::Reference*>& refs = atom->getReferences();
- const int refCount = refs.size();
- for (int l=0; l < refCount; ++l) {
- ObjectFile::Reference* ref = refs[l];
- if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers
- || curSection->fAllLazyDylibPointers || curSection->fAllStubs ) {
- uint32_t offsetInSection = atom->getSectionOffset();
- uint32_t indexInSection = offsetInSection / atom->getSize();
- uint32_t undefinedSymbolIndex;
- if ( curSection->fAllStubs ) {
- ObjectFile::Atom& stubTarget =ref->getTarget();
- ObjectFile::Atom& stubTargetTarget = stubTarget.getReferences()[0]->getTarget();
- undefinedSymbolIndex = this->symbolIndex(stubTargetTarget);
- //fprintf(stderr, "stub %s ==> %s ==> %s ==> index:%u\n", atom->getDisplayName(), stubTarget.getDisplayName(), stubTargetTarget.getDisplayName(), undefinedSymbolIndex);
- }
- else if ( curSection->fAllNonLazyPointers) {
- // only use INDIRECT_SYMBOL_LOCAL in non-lazy-pointers for atoms that won't be in symbol table or have an addend
- if ( this->indirectSymbolIsLocal(ref) )
- undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL;
- else
- undefinedSymbolIndex = this->symbolIndex(ref->getTarget());
- }
- else {
- // should never get here, fAllLazyPointers not used in generated .o files
- undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL;
- }
- uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset;
- IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex };
- //printf("fIndirectTableAtom->fTable.add(sectionIndex=%u, indirectTableIndex=%u => %u), size=%lld\n", indexInSection, indirectTableIndex, undefinedSymbolIndex, atom->getSize());
- fIndirectTableAtom->fTable.push_back(entry);
- if ( curSection->fAllLazyPointers ) {
- ObjectFile::Atom& target = ref->getTarget();
- ObjectFile::Atom& fromTarget = ref->getFromTarget();
- if ( &fromTarget == NULL ) {
- warning("lazy pointer %s missing initial binding", atom->getDisplayName());
- }
- else {
- bool isExtern = ( ((target.getDefinitionKind() == ObjectFile::Atom::kExternalDefinition)
- || (target.getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition))
- && (target.getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn) );
- macho_relocation_info<P> reloc1;
- reloc1.set_r_address(atom->getSectionOffset());
- reloc1.set_r_symbolnum(isExtern ? this->symbolIndex(target) : target.getSection()->getIndex());
- reloc1.set_r_pcrel(false);
- reloc1.set_r_length();
- reloc1.set_r_extern(isExtern);
- reloc1.set_r_type(GENERIC_RELOC_VANILLA);
- fSectionRelocs.push_back(reloc1);
- ++relocIndex;
- }
- }
- else if ( curSection->fAllStubs ) {
- relocIndex += this->addObjectRelocs(atom, ref);
- }
- }
- else if ( (ref->getKind() != A::kNoFixUp) && (ref->getTargetBinding() != ObjectFile::Reference::kDontBind) ) {
- relocIndex += this->addObjectRelocs(atom, ref);
- }
- }
- }
- curSection->fRelocCount = relocIndex - curSection->fRelocOffset;
- }
- }
- }
-
- // reverse the relocs
- std::reverse(fSectionRelocs.begin(), fSectionRelocs.end());
-
- // now reverse section reloc offsets
- for(int i=0; i < segCount; ++i) {
- SegmentInfo* curSegment = segmentInfos[i];
- std::vector<SectionInfo*>& sectionInfos = curSegment->fSections;
- const int sectionCount = sectionInfos.size();
- for(int j=0; j < sectionCount; ++j) {
- SectionInfo* curSection = sectionInfos[j];
- curSection->fRelocOffset = relocIndex - curSection->fRelocOffset - curSection->fRelocCount;
- }
- }
-
-}
-
-template <>
-bool Writer<ppc>::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref)
-{
- switch ( ref.getKind() ) {
- case ppc::kAbsLow16:
- case ppc::kAbsLow14:
- case ppc::kAbsHigh16:
- case ppc::kAbsHigh16AddLow:
- if ( fSlideable )
- return true;
- }
- return false;
-}
-
-
-template <>
-bool Writer<ppc64>::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref)
-{
- switch ( ref.getKind() ) {
- case ppc::kAbsLow16:
- case ppc::kAbsLow14:
- case ppc::kAbsHigh16:
- case ppc::kAbsHigh16AddLow:
- if ( fSlideable )
- return true;
- }
- return false;
-}
-
-template <>
-bool Writer<x86>::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref)
-{
- if ( ref.getKind() == x86::kAbsolute32 ) {
- switch ( ref.getTarget().getDefinitionKind() ) {
- case ObjectFile::Atom::kTentativeDefinition:
- case ObjectFile::Atom::kRegularDefinition:
- case ObjectFile::Atom::kWeakDefinition:
- // illegal in dylibs/bundles, until we support TEXT relocs
- return fSlideable;
- case ObjectFile::Atom::kExternalDefinition:
- case ObjectFile::Atom::kExternalWeakDefinition:
- // illegal until we support TEXT relocs
- return true;
- case ObjectFile::Atom::kAbsoluteSymbol:
- // absolute symbbols only allowed in static executables
- return ( fOptions.outputKind() != Options::kStaticExecutable);
- }
- }
- return false;
-}
-
-template <>
-bool Writer<x86_64>::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref)
-{
- return false;
-}
-
-template <>
-bool Writer<arm>::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref)
-{
- if ( ref.getKind() == arm::kReadOnlyPointer ) {
- switch ( ref.getTarget().getDefinitionKind() ) {
- case ObjectFile::Atom::kTentativeDefinition:
- case ObjectFile::Atom::kRegularDefinition:
- case ObjectFile::Atom::kWeakDefinition:
- // illegal in dylibs/bundles, until we support TEXT relocs
- return fSlideable;
- case ObjectFile::Atom::kExternalDefinition:
- case ObjectFile::Atom::kExternalWeakDefinition:
- // illegal until we support TEXT relocs
- return true;
- case ObjectFile::Atom::kAbsoluteSymbol:
- // absolute symbbols only allowed in static executables
- return ( fOptions.outputKind() != Options::kStaticExecutable);
- }
- }
- return false;
-}
-
-template <>
-bool Writer<x86>::generatesLocalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection)
-{
- if ( ref.getKind() == x86::kAbsolute32 ) {
- switch ( ref.getTarget().getDefinitionKind() ) {
- case ObjectFile::Atom::kTentativeDefinition:
- case ObjectFile::Atom::kRegularDefinition:
- case ObjectFile::Atom::kWeakDefinition:
- // a reference to the absolute address of something in this same linkage unit can be
- // encoded as a local text reloc in a dylib or bundle
- if ( fSlideable ) {
- macho_relocation_info<P> reloc;
- SectionInfo* sectInfo = (SectionInfo*)(ref.getTarget().getSection());
- reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom));
- reloc.set_r_symbolnum(sectInfo->getIndex());
- reloc.set_r_pcrel(false);
- reloc.set_r_length();
- reloc.set_r_extern(false);
- reloc.set_r_type(GENERIC_RELOC_VANILLA);
- fInternalRelocs.push_back(reloc);
- atomSection->fHasTextLocalRelocs = true;
- return true;
- }
- return false;
- case ObjectFile::Atom::kExternalDefinition:
- case ObjectFile::Atom::kExternalWeakDefinition:
- case ObjectFile::Atom::kAbsoluteSymbol:
- return false;
- }
- }
- return false;
-}
-
-template <>
-bool Writer<ppc>::generatesLocalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection)
-{
- macho_relocation_info<P> reloc1;
- macho_relocation_info<P> reloc2;
- switch ( ref.getTarget().getDefinitionKind() ) {
- case ObjectFile::Atom::kTentativeDefinition:
- case ObjectFile::Atom::kRegularDefinition:
- case ObjectFile::Atom::kWeakDefinition:
- switch ( ref.getKind() ) {
- case ppc::kAbsLow16:
- case ppc::kAbsLow14:
- // a reference to the absolute address of something in this same linkage unit can be
- // encoded as a local text reloc in a dylib or bundle
- if ( fSlideable ) {
- SectionInfo* sectInfo = (SectionInfo*)(ref.getTarget().getSection());
- uint32_t targetAddr = ref.getTarget().getAddress() + ref.getTargetOffset();
- reloc1.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom));
- reloc1.set_r_symbolnum(sectInfo->getIndex());
- reloc1.set_r_pcrel(false);
- reloc1.set_r_length(2);
- reloc1.set_r_extern(false);
- reloc1.set_r_type(ref.getKind()==ppc::kAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14);
- reloc2.set_r_address(targetAddr >> 16);
- reloc2.set_r_symbolnum(0);
- reloc2.set_r_pcrel(false);
- reloc2.set_r_length(2);
- reloc2.set_r_extern(false);
- reloc2.set_r_type(PPC_RELOC_PAIR);
- fInternalRelocs.push_back(reloc1);
- fInternalRelocs.push_back(reloc2);
- atomSection->fHasTextLocalRelocs = true;
- return true;
- }
- break;
- case ppc::kAbsHigh16:
- case ppc::kAbsHigh16AddLow:
- if ( fSlideable ) {
- SectionInfo* sectInfo = (SectionInfo*)(ref.getTarget().getSection());
- uint32_t targetAddr = ref.getTarget().getAddress() + ref.getTargetOffset();
- reloc1.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom));
- reloc1.set_r_symbolnum(sectInfo->getIndex());
- reloc1.set_r_pcrel(false);
- reloc1.set_r_length(2);
- reloc1.set_r_extern(false);
- reloc1.set_r_type(ref.getKind()==ppc::kAbsHigh16AddLow ? PPC_RELOC_HA16 : PPC_RELOC_HI16);
- reloc2.set_r_address(targetAddr & 0xFFFF);
- reloc2.set_r_symbolnum(0);
- reloc2.set_r_pcrel(false);
- reloc2.set_r_length(2);
- reloc2.set_r_extern(false);
- reloc2.set_r_type(PPC_RELOC_PAIR);
- fInternalRelocs.push_back(reloc1);
- fInternalRelocs.push_back(reloc2);
- atomSection->fHasTextLocalRelocs = true;
- return true;
- }
- }
- break;
- case ObjectFile::Atom::kExternalDefinition:
- case ObjectFile::Atom::kExternalWeakDefinition:
- case ObjectFile::Atom::kAbsoluteSymbol:
- return false;
- }
- return false;
-}
-
-template <>
-bool Writer<arm>::generatesLocalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection)
-{
- if ( ref.getKind() == arm::kReadOnlyPointer ) {
- switch ( ref.getTarget().getDefinitionKind() ) {
- case ObjectFile::Atom::kTentativeDefinition:
- case ObjectFile::Atom::kRegularDefinition:
- case ObjectFile::Atom::kWeakDefinition:
- // a reference to the absolute address of something in this same linkage unit can be
- // encoded as a local text reloc in a dylib or bundle
- if ( fSlideable ) {
- macho_relocation_info<P> reloc;
- SectionInfo* sectInfo = (SectionInfo*)(ref.getTarget().getSection());
- reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom));
- reloc.set_r_symbolnum(sectInfo->getIndex());
- reloc.set_r_pcrel(false);
- reloc.set_r_length();
- reloc.set_r_extern(false);
- reloc.set_r_type(GENERIC_RELOC_VANILLA);
- fInternalRelocs.push_back(reloc);
- atomSection->fHasTextLocalRelocs = true;
- return true;
- }
- return false;
- case ObjectFile::Atom::kExternalDefinition:
- case ObjectFile::Atom::kExternalWeakDefinition:
- case ObjectFile::Atom::kAbsoluteSymbol:
- return false;
- }
- }
- return false;
-}
-
-
-template <>
-bool Writer<x86_64>::generatesLocalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection)
-{
- // text relocs not supported (usually never needed because of RIP addressing)
- return false;
-}
-
-template <>
-bool Writer<ppc64>::generatesLocalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection)
-{
- // text relocs not supported
- return false;
-}
-
-template <>
-bool Writer<x86>::generatesExternalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection)
-{
- if ( ref.getKind() == x86::kAbsolute32 ) {
- macho_relocation_info<P> reloc;
- switch ( ref.getTarget().getDefinitionKind() ) {
- case ObjectFile::Atom::kTentativeDefinition:
- case ObjectFile::Atom::kRegularDefinition:
- case ObjectFile::Atom::kWeakDefinition:
- return false;
- case ObjectFile::Atom::kExternalDefinition:
- case ObjectFile::Atom::kExternalWeakDefinition:
- // a reference to the absolute address of something in another linkage unit can be
- // encoded as an external text reloc in a dylib or bundle
- reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom));
- reloc.set_r_symbolnum(this->symbolIndex(ref.getTarget()));
- reloc.set_r_pcrel(false);
- reloc.set_r_length();
- reloc.set_r_extern(true);
- reloc.set_r_type(GENERIC_RELOC_VANILLA);
- fExternalRelocs.push_back(reloc);
- atomSection->fHasTextExternalRelocs = true;
- return true;
- case ObjectFile::Atom::kAbsoluteSymbol:
- return false;
- }
- }
- return false;
-}
-
-template <typename A>
-bool Writer<A>::generatesExternalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection)
-{
- return false;
-}
-
-
-
-
-template <typename A>
-typename Writer<A>::RelocKind Writer<A>::relocationNeededInFinalLinkedImage(const ObjectFile::Atom& target) const
-{
- switch ( target.getDefinitionKind() ) {
- case ObjectFile::Atom::kTentativeDefinition:
- case ObjectFile::Atom::kRegularDefinition:
- // in main executables, the only way regular symbols are indirected is if -interposable is used
- if ( fOptions.outputKind() == Options::kDynamicExecutable ) {
- if ( this->shouldExport(target) && fOptions.interposable(target.getName()) )
- return kRelocExternal;
- else if ( fSlideable )
- return kRelocInternal;
- else
- return kRelocNone;
- }
- // for flat-namespace or interposable two-level-namespace
- // all references to exported symbols get indirected
- else if ( this->shouldExport(target) &&
- ((fOptions.nameSpace() == Options::kFlatNameSpace)
- || (fOptions.nameSpace() == Options::kForceFlatNameSpace)
- || fOptions.interposable(target.getName()))
- && (target.getName() != NULL)
- && (strncmp(target.getName(), ".objc_class_", 12) != 0) ) // <rdar://problem/5254468>
- return kRelocExternal;
- else if ( fSlideable )
- return kRelocInternal;
- else
- return kRelocNone;
- case ObjectFile::Atom::kWeakDefinition:
- // all calls to global weak definitions get indirected
- if ( this->shouldExport(target) )
- return kRelocExternal;
- else if ( fSlideable )
- return kRelocInternal;
- else
- return kRelocNone;
- case ObjectFile::Atom::kExternalDefinition:
- case ObjectFile::Atom::kExternalWeakDefinition:
- return kRelocExternal;
- case ObjectFile::Atom::kAbsoluteSymbol:
- return kRelocNone;
- }
- 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 first segment
- uint64_t result = address - fSegmentInfos[0]->fBaseAddress;
- // or the offset from the first writable segment if built split-seg
- if ( fOptions.splitSeg() )
- result = address - fFirstWritableSegment->fBaseAddress;
- 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() < ObjectFile::ReaderOptions::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 - fSegmentInfos[0]->fBaseAddress;
- if ( (fOptions.macosxVersionMin() < ObjectFile::ReaderOptions::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 <> bool Writer<ppc>::preboundLazyPointerType(uint8_t* type) { *type = PPC_RELOC_PB_LA_PTR; return true; }
-template <> bool Writer<ppc64>::preboundLazyPointerType(uint8_t* type) { throw "prebinding not supported"; }
-template <> bool Writer<x86>::preboundLazyPointerType(uint8_t* type) { *type = GENERIC_RELOC_PB_LA_PTR; return true; }
-template <> bool Writer<x86_64>::preboundLazyPointerType(uint8_t* type) { throw "prebinding not supported"; }
-template <> bool Writer<arm>::preboundLazyPointerType(uint8_t* type) { *type = ARM_RELOC_PB_LA_PTR; return true; }
-
-template <typename A>
-void Writer<A>::buildExecutableFixups()
-{
- fIndirectTableAtom->fTable.reserve(50); // minimize reallocations
- std::vector<SegmentInfo*>& segmentInfos = fSegmentInfos;
- const int segCount = segmentInfos.size();
- for(int i=0; i < segCount; ++i) {
- SegmentInfo* curSegment = segmentInfos[i];
- std::vector<SectionInfo*>& sectionInfos = curSegment->fSections;
- const int sectionCount = sectionInfos.size();
- for(int j=0; j < sectionCount; ++j) {
- SectionInfo* curSection = sectionInfos[j];
- //fprintf(stderr, "starting section %s\n", curSection->fSectionName);
- std::vector<ObjectFile::Atom*>& sectionAtoms = curSection->fAtoms;
- if ( ! curSection->fAllZeroFill ) {
- if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers
- || curSection->fAllStubs || curSection->fAllSelfModifyingStubs )
- curSection->fIndirectSymbolOffset = fIndirectTableAtom->fTable.size();
- const int atomCount = sectionAtoms.size();
- for (int k=0; k < atomCount; ++k) {
- ObjectFile::Atom* atom = sectionAtoms[k];
- std::vector<ObjectFile::Reference*>& refs = atom->getReferences();
- const int refCount = refs.size();
- //fprintf(stderr, "atom %s has %d references in section %s, %p\n", atom->getDisplayName(), refCount, curSection->fSectionName, atom->getSection());
- for (int l=0; l < refCount; ++l) {
- ObjectFile::Reference* ref = refs[l];
- if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers ) {
- // if atom is in (non)lazy_pointer section, this is encoded as an indirect symbol
- if ( atom->getSize() != sizeof(pint_t) ) {
- warning("wrong size pointer atom %s from file %s", atom->getDisplayName(), atom->getFile()->getPath());
- }
- ObjectFile::Atom* pointerTarget = &(ref->getTarget());
- if ( curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers ) {
- pointerTarget = ((LazyPointerAtom<A>*)atom)->getTarget();
- }
- uint32_t offsetInSection = atom->getSectionOffset();
- uint32_t indexInSection = offsetInSection / sizeof(pint_t);
- uint32_t undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL;
- if ( this->relocationNeededInFinalLinkedImage(*pointerTarget) == kRelocExternal )
- undefinedSymbolIndex = this->symbolIndex(*pointerTarget);
- uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset;
- IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex };
- //fprintf(stderr,"fIndirectTableAtom->fTable.push_back(tableIndex=%d, symIndex=0x%X), pointerTarget=%s\n",
- // indirectTableIndex, undefinedSymbolIndex, pointerTarget->getDisplayName());
- fIndirectTableAtom->fTable.push_back(entry);
- if ( curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers ) {
- uint8_t preboundLazyType;
- if ( fOptions.prebind() && (fDyldHelper != NULL)
- && curSection->fAllLazyPointers && preboundLazyPointerType(&preboundLazyType) ) {
- // this is a prebound image, need special relocs for dyld to reset lazy pointers if prebinding is invalid
- macho_scattered_relocation_info<P> pblaReloc;
- pblaReloc.set_r_scattered(true);
- pblaReloc.set_r_pcrel(false);
- pblaReloc.set_r_length();
- pblaReloc.set_r_type(preboundLazyType);
- pblaReloc.set_r_address(relocAddressInFinalLinkedImage(atom->getAddress(), atom));
- pblaReloc.set_r_value(fDyldHelper->getAddress());
- fInternalRelocs.push_back(*((macho_relocation_info<P>*)&pblaReloc));
- }
- else if ( fSlideable ) {
- // this is a non-prebound dylib/bundle, need vanilla internal relocation to fix up binding handler if image slides
- macho_relocation_info<P> dyldHelperReloc;
- uint32_t sectionNum = 1;
- if ( fDyldHelper != NULL )
- sectionNum = ((SectionInfo*)(fDyldHelper->getSection()))->getIndex();
- //fprintf(stderr, "lazy pointer reloc, section index=%u, section name=%s\n", sectionNum, curSection->fSectionName);
- dyldHelperReloc.set_r_address(relocAddressInFinalLinkedImage(atom->getAddress(), atom));
- dyldHelperReloc.set_r_symbolnum(sectionNum);
- dyldHelperReloc.set_r_pcrel(false);
- dyldHelperReloc.set_r_length();
- dyldHelperReloc.set_r_extern(false);
- dyldHelperReloc.set_r_type(GENERIC_RELOC_VANILLA);
- fInternalRelocs.push_back(dyldHelperReloc);
- }
- }
- }
- else if ( (ref->getKind() == A::kPointer) || (ref->getKind() == A::kPointerWeakImport) ) {
- if ( fSlideable && ((curSegment->fInitProtection & VM_PROT_WRITE) == 0) ) {
- throwf("pointer in read-only segment not allowed in slidable image, used in %s from %s",
- atom->getDisplayName(), atom->getFile()->getPath());
- }
- switch ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) ) {
- case kRelocNone:
- // no reloc needed
- break;
- case kRelocInternal:
- {
- macho_relocation_info<P> internalReloc;
- SectionInfo* sectInfo = (SectionInfo*)ref->getTarget().getSection();
- uint32_t sectionNum = sectInfo->getIndex();
- // special case _mh_dylib_header and friends which are not in any real section
- if ( (sectionNum ==0) && sectInfo->fVirtualSection && (strcmp(sectInfo->fSectionName, "._mach_header") == 0) )
- sectionNum = 1;
- internalReloc.set_r_address(this->relocAddressInFinalLinkedImage(atom->getAddress() + ref->getFixUpOffset(), atom));
- internalReloc.set_r_symbolnum(sectionNum);
- internalReloc.set_r_pcrel(false);
- internalReloc.set_r_length();
- internalReloc.set_r_extern(false);
- internalReloc.set_r_type(GENERIC_RELOC_VANILLA);
- fInternalRelocs.push_back(internalReloc);
- }
- break;
- case kRelocExternal:
- {
- macho_relocation_info<P> externalReloc;
- 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();
- externalReloc.set_r_extern(true);
- externalReloc.set_r_type(GENERIC_RELOC_VANILLA);
- fExternalRelocs.push_back(externalReloc);
- }
- break;
- }
- }
- else if ( this->illegalRelocInFinalLinkedImage(*ref) ) {
- if ( fOptions.allowTextRelocs() && !atom->getSegment().isContentWritable() ) {
- if ( fOptions.warnAboutTextRelocs() )
- warning("text reloc in %s to %s", atom->getDisplayName(), ref->getTargetName());
- if ( this->generatesLocalTextReloc(*ref, *atom, curSection) ) {
- // relocs added to fInternalRelocs
- }
- else if ( this->generatesExternalTextReloc(*ref, *atom, curSection) ) {
- // relocs added to fExternalRelocs
- }
- else {
- throwf("relocation used in %s from %s not allowed in slidable image", atom->getDisplayName(), atom->getFile()->getPath());
- }
- }
- else {
- throwf("absolute addressing (perhaps -mdynamic-no-pic) used in %s from %s not allowed in slidable image. "
- "Use '-read_only_relocs suppress' to enable text relocs", atom->getDisplayName(), atom->getFile()->getPath());
- }
- }
- }
- if ( curSection->fAllSelfModifyingStubs || curSection->fAllStubs ) {
- ObjectFile::Atom* stubTarget = ((StubAtom<A>*)atom)->getTarget();
- uint32_t undefinedSymbolIndex = (stubTarget != NULL) ? this->symbolIndex(*stubTarget) : INDIRECT_SYMBOL_ABS;
- uint32_t offsetInSection = atom->getSectionOffset();
- uint32_t indexInSection = offsetInSection / atom->getSize();
- uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset;
- IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex };
- //fprintf(stderr,"for stub: fIndirectTableAtom->fTable.add(%d-%d => 0x%X-%s), size=%lld\n", indexInSection, indirectTableIndex, undefinedSymbolIndex, stubTarget->getName(), atom->getSize());
- fIndirectTableAtom->fTable.push_back(entry);
- }
- }
- }
- }
- }
- if ( fSplitCodeToDataContentAtom != NULL )
- fSplitCodeToDataContentAtom->encode();
-}
-
-
-template <>
-void Writer<ppc>::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref)
-{
- switch ( (ppc::ReferenceKinds)ref->getKind() ) {
- case ppc::kPICBaseHigh16:
- fSplitCodeToDataContentAtom->addPPCHi16Location(atom, ref->getFixUpOffset());
- break;
- case ppc::kPointerDiff32:
- fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset());
- break;
- case ppc::kPointerDiff64:
- fSplitCodeToDataContentAtom->add64bitPointerLocation(atom, ref->getFixUpOffset());
- break;
- case ppc::kNoFixUp:
- case ppc::kGroupSubordinate:
- case ppc::kPointer:
- case ppc::kPointerWeakImport:
- case ppc::kPICBaseLow16:
- case ppc::kPICBaseLow14:
- // ignore
- break;
- default:
- warning("codegen with reference kind %d in %s prevents image from loading in dyld shared cache", ref->getKind(), atom->getDisplayName());
- fSplitCodeToDataContentAtom->setCantEncode();
- }
-}
-
-template <>
-void Writer<ppc64>::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref)
-{
- switch ( (ppc64::ReferenceKinds)ref->getKind() ) {
- case ppc64::kPICBaseHigh16:
- fSplitCodeToDataContentAtom->addPPCHi16Location(atom, ref->getFixUpOffset());
- break;
- case ppc64::kPointerDiff32:
- fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset());
- break;
- case ppc64::kPointerDiff64:
- fSplitCodeToDataContentAtom->add64bitPointerLocation(atom, ref->getFixUpOffset());
- break;
- case ppc64::kNoFixUp:
- case ppc64::kGroupSubordinate:
- case ppc64::kPointer:
- case ppc64::kPointerWeakImport:
- case ppc64::kPICBaseLow16:
- case ppc64::kPICBaseLow14:
- // ignore
- break;
- default:
- warning("codegen with reference kind %d in %s prevents image from loading in dyld shared cache", ref->getKind(), atom->getDisplayName());
- fSplitCodeToDataContentAtom->setCantEncode();
- }
-}
-
-template <>
-void Writer<x86>::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref)
-{
- switch ( (x86::ReferenceKinds)ref->getKind() ) {
- case x86::kPointerDiff:
- if ( strcmp(ref->getTarget().getSegment().getName(), "__IMPORT") == 0 )
- fSplitCodeToDataContentAtom->add32bitImportLocation(atom, ref->getFixUpOffset());
- else
- fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset());
- break;
- case x86::kNoFixUp:
- case x86::kGroupSubordinate:
- case x86::kPointer:
- case x86::kPointerWeakImport:
- // ignore
- break;
- case x86::kPCRel32:
- case x86::kPCRel32WeakImport:
- if ( (&(ref->getTarget().getSegment()) == &Segment::fgImportSegment)
- || (&(ref->getTarget().getSegment()) == &Segment::fgROImportSegment) ) {
- fSplitCodeToDataContentAtom->add32bitImportLocation(atom, ref->getFixUpOffset());
- break;
- }
- // fall into warning case
- default:
- warning("codegen in %s (offset 0x%08llX) prevents image from loading in dyld shared cache", atom->getDisplayName(), ref->getFixUpOffset());
- fSplitCodeToDataContentAtom->setCantEncode();
- }
-}
-
-template <>
-void Writer<x86_64>::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref)
-{
- switch ( (x86_64::ReferenceKinds)ref->getKind() ) {
- case x86_64::kPCRel32:
- case x86_64::kPCRel32_1:
- case x86_64::kPCRel32_2:
- case x86_64::kPCRel32_4:
- case x86_64::kPCRel32GOTLoad:
- case x86_64::kPCRel32GOTLoadWeakImport:
- case x86_64::kPCRel32GOT:
- case x86_64::kPCRel32GOTWeakImport:
- case x86_64::kPointerDiff32:
- fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset());
- break;
- case x86_64::kPointerDiff:
- fSplitCodeToDataContentAtom->add64bitPointerLocation(atom, ref->getFixUpOffset());
- break;
- case x86_64::kNoFixUp:
- case x86_64::kGroupSubordinate:
- case x86_64::kPointer:
- // ignore
- break;
- default:
- warning("codegen in %s with kind %d prevents image from loading in dyld shared cache", atom->getDisplayName(), ref->getKind());
- fSplitCodeToDataContentAtom->setCantEncode();
- }
-}
-
-template <>
-void Writer<arm>::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref)
-{
- switch ( (arm::ReferenceKinds)ref->getKind() ) {
- case arm::kPointerDiff:
- fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset());
- break;
- case arm::kNoFixUp:
- case arm::kGroupSubordinate:
- case arm::kPointer:
- case arm::kPointerWeakImport:
- case arm::kReadOnlyPointer:
- // ignore
- break;
- default:
- warning("codegen in %s prevents image from loading in dyld shared cache", atom->getDisplayName());
- fSplitCodeToDataContentAtom->setCantEncode();
- }
-}
-
-template <typename A>
-bool Writer<A>::segmentsCanSplitApart(const ObjectFile::Atom& from, const ObjectFile::Atom& to)
-{
- switch ( to.getDefinitionKind() ) {
- case ObjectFile::Atom::kExternalDefinition:
- case ObjectFile::Atom::kExternalWeakDefinition:
- case ObjectFile::Atom::kAbsoluteSymbol:
- return false;
- case ObjectFile::Atom::kRegularDefinition:
- case ObjectFile::Atom::kWeakDefinition:
- case ObjectFile::Atom::kTentativeDefinition:
- // segments with same permissions slide together
- return ( (from.getSegment().isContentExecutable() != to.getSegment().isContentExecutable())
- || (from.getSegment().isContentWritable() != to.getSegment().isContentWritable()) );
- }
- throw "ld64 internal error";
-}
-
-
-template <>
-void Writer<ppc>::writeNoOps(int fd, uint32_t from, uint32_t to)
-{
- uint32_t ppcNop;
- OSWriteBigInt32(&ppcNop, 0, 0x60000000);
- for (uint32_t p=from; p < to; p += 4)
- ::pwrite(fd, &ppcNop, 4, p);
-}
-
-template <>
-void Writer<ppc64>::writeNoOps(int fd, uint32_t from, uint32_t to)
-{
- uint32_t ppcNop;
- OSWriteBigInt32(&ppcNop, 0, 0x60000000);
- for (uint32_t p=from; p < to; p += 4)
- ::pwrite(fd, &ppcNop, 4, p);
-}
-
-template <>
-void Writer<x86>::writeNoOps(int fd, uint32_t from, uint32_t to)
-{
- uint8_t x86Nop = 0x90;
- for (uint32_t p=from; p < to; ++p)
- ::pwrite(fd, &x86Nop, 1, p);
-}
-
-template <>
-void Writer<x86_64>::writeNoOps(int fd, uint32_t from, uint32_t to)
-{
- uint8_t x86Nop = 0x90;
- for (uint32_t p=from; p < to; ++p)
- ::pwrite(fd, &x86Nop, 1, p);
-}
-
-template <>
-void Writer<arm>::writeNoOps(int fd, uint32_t from, uint32_t to)
-{
- // FIXME: need thumb nop?
- uint32_t armNop;
- OSWriteLittleInt32(&armNop, 0, 0xe1a00000);
- for (uint32_t p=from; p < to; p += 4)
- ::pwrite(fd, &armNop, 4, p);
-}
-
-template <>
-void Writer<ppc>::copyNoOps(uint8_t* from, uint8_t* to)
-{
- for (uint8_t* p=from; p < to; p += 4)
- OSWriteBigInt32((uint32_t*)p, 0, 0x60000000);
-}
-
-template <>
-void Writer<ppc64>::copyNoOps(uint8_t* from, uint8_t* to)
-{
- for (uint8_t* p=from; p < to; p += 4)
- OSWriteBigInt32((uint32_t*)p, 0, 0x60000000);
-}
-
-template <>
-void Writer<x86>::copyNoOps(uint8_t* from, uint8_t* to)
-{
- for (uint8_t* p=from; p < to; ++p)
- *p = 0x90;
-}
-
-template <>
-void Writer<x86_64>::copyNoOps(uint8_t* from, uint8_t* to)
-{
- for (uint8_t* p=from; p < to; ++p)
- *p = 0x90;
-}
-
-template <>
-void Writer<arm>::copyNoOps(uint8_t* from, uint8_t* to)
-{
- // fixme: need thumb nop?
- for (uint8_t* p=from; p < to; p += 4)
- OSWriteBigInt32((uint32_t*)p, 0, 0xe1a00000);
-}
-
-static const char* stringName(const char* str)
-{
- if ( strncmp(str, "cstring=", 8) == 0) {
- static char buffer[1024];
- char* t = buffer;
- *t++ = '\"';
- for(const char*s = &str[8]; *s != '\0'; ++s) {
- switch(*s) {
- case '\n':
- *t++ = '\\';
- *t++ = 'n';
- break;
- case '\t':
- *t++ = '\\';
- *t++ = 't';
- break;
- default:
- *t++ = *s;
- break;
- }
- if ( t > &buffer[1020] ) {
- *t++= '\"';
- *t++= '.';
- *t++= '.';
- *t++= '.';
- *t++= '\0';
- return buffer;
- }
- }
- *t++= '\"';
- *t++= '\0';
- return buffer;
- }
- else {
- return str;
- }
-}
-
-
-template <> const char* Writer<ppc>::getArchString() { return "ppc"; }
-template <> const char* Writer<ppc64>::getArchString() { return "ppc64"; }
-template <> const char* Writer<x86>::getArchString() { return "i386"; }
-template <> const char* Writer<x86_64>::getArchString() { return "x86_64"; }
-template <> const char* Writer<arm>::getArchString() { return "arm"; }
-
-template <typename A>
-void Writer<A>::writeMap()
-{
- if ( fOptions.generatedMapPath() != NULL ) {
- FILE* mapFile = fopen(fOptions.generatedMapPath(), "w");
- if ( mapFile != NULL ) {
- // write output path
- fprintf(mapFile, "# Path: %s\n", fFilePath);
- // write output architecure
- fprintf(mapFile, "# Arch: %s\n", getArchString());
- // write UUID
- if ( fUUIDAtom != NULL ) {
- const uint8_t* uuid = fUUIDAtom->getUUID();
- fprintf(mapFile, "# UUID: %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X \n",
- uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
- uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]);
- }
- // write table of object files
- std::map<ObjectFile::Reader*, uint32_t> readerToOrdinal;
- std::map<uint32_t, ObjectFile::Reader*> ordinalToReader;
- std::map<ObjectFile::Reader*, uint32_t> readerToFileOrdinal;
- for (std::vector<SegmentInfo*>::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) {
- std::vector<SectionInfo*>& sectionInfos = (*segit)->fSections;
- for (std::vector<SectionInfo*>::iterator secit = sectionInfos.begin(); secit != sectionInfos.end(); ++secit) {
- if ( ! (*secit)->fVirtualSection ) {
- std::vector<ObjectFile::Atom*>& sectionAtoms = (*secit)->fAtoms;
- for (std::vector<ObjectFile::Atom*>::iterator ait = sectionAtoms.begin(); ait != sectionAtoms.end(); ++ait) {
- ObjectFile::Reader* reader = (*ait)->getFile();
- uint32_t readerOrdinal = (*ait)->getOrdinal();
- std::map<ObjectFile::Reader*, uint32_t>::iterator pos = readerToOrdinal.find(reader);
- if ( pos == readerToOrdinal.end() ) {
- readerToOrdinal[reader] = readerOrdinal;
- ordinalToReader[readerOrdinal] = reader;
- }
- }
- }
- }
- }
- fprintf(mapFile, "# Object files:\n");
- fprintf(mapFile, "[%3u] %s\n", 0, "linker synthesized");
- uint32_t fileIndex = 0;
- readerToFileOrdinal[this] = fileIndex++;
- for(std::map<uint32_t, ObjectFile::Reader*>::iterator it = ordinalToReader.begin(); it != ordinalToReader.end(); ++it) {
- if ( it->first != 0 ) {
- fprintf(mapFile, "[%3u] %s\n", fileIndex, it->second->getPath());
- readerToFileOrdinal[it->second] = fileIndex++;
- }
- }
- // write table of sections
- fprintf(mapFile, "# Sections:\n");
- fprintf(mapFile, "# Address\tSize \tSegment\tSection\n");
- for (std::vector<SegmentInfo*>::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) {
- std::vector<SectionInfo*>& sectionInfos = (*segit)->fSections;
- for (std::vector<SectionInfo*>::iterator secit = sectionInfos.begin(); secit != sectionInfos.end(); ++secit) {
- if ( ! (*secit)->fVirtualSection ) {
- SectionInfo* sect = *secit;
- fprintf(mapFile, "0x%08llX\t0x%08llX\t%s\t%s\n", sect->getBaseAddress(), sect->fSize,
- (*segit)->fName, sect->fSectionName);
- }
- }
- }
- // write table of symbols
- fprintf(mapFile, "# Symbols:\n");
- fprintf(mapFile, "# Address\tSize \tFile Name\n");
- for (std::vector<SegmentInfo*>::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) {
- std::vector<SectionInfo*>& sectionInfos = (*segit)->fSections;
- for (std::vector<SectionInfo*>::iterator secit = sectionInfos.begin(); secit != sectionInfos.end(); ++secit) {
- if ( ! (*secit)->fVirtualSection ) {
- std::vector<ObjectFile::Atom*>& sectionAtoms = (*secit)->fAtoms;
- bool isCstring = (strcmp((*secit)->fSectionName, "__cstring") == 0);
- for (std::vector<ObjectFile::Atom*>::iterator ait = sectionAtoms.begin(); ait != sectionAtoms.end(); ++ait) {
- ObjectFile::Atom* atom = *ait;
- fprintf(mapFile, "0x%08llX\t0x%08llX\t[%3u] %s\n", atom->getAddress(), atom->getSize(),
- readerToFileOrdinal[atom->getFile()], isCstring ? stringName(atom->getDisplayName()): atom->getDisplayName());
- }
- }
- }
- }
- fclose(mapFile);
- }
- else {
- warning("could not write map file: %s\n", fOptions.generatedMapPath());
- }
- }
-}
-
-static const char* sCleanupFile = NULL;
-static void cleanup(int sig)
-{
- ::signal(sig, SIG_DFL);
- if ( sCleanupFile != NULL ) {
- ::unlink(sCleanupFile);
- }
- if ( sig == SIGINT )
- ::exit(1);
-}
-
-
-template <typename A>
-uint64_t Writer<A>::writeAtoms()
-{
- // for UNIX conformance, error if file exists and is not writable
- if ( (access(fFilePath, F_OK) == 0) && (access(fFilePath, W_OK) == -1) )
- throwf("can't write output file: %s", fFilePath);
-
- int permissions = 0777;
- if ( fOptions.outputKind() == Options::kObjectFile )
- permissions = 0666;
- // Calling unlink first assures the file is gone so that open creates it with correct permissions
- // It also handles the case where fFilePath file is not writable but its directory is
- // And it means we don't have to truncate the file when done writing (in case new is smaller than old)
- (void)unlink(fFilePath);
-
- // try to allocate buffer for entire output file content
- int fd = -1;
- SectionInfo* lastSection = fSegmentInfos.back()->fSections.back();
- uint64_t fileBufferSize = (lastSection->fFileOffset + lastSection->fSize + 4095) & (-4096);
- uint8_t* wholeBuffer = (uint8_t*)calloc(fileBufferSize, 1);
- uint8_t* atomBuffer = NULL;
- bool streaming = false;
- if ( wholeBuffer == NULL ) {
- fd = open(fFilePath, O_CREAT | O_WRONLY | O_TRUNC, permissions);
- if ( fd == -1 )
- throwf("can't open output file for writing: %s, errno=%d", fFilePath, errno);
- atomBuffer = new uint8_t[(fLargestAtomSize+4095) & (-4096)];
- streaming = true;
- // install signal handlers to delete output file if program is killed
- sCleanupFile = fFilePath;
- ::signal(SIGINT, cleanup);
- ::signal(SIGBUS, cleanup);
- ::signal(SIGSEGV, cleanup);
- }
- uint32_t size = 0;
- uint32_t end = 0;
- try {
- for (std::vector<SegmentInfo*>::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) {
- SegmentInfo* curSegment = *segit;
- bool isTextSeg = (strcmp(curSegment->fName, "__TEXT") == 0);
- std::vector<SectionInfo*>& sectionInfos = curSegment->fSections;
- for (std::vector<SectionInfo*>::iterator secit = sectionInfos.begin(); secit != sectionInfos.end(); ++secit) {
- SectionInfo* curSection = *secit;
- std::vector<ObjectFile::Atom*>& sectionAtoms = curSection->fAtoms;
- //printf("writing with max atom size 0x%X\n", fLargestAtomSize);
- //fprintf(stderr, "writing %lu atoms for section %s\n", sectionAtoms.size(), curSection->fSectionName);
- if ( ! curSection->fAllZeroFill ) {
- end = curSection->fFileOffset;
- bool needsNops = isTextSeg && (strcmp(curSection->fSectionName, "__cstring") != 0);
- for (std::vector<ObjectFile::Atom*>::iterator ait = sectionAtoms.begin(); ait != sectionAtoms.end(); ++ait) {
- ObjectFile::Atom* atom = *ait;
- if ( (atom->getDefinitionKind() != ObjectFile::Atom::kExternalDefinition)
- && (atom->getDefinitionKind() != ObjectFile::Atom::kExternalWeakDefinition)
- && (atom->getDefinitionKind() != ObjectFile::Atom::kAbsoluteSymbol) ) {
- uint32_t fileOffset = curSection->fFileOffset + atom->getSectionOffset();
- if ( fileOffset != end ) {
- if ( needsNops ) {
- // fill gaps with no-ops
- if ( streaming )
- writeNoOps(fd, end, fileOffset);
- else
- copyNoOps(&wholeBuffer[end], &wholeBuffer[fileOffset]);
- }
- else if ( streaming ) {
- // zero fill gaps
- if ( (fileOffset-end) == 4 ) {
- uint32_t zero = 0;
- ::pwrite(fd, &zero, 4, end);
- }
- else {
- uint8_t zero = 0x00;
- for (uint32_t p=end; p < fileOffset; ++p)
- ::pwrite(fd, &zero, 1, p);
- }
- }
- }
- uint64_t atomSize = atom->getSize();
- if ( streaming ) {
- if ( atomSize > fLargestAtomSize )
- throwf("ld64 internal error: atom \"%s\"is larger than expected 0x%X > 0x%llX",
- atom->getDisplayName(), atomSize, fLargestAtomSize);
- }
- else {
- if ( fileOffset > fileBufferSize )
- throwf("ld64 internal error: atom \"%s\" has file offset greater thatn expceted 0x%X > 0x%llX",
- atom->getDisplayName(), fileOffset, fileBufferSize);
- }
- uint8_t* buffer = streaming ? atomBuffer : &wholeBuffer[fileOffset];
- end = fileOffset+atomSize;
- // copy raw bytes
- atom->copyRawContent(buffer);
- // apply any fix-ups
- try {
- std::vector<ObjectFile::Reference*>& references = atom->getReferences();
- for (std::vector<ObjectFile::Reference*>::iterator it=references.begin(); it != references.end(); it++) {
- ObjectFile::Reference* ref = *it;
- if ( fOptions.outputKind() == Options::kObjectFile ) {
- // doing ld -r
- // skip fix-ups for undefined targets
- if ( &(ref->getTarget()) != NULL )
- this->fixUpReferenceRelocatable(ref, atom, buffer);
- }
- else {
- // producing final linked image
- this->fixUpReferenceFinal(ref, atom, buffer);
- }
- }
- }
- catch (const char* msg) {
- throwf("%s in %s from %s", msg, atom->getDisplayName(), atom->getFile()->getPath());
- }
- //fprintf(stderr, "writing 0x%08X -> 0x%08X (addr=0x%llX, size=0x%llX), atom %s from %s\n",
- // fileOffset, end, atom->getAddress(), atom->getSize(), atom->getDisplayName(), atom->getFile()->getPath());
- if ( streaming ) {
- // write out
- ::pwrite(fd, buffer, atomSize, fileOffset);
- }
- else {
- if ( (fileOffset + atomSize) > size )
- size = fileOffset + atomSize;
- }
- }
- }
- }
- }
- }
-
- // update content based UUID
- if ( fOptions.getUUIDMode() == Options::kUUIDContent ) {
- uint8_t digest[CC_MD5_DIGEST_LENGTH];
- if ( streaming ) {
- // if output file file did not fit in memory, re-read file to generate md5 hash
- uint32_t kMD5BufferSize = 16*1024;
- uint8_t* md5Buffer = (uint8_t*)::malloc(kMD5BufferSize);
- if ( md5Buffer != NULL ) {
- CC_MD5_CTX md5State;
- CC_MD5_Init(&md5State);
- ::lseek(fd, 0, SEEK_SET);
- ssize_t len;
- while ( (len = ::read(fd, md5Buffer, kMD5BufferSize)) > 0 )
- CC_MD5_Update(&md5State, md5Buffer, len);
- CC_MD5_Final(digest, &md5State);
- ::free(md5Buffer);
- }
- else {
- // if malloc fails, fall back to random uuid
- ::uuid_generate_random(digest);
- }
- fUUIDAtom->setContent(digest);
- uint32_t uuidOffset = ((SectionInfo*)fUUIDAtom->getSection())->fFileOffset + fUUIDAtom->getSectionOffset();
- fUUIDAtom->copyRawContent(atomBuffer);
- ::pwrite(fd, atomBuffer, fUUIDAtom->getSize(), uuidOffset);
- }
- else {
- // if output file fit in memory, just genrate an md5 hash in memory
- #if 1
- // temp hack for building on Tiger
- CC_MD5_CTX md5State;
- CC_MD5_Init(&md5State);
- CC_MD5_Update(&md5State, wholeBuffer, size);
- CC_MD5_Final(digest, &md5State);
- #else
- CC_MD5(wholeBuffer, size, digest);
- #endif
- fUUIDAtom->setContent(digest);
- uint32_t uuidOffset = ((SectionInfo*)fUUIDAtom->getSection())->fFileOffset + fUUIDAtom->getSectionOffset();
- fUUIDAtom->copyRawContent(&wholeBuffer[uuidOffset]);
- }
- }
- }
- catch (...) {
- if ( sCleanupFile != NULL )
- ::unlink(sCleanupFile);
- throw;
- }
-
- // finish up
- if ( streaming ) {
- delete [] atomBuffer;
- close(fd);
- // restore default signal handlers
- sCleanupFile = NULL;
- ::signal(SIGINT, SIG_DFL);
- ::signal(SIGBUS, SIG_DFL);
- ::signal(SIGSEGV, SIG_DFL);
- }
- else {
- // write whole output file in one chunk
- fd = open(fFilePath, O_CREAT | O_WRONLY | O_TRUNC, permissions);
- if ( fd == -1 )
- throwf("can't open output file for writing: %s, errno=%d", fFilePath, errno);
- ::pwrite(fd, wholeBuffer, size, 0);
- close(fd);
- delete [] wholeBuffer;
- }
-
- return end;
-}
-
-template <>
-void Writer<arm>::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const
-{
- int64_t displacement;
- int64_t baseAddr;
- uint32_t instruction;
- uint32_t newInstruction;
- uint64_t targetAddr = 0;
- uint32_t firstDisp;
- uint32_t nextDisp;
- uint32_t opcode;
- bool relocateableExternal = false;
- bool is_bl;
- bool is_blx;
- bool targetIsThumb;
-
- if ( ref->getTargetBinding() != ObjectFile::Reference::kDontBind ) {
- targetAddr = ref->getTarget().getAddress() + ref->getTargetOffset();
- relocateableExternal = (relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal);
- }
-
- uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()];
- switch ( (arm::ReferenceKinds)(ref->getKind()) ) {
- case arm::kNoFixUp:
- case arm::kFollowOn:
- case arm::kGroupSubordinate:
- // do nothing
- break;
- case arm::kPointerWeakImport:
- case arm::kPointer:
- // If this is the lazy pointers section, then set all lazy pointers to
- // point to the dyld stub binding helper.
- if ( ((SectionInfo*)inAtom->getSection())->fAllLazyPointers
- || ((SectionInfo*)inAtom->getSection())->fAllLazyDylibPointers ) {
- switch (ref->getTarget().getDefinitionKind()) {
- case ObjectFile::Atom::kExternalDefinition:
- case ObjectFile::Atom::kExternalWeakDefinition:
- // prebound lazy pointer to another dylib ==> pointer contains zero
- LittleEndian::set32(*fixUp, 0);
- break;
- case ObjectFile::Atom::kTentativeDefinition:
- case ObjectFile::Atom::kRegularDefinition:
- case ObjectFile::Atom::kWeakDefinition:
- case ObjectFile::Atom::kAbsoluteSymbol:
- // prebound lazy pointer to withing this dylib ==> pointer contains address
- if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0) )
- targetAddr |= 1;
- LittleEndian::set32(*fixUp, targetAddr);
- break;
- }
- }
- else if ( relocateableExternal ) {
- if ( fOptions.prebind() ) {
- switch (ref->getTarget().getDefinitionKind()) {
- case ObjectFile::Atom::kExternalDefinition:
- case ObjectFile::Atom::kExternalWeakDefinition:
- // prebound external relocation ==> pointer contains addend
- LittleEndian::set32(*fixUp, ref->getTargetOffset());
- break;
- case ObjectFile::Atom::kTentativeDefinition:
- case ObjectFile::Atom::kRegularDefinition:
- case ObjectFile::Atom::kWeakDefinition:
- // prebound external relocation to internal atom ==> pointer contains target address + addend
- if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0) )
- targetAddr |= 1;
- LittleEndian::set32(*fixUp, targetAddr);
- break;
- case ObjectFile::Atom::kAbsoluteSymbol:
- break;
- }
- }
- else {
- // external relocation ==> pointer contains addend
- LittleEndian::set32(*fixUp, ref->getTargetOffset());
- }
- }
- else {
- // pointer contains target address
- if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0))
- targetAddr |= 1;
- LittleEndian::set32(*fixUp, targetAddr);
- }
- break;
- case arm::kPointerDiff:
- LittleEndian::set32(*fixUp,
- (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) );
- break;
- case arm::kReadOnlyPointer:
- 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 relocation ==> pointer contains addend
- LittleEndian::set32(*fixUp, ref->getTargetOffset());
- break;
- case ObjectFile::Atom::kAbsoluteSymbol:
- // pointer contains target address
- LittleEndian::set32(*fixUp, ref->getTarget().getSectionOffset() + ref->getTargetOffset());
- break;
- }
- break;
- case arm::kBranch24WeakImport:
- case arm::kBranch24:
- displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset());
- // The pc added will be +8 from the pc
- displacement -= 8;
- // fprintf(stderr, "bl/blx fixup to %s at 0x%08llX, displacement = 0x%08llX\n", ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), displacement);
- // max positive displacement is 0x007FFFFF << 2
- // max negative displacement is 0xFF800000 << 2
- if ( (displacement > 33554428LL) || (displacement < (-33554432LL)) ) {
- throwf("b/bl/blx out of range (%lld max is +/-32M) from %s in %s to %s in %s",
- displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(),
- ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath());
- }
- instruction = LittleEndian::get32(*fixUp);
- // Make sure we are calling arm with bl, thumb with blx
- is_bl = ((instruction & 0xFF000000) == 0xEB000000);
- is_blx = ((instruction & 0xFE000000) == 0xFA000000);
- if ( is_bl && ref->getTarget().isThumb() ) {
- uint32_t opcode = 0xFA000000;
- uint32_t disp = (uint32_t)(displacement >> 2) & 0x00FFFFFF;
- uint32_t h_bit = (uint32_t)(displacement << 23) & 0x01000000;
- newInstruction = opcode | h_bit | disp;
- }
- else if ( is_blx && !ref->getTarget().isThumb() ) {
- uint32_t opcode = 0xEB000000;
- uint32_t disp = (uint32_t)(displacement >> 2) & 0x00FFFFFF;
- newInstruction = opcode | disp;
- }
- else if ( !is_bl && !is_blx && ref->getTarget().isThumb() ) {
- throwf("don't know how to convert instruction %x referencing %s to thumb",
- instruction, ref->getTarget().getDisplayName());
- }
- else {
- newInstruction = (instruction & 0xFF000000) | ((uint32_t)(displacement >> 2) & 0x00FFFFFF);
- }
- LittleEndian::set32(*fixUp, newInstruction);
- break;
- case arm::kThumbBranch22WeakImport:
- case arm::kThumbBranch22:
- instruction = LittleEndian::get32(*fixUp);
- is_bl = ((instruction & 0xF8000000) == 0xF8000000);
- is_blx = ((instruction & 0xF8000000) == 0xE8000000);
- targetIsThumb = ref->getTarget().isThumb();
-
- // The pc added will be +4 from the pc
- baseAddr = inAtom->getAddress() + ref->getFixUpOffset() + 4;
- // If the target is not thumb, we will be generating a blx instruction
- // Since blx cannot have the low bit set, set bit[1] of the target to
- // bit[1] of the base address, so that the difference is a multiple of
- // 4 bytes.
- if ( !targetIsThumb ) {
- targetAddr &= -3ULL;
- targetAddr |= (baseAddr & 2LL);
- }
- displacement = targetAddr - baseAddr;
-
- // max positive displacement is 0x003FFFFE
- // max negative displacement is 0xFFC00000
- if ( (displacement > 4194302LL) || (displacement < (-4194304LL)) ) {
- throwf("thumb bl/blx out of range (%lld max is +/-4M) from %s in %s to %s in %s",
- displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(),
- ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath());
- }
- // The instruction is really two instructions:
- // The lower 16 bits are the first instruction, which contains the first
- // 11 bits of the displacement.
- // The upper 16 bits are the second instruction, which contains the next
- // 11 bits of the displacement, as well as differentiating bl and blx.
- {
- firstDisp = (uint32_t)(displacement >> 12) & 0x7FF;
- nextDisp = (uint32_t)(displacement >> 1) & 0x7FF;
- if ( is_bl && !targetIsThumb ) {
- opcode = 0xE800F000;
- }
- else if ( is_blx && targetIsThumb ) {
- opcode = 0xF800F000;
- }
- else if ( !is_bl && !is_blx && !targetIsThumb ) {
- throwf("don't know how to convert instruction %x referencing %s to arm",
- instruction, ref->getTarget().getDisplayName());
- }
- else {
- opcode = instruction & 0xF800F800;
- }
- newInstruction = opcode | (nextDisp << 16) | firstDisp;
- LittleEndian::set32(*fixUp, newInstruction);
- }
- break;
- case arm::kDtraceProbeSite:
- case arm::kDtraceIsEnabledSite:
- if ( inAtom->isThumb() ) {
- // change 32-bit blx call site to two thumb NOPs
- LittleEndian::set32(*fixUp, 0x46C046C0);
- }
- else {
- // change call site to a NOP
- LittleEndian::set32(*fixUp, 0xE1A00000);
- }
- break;
- case arm::kDtraceTypeReference:
- case arm::kDtraceProbe:
- // nothing to fix up
- break;
- default:
- throw "boom shaka laka";
- }
-}
-
-template <>
-void Writer<arm>::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const
-{
- int64_t displacement;
- uint32_t instruction;
- uint32_t newInstruction;
- uint64_t targetAddr = 0;
- int64_t baseAddr;
- uint32_t firstDisp;
- uint32_t nextDisp;
- uint32_t opcode;
- bool relocateableExternal = false;
- bool is_bl;
- bool is_blx;
- bool targetIsThumb;
-
- if ( ref->getTargetBinding() != ObjectFile::Reference::kDontBind ) {
- targetAddr = ref->getTarget().getAddress() + ref->getTargetOffset();
- relocateableExternal = this->makesExternalRelocatableReference(ref->getTarget());
- }
-
- uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()];
- switch ( (arm::ReferenceKinds)(ref->getKind()) ) {
- case arm::kNoFixUp:
- case arm::kFollowOn:
- case arm::kGroupSubordinate:
- // do nothing
- break;
- case arm::kPointer:
- case arm::kReadOnlyPointer:
- case arm::kPointerWeakImport:
- {
- if ( ((SectionInfo*)inAtom->getSection())->fAllNonLazyPointers ) {
- // indirect symbol table has INDIRECT_SYMBOL_LOCAL, so we must put address in content
- if ( this->indirectSymbolIsLocal(ref) )
- LittleEndian::set32(*fixUp, targetAddr);
- else
- LittleEndian::set32(*fixUp, 0);
- }
- else if ( relocateableExternal ) {
- if ( fOptions.prebind() ) {
- switch (ref->getTarget().getDefinitionKind()) {
- case ObjectFile::Atom::kExternalDefinition:
- case ObjectFile::Atom::kExternalWeakDefinition:
- // prebound external relocation ==> pointer contains addend
- LittleEndian::set32(*fixUp, ref->getTargetOffset());
- break;
- case ObjectFile::Atom::kTentativeDefinition:
- case ObjectFile::Atom::kRegularDefinition:
- case ObjectFile::Atom::kWeakDefinition:
- // prebound external relocation to internal atom ==> pointer contains target address + addend
- LittleEndian::set32(*fixUp, targetAddr);
- break;
- case ObjectFile::Atom::kAbsoluteSymbol:
- break;
- }
- }
- }
- else {
- // internal relocation
- if ( ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition ) {
- // pointer contains target address
- if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0))
- targetAddr |= 1;
- LittleEndian::set32(*fixUp, targetAddr);
- }
- else {
- // pointer contains addend
- LittleEndian::set32(*fixUp, ref->getTargetOffset());
- }
- }
- }
- break;
- case arm::kPointerDiff:
- LittleEndian::set32(*fixUp,
- (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) );
- break;
- case arm::kDtraceProbeSite:
- case arm::kDtraceIsEnabledSite:
- case arm::kBranch24WeakImport:
- case arm::kBranch24:
- displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset());
- // The pc added will be +8 from the pc
- displacement -= 8;
- // fprintf(stderr, "b/bl/blx fixup to %s at 0x%08llX, displacement = 0x%08llX\n", ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), displacement);
- if ( relocateableExternal ) {
- // doing "ld -r" to an external symbol
- // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target
- displacement -= ref->getTarget().getAddress();
- }
- else {
- // max positive displacement is 0x007FFFFF << 2
- // max negative displacement is 0xFF800000 << 2
- if ( (displacement > 33554428LL) || (displacement < (-33554432LL)) ) {
- throwf("arm b/bl/blx out of range (%lld max is +/-32M) from %s in %s to %s in %s",
- displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(),
- ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath());
- }
- }
- instruction = LittleEndian::get32(*fixUp);
- // Make sure we are calling arm with bl, thumb with blx
- is_bl = ((instruction & 0xFF000000) == 0xEB000000);
- is_blx = ((instruction & 0xFE000000) == 0xFA000000);
- if ( is_bl && ref->getTarget().isThumb() ) {
- uint32_t opcode = 0xFA000000;
- uint32_t disp = (uint32_t)(displacement >> 2) & 0x00FFFFFF;
- uint32_t h_bit = (uint32_t)(displacement << 23) & 0x01000000;
- newInstruction = opcode | h_bit | disp;
- }
- else if ( is_blx && !ref->getTarget().isThumb() ) {
- uint32_t opcode = 0xEB000000;
- uint32_t disp = (uint32_t)(displacement >> 2) & 0x00FFFFFF;
- newInstruction = opcode | disp;
- }
- else if ( !is_bl && !is_blx && ref->getTarget().isThumb() ) {
- throwf("don't know how to convert instruction %x referencing %s to thumb",
- instruction, ref->getTarget().getDisplayName());
- }
- else {
- newInstruction = (instruction & 0xFF000000) | ((uint32_t)(displacement >> 2) & 0x00FFFFFF);
- }
- LittleEndian::set32(*fixUp, newInstruction);
- break;
- case arm::kThumbBranch22WeakImport:
- case arm::kThumbBranch22:
- instruction = LittleEndian::get32(*fixUp);
- is_bl = ((instruction & 0xF8000000) == 0xF8000000);
- is_blx = ((instruction & 0xF8000000) == 0xE8000000);
- targetIsThumb = ref->getTarget().isThumb();
-
- // The pc added will be +4 from the pc
- baseAddr = inAtom->getAddress() + ref->getFixUpOffset() + 4;
- // If the target is not thumb, we will be generating a blx instruction
- // Since blx cannot have the low bit set, set bit[1] of the target to
- // bit[1] of the base address, so that the difference is a multiple of
- // 4 bytes.
- if (!targetIsThumb) {
- targetAddr &= -3ULL;
- targetAddr |= (baseAddr & 2LL);
- }
- displacement = targetAddr - baseAddr;
-
- //fprintf(stderr, "thumb %s fixup to %s at 0x%08llX, baseAddr = 0x%08llX, displacement = 0x%08llX, %d\n", is_blx ? "blx" : "bl", ref->getTarget().getDisplayName(), targetAddr, baseAddr, displacement, targetIsThumb);
- if ( relocateableExternal ) {
- // doing "ld -r" to an external symbol
- // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target
- displacement -= ref->getTarget().getAddress();
- }
- else {
- // max positive displacement is 0x003FFFFE
- // max negative displacement is 0xFFC00000
- if ( (displacement > 4194302LL) || (displacement < (-4194304LL)) ) {
- throwf("thumb bl/blx out of range (%lld max is +/-4M) from %s in %s to %s in %s",
- displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(),
- ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath());
- }
- }
- // The instruction is really two instructions:
- // The lower 16 bits are the first instruction, which contains the first
- // 11 bits of the displacement.
- // The upper 16 bits are the second instruction, which contains the next
- // 11 bits of the displacement, as well as differentiating bl and blx.
- firstDisp = (uint32_t)(displacement >> 12) & 0x7FF;
- nextDisp = (uint32_t)(displacement >> 1) & 0x7FF;
- if ( is_bl && !targetIsThumb ) {
- opcode = 0xE800F000;
- }
- else if ( is_blx && targetIsThumb ) {
- opcode = 0xF800F000;
- }
- else if ( !is_bl && !is_blx && !targetIsThumb ) {
- throwf("don't know how to convert instruction %x referencing %s to arm",
- instruction, ref->getTarget().getDisplayName());
- }
- else {
- opcode = instruction & 0xF800F800;
- }
- newInstruction = opcode | (nextDisp << 16) | firstDisp;
- LittleEndian::set32(*fixUp, newInstruction);
- break;
- case arm::kDtraceProbe:
- case arm::kDtraceTypeReference:
- // nothing to fix up
- break;
- }
-}
-
-template <>
-void Writer<x86>::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const
-{
- uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()];
- uint8_t* dtraceProbeSite;
- const int64_t kTwoGigLimit = 0x7FFFFFFF;
- const int64_t kSixtyFourKiloLimit = 0x7FFF;
- const int64_t kOneTwentyEightLimit = 0x7F;
- int64_t displacement;
- x86::ReferenceKinds kind = (x86::ReferenceKinds)(ref->getKind());
- switch ( kind ) {
- case x86::kNoFixUp:
- case x86::kFollowOn:
- case x86::kGroupSubordinate:
- // do nothing
- break;
- case x86::kPointerWeakImport:
- case x86::kPointer:
- {
- if ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal ) {
- if ( fOptions.prebind() ) {
- switch (ref->getTarget().getDefinitionKind()) {
- case ObjectFile::Atom::kExternalDefinition:
- case ObjectFile::Atom::kExternalWeakDefinition:
- // prebound external relocation ==> pointer contains addend
- LittleEndian::set32(*fixUp, ref->getTargetOffset());
- break;
- case ObjectFile::Atom::kTentativeDefinition:
- case ObjectFile::Atom::kRegularDefinition:
- case ObjectFile::Atom::kWeakDefinition:
- // prebound external relocation to internal atom ==> pointer contains target address + addend
- LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset());
- break;
- case ObjectFile::Atom::kAbsoluteSymbol:
- break;
- }
- }
- else {
- // external relocation ==> pointer contains addend
- LittleEndian::set32(*fixUp, ref->getTargetOffset());
- }
- }
- else {
- // pointer contains target address
- //printf("Atom::fixUpReferenceFinal() target.name=%s, target.address=0x%08llX\n", target.getDisplayName(), target.getAddress());
- LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset());
- }
- }
- break;
- case x86::kPointerDiff:
- displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset());
- LittleEndian::set32(*fixUp, (uint32_t)displacement);
- break;
- case x86::kPointerDiff16:
- displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset());
- if ( (displacement > kSixtyFourKiloLimit) || (displacement < -(kSixtyFourKiloLimit)) )
- throwf("16-bit pointer diff out of range in %s", inAtom->getDisplayName());
- LittleEndian::set16(*((uint16_t*)fixUp), (uint16_t)displacement);
- break;
- case x86::kDtraceProbeSite:
- // change call site to a NOP
- dtraceProbeSite = (uint8_t*)fixUp;
- dtraceProbeSite[-1] = 0x90; // 1-byte nop
- dtraceProbeSite[0] = 0x0F; // 4-byte nop
- dtraceProbeSite[1] = 0x1F;
- dtraceProbeSite[2] = 0x40;
- dtraceProbeSite[3] = 0x00;
- break;
- case x86::kDtraceIsEnabledSite:
- // change call site to a clear eax
- dtraceProbeSite = (uint8_t*)fixUp;
- dtraceProbeSite[-1] = 0x33; // xorl eax,eax
- dtraceProbeSite[0] = 0xC0;
- dtraceProbeSite[1] = 0x90; // 1-byte nop
- dtraceProbeSite[2] = 0x90; // 1-byte nop
- dtraceProbeSite[3] = 0x90; // 1-byte nop
- break;
- case x86::kPCRel32WeakImport:
- case x86::kPCRel32:
- case x86::kPCRel16:
- case x86::kPCRel8:
- displacement = 0;
- switch ( ref->getTarget().getDefinitionKind() ) {
- case ObjectFile::Atom::kRegularDefinition:
- case ObjectFile::Atom::kWeakDefinition:
- displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4);
- break;
- case ObjectFile::Atom::kExternalDefinition:
- case ObjectFile::Atom::kExternalWeakDefinition:
- throw "codegen problem, can't use rel32 to external symbol";
- case ObjectFile::Atom::kTentativeDefinition:
- displacement = 0;
- break;
- case ObjectFile::Atom::kAbsoluteSymbol:
- displacement = (ref->getTarget().getSectionOffset() + ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4);
- break;
- }
- if ( kind == x86::kPCRel8 ) {
- if ( (displacement > kOneTwentyEightLimit) || (displacement < -(kOneTwentyEightLimit)) ) {
- //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());
- throwf("rel8 out of range in %s", inAtom->getDisplayName());
- }
- *(int8_t*)fixUp = (int8_t)displacement;
- }
- else if ( kind == x86::kPCRel16 ) {
- if ( (displacement > kSixtyFourKiloLimit) || (displacement < -(kSixtyFourKiloLimit)) ) {
- //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());
- throwf("rel16 out of range in %s", inAtom->getDisplayName());
- }
- LittleEndian::set16(*((uint16_t*)fixUp), (uint16_t)displacement);
- }
- else {
- if ( (displacement > kTwoGigLimit) || (displacement < (-kTwoGigLimit)) ) {
- //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());
- throwf("rel32 out of range in %s", inAtom->getDisplayName());
- }
- 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 relocation ==> pointer contains addend
- LittleEndian::set32(*fixUp, ref->getTargetOffset());
- break;
- case ObjectFile::Atom::kAbsoluteSymbol:
- // pointer contains target address
- LittleEndian::set32(*fixUp, ref->getTarget().getSectionOffset() + ref->getTargetOffset());
- break;
- }
- break;
- case x86::kDtraceTypeReference:
- case x86::kDtraceProbe:
- // nothing to fix up
- break;
- }
-}
-
-
-
-template <>
-void Writer<x86>::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const
-{
- const int64_t kTwoGigLimit = 0x7FFFFFFF;
- const int64_t kSixtyFourKiloLimit = 0x7FFF;
- const int64_t kOneTwentyEightLimit = 0x7F;
- uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()];
- bool isExtern = this->makesExternalRelocatableReference(ref->getTarget());
- int64_t displacement;
- x86::ReferenceKinds kind = (x86::ReferenceKinds)(ref->getKind());
- switch ( kind ) {
- case x86::kNoFixUp:
- case x86::kFollowOn:
- case x86::kGroupSubordinate:
- // do nothing
- break;
- case x86::kPointer:
- case x86::kPointerWeakImport:
- case x86::kAbsolute32:
- {
- if ( isExtern ) {
- // external relocation ==> pointer contains addend
- LittleEndian::set32(*fixUp, ref->getTargetOffset());
- }
- else if ( ((SectionInfo*)inAtom->getSection())->fAllNonLazyPointers ) {
- // if INDIRECT_SYMBOL_LOCAL the content is pointer, else it is zero
- if ( this->indirectSymbolIsLocal(ref) )
- LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset());
- else
- LittleEndian::set32(*fixUp, 0);
- }
- else if ( ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition ) {
- // internal relocation => pointer contains target address
- LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset());
- }
- else {
- // internal relocation to tentative ==> pointer contains addend
- LittleEndian::set32(*fixUp, ref->getTargetOffset());
- }
- }
- break;
- case x86::kPointerDiff:
- displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset());
- LittleEndian::set32(*fixUp, (uint32_t)displacement);
- break;
- case x86::kPointerDiff16:
- displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset());
- if ( (displacement > kSixtyFourKiloLimit) || (displacement < -(kSixtyFourKiloLimit)) )
- throwf("16-bit pointer diff out of range in %s", inAtom->getDisplayName());
- LittleEndian::set16(*((uint16_t*)fixUp), (uint16_t)displacement);
- break;
- case x86::kPCRel8:
- case x86::kPCRel16:
- case x86::kPCRel32:
- case x86::kPCRel32WeakImport:
- case x86::kDtraceProbeSite:
- case x86::kDtraceIsEnabledSite:
- {
- if ( isExtern )
- displacement = ref->getTargetOffset() - (inAtom->getAddress() + ref->getFixUpOffset() + 4);
- else
- displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4);
- if ( kind == x86::kPCRel8 ) {
- displacement += 3;
- if ( (displacement > kOneTwentyEightLimit) || (displacement < -(kOneTwentyEightLimit)) ) {
- //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());
- throwf("rel8 out of range (%lld)in %s", displacement, inAtom->getDisplayName());
- }
- int8_t byte = (int8_t)displacement;
- *((int8_t*)fixUp) = byte;
- }
- else if ( kind == x86::kPCRel16 ) {
- displacement += 2;
- if ( (displacement > kSixtyFourKiloLimit) || (displacement < -(kSixtyFourKiloLimit)) ) {
- //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());
- throwf("rel16 out of range in %s", inAtom->getDisplayName());
- }
- int16_t word = (int16_t)displacement;
- LittleEndian::set16(*((uint16_t*)fixUp), word);
- }
- else {
- if ( (displacement > kTwoGigLimit) || (displacement < (-kTwoGigLimit)) ) {
- //fprintf(stderr, "call out of range, displacement=ox%llX, from %s in %s to %s in %s\n", displacement,
- // inAtom->getDisplayName(), inAtom->getFile()->getPath(), ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath());
- throwf("rel32 out of range in %s", inAtom->getDisplayName());
- }
- LittleEndian::set32(*fixUp, (int32_t)displacement);
- }
- }
- break;
- case x86::kDtraceProbe:
- case x86::kDtraceTypeReference:
- // nothing to fix up
- break;
- }
-}
-
-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()];
- uint8_t* dtraceProbeSite;
- int64_t displacement = 0;
- switch ( (x86_64::ReferenceKinds)(ref->getKind()) ) {
- case x86_64::kNoFixUp:
- case x86_64::kFollowOn:
- case x86_64::kGroupSubordinate:
- // 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 relocation ==> 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::kPCRel32GOTLoad:
- case x86_64::kPCRel32GOTLoadWeakImport:
- // if GOT entry was optimized away, change movq instruction to a leaq
- if ( std::find(fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end(), &(ref->getTarget())) == fAllSynthesizedNonLazyPointers.end() ) {
- //fprintf(stderr, "GOT for %s optimized away\n", ref->getTarget().getDisplayName());
- uint8_t* opcodes = (uint8_t*)fixUp;
- if ( opcodes[-2] != 0x8B )
- throw "GOT load reloc does not point to a movq instruction";
- opcodes[-2] = 0x8D;
- }
- // fall into general rel32 case
- case x86_64::kBranchPCRel32WeakImport:
- case x86_64::kBranchPCRel32:
- case x86_64::kBranchPCRel8:
- 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:
- 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::kAbsoluteSymbol:
- displacement = (ref->getTarget().getSectionOffset() + (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;
- case x86_64::kBranchPCRel8:
- displacement += 3;
- break;
- }
- if ( ref->getKind() == x86_64::kBranchPCRel8 ) {
- if ( (displacement > 127) || (displacement < (-128)) ) {
- fprintf(stderr, "branch out of range from %s (%llX) in %s to %s (%llX) in %s\n",
- inAtom->getDisplayName(), inAtom->getAddress(), inAtom->getFile()->getPath(), ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), ref->getTarget().getFile()->getPath());
- throw "rel8 out of range";
- }
- *((int8_t*)fixUp) = (int8_t)displacement;
- }
- else {
- if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) {
- fprintf(stderr, "call out of range from %s (%llX) in %s to %s (%llX) in %s\n",
- inAtom->getDisplayName(), inAtom->getAddress(), inAtom->getFile()->getPath(), ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), ref->getTarget().getFile()->getPath());
- throw "rel32 out of range";
- }
- LittleEndian::set32(*((uint32_t*)fixUp), (int32_t)displacement);
- }
- break;
- case x86_64::kDtraceProbeSite:
- // change call site to a NOP
- dtraceProbeSite = (uint8_t*)fixUp;
- dtraceProbeSite[-1] = 0x90; // 1-byte nop
- dtraceProbeSite[0] = 0x0F; // 4-byte nop
- dtraceProbeSite[1] = 0x1F;
- dtraceProbeSite[2] = 0x40;
- dtraceProbeSite[3] = 0x00;
- break;
- case x86_64::kDtraceIsEnabledSite:
- // change call site to a clear eax
- dtraceProbeSite = (uint8_t*)fixUp;
- dtraceProbeSite[-1] = 0x48; // xorq eax,eax
- dtraceProbeSite[0] = 0x33;
- dtraceProbeSite[1] = 0xC0;
- dtraceProbeSite[2] = 0x90; // 1-byte nop
- dtraceProbeSite[3] = 0x90; // 1-byte nop
- break;
- case x86_64::kDtraceTypeReference:
- case x86_64::kDtraceProbe:
- // nothing to fix up
- 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 = this->makesExternalRelocatableReference(ref->getTarget());
- 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:
- case x86_64::kGroupSubordinate:
- // do nothing
- break;
- case x86_64::kPointer:
- case x86_64::kPointerWeakImport:
- {
- if ( external ) {
- // external relocation ==> 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::kDtraceProbeSite:
- case x86_64::kDtraceIsEnabledSite:
- 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::kBranchPCRel8:
- // 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() + 1);
- }
- if ( (displacement > 127) || (displacement < (-128)) ) {
- //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 "rel8 out of range";
- }
- *((int8_t*)fixUp) = (int8_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;
- case x86_64::kDtraceTypeReference:
- case x86_64::kDtraceProbe:
- // nothing to fix up
- break;
- }
-}
-
-template <>
-void Writer<ppc>::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const
-{
- fixUpReference_powerpc(ref, inAtom, buffer, true);
-}
-
-template <>
-void Writer<ppc64>::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const
-{
- fixUpReference_powerpc(ref, inAtom, buffer, true);
-}
-
-template <>
-void Writer<ppc>::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const
-{
- fixUpReference_powerpc(ref, inAtom, buffer, false);
-}
-
-template <>
-void Writer<ppc64>::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const
-{
- fixUpReference_powerpc(ref, inAtom, buffer, false);
-}
-
-//
-// ppc and ppc64 are mostly the same, so they share a template specialzation
-//
-template <typename A>
-void Writer<A>::fixUpReference_powerpc(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[], bool finalLinkedImage) const
-{
- uint32_t instruction;
- uint32_t newInstruction;
- int64_t displacement;
- uint64_t targetAddr = 0;
- uint64_t picBaseAddr;
- uint16_t instructionLowHalf;
- uint16_t instructionHighHalf;
- uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()];
- pint_t* fixUpPointer = (pint_t*)&buffer[ref->getFixUpOffset()];
- bool relocateableExternal = false;
- const int64_t picbase_twoGigLimit = 0x80000000;
-
- if ( ref->getTargetBinding() != ObjectFile::Reference::kDontBind ) {
- targetAddr = ref->getTarget().getAddress() + ref->getTargetOffset();
- if ( finalLinkedImage )
- relocateableExternal = (relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal);
- else
- relocateableExternal = this->makesExternalRelocatableReference(ref->getTarget());
- }
-
- switch ( (typename A::ReferenceKinds)(ref->getKind()) ) {
- case A::kNoFixUp:
- case A::kFollowOn:
- case A::kGroupSubordinate:
- // do nothing
- break;
- case A::kPointerWeakImport:
- case A::kPointer:
- {
- //fprintf(stderr, "fixUpReferenceFinal: %s reference to %s\n", this->getDisplayName(), target.getDisplayName());
- if ( finalLinkedImage && (((SectionInfo*)inAtom->getSection())->fAllLazyPointers
- || ((SectionInfo*)inAtom->getSection())->fAllLazyDylibPointers) ) {
- switch (ref->getTarget().getDefinitionKind()) {
- case ObjectFile::Atom::kExternalDefinition:
- case ObjectFile::Atom::kExternalWeakDefinition:
- // prebound lazy pointer to another dylib ==> pointer contains zero
- P::setP(*fixUpPointer, 0);
- break;
- case ObjectFile::Atom::kTentativeDefinition:
- case ObjectFile::Atom::kRegularDefinition:
- case ObjectFile::Atom::kWeakDefinition:
- case ObjectFile::Atom::kAbsoluteSymbol:
- // prebound lazy pointer to withing this dylib ==> pointer contains address
- P::setP(*fixUpPointer, targetAddr);
- break;
- }
- }
- else if ( !finalLinkedImage && ((SectionInfo*)inAtom->getSection())->fAllNonLazyPointers ) {
- // if INDIRECT_SYMBOL_LOCAL the content is pointer, else it is zero
- if ( this->indirectSymbolIsLocal(ref) )
- P::setP(*fixUpPointer, targetAddr);
- else
- P::setP(*fixUpPointer, 0);
- }
- else if ( relocateableExternal ) {
- if ( fOptions.prebind() ) {
- switch (ref->getTarget().getDefinitionKind()) {
- case ObjectFile::Atom::kExternalDefinition:
- case ObjectFile::Atom::kExternalWeakDefinition:
- // prebound external relocation ==> pointer contains addend
- P::setP(*fixUpPointer, ref->getTargetOffset());
- break;
- case ObjectFile::Atom::kTentativeDefinition:
- case ObjectFile::Atom::kRegularDefinition:
- case ObjectFile::Atom::kWeakDefinition:
- // prebound external relocation to internal atom ==> pointer contains target address + addend
- P::setP(*fixUpPointer, targetAddr);
- break;
- case ObjectFile::Atom::kAbsoluteSymbol:
- break;
- }
- }
- else {
- // external relocation ==> pointer contains addend
- P::setP(*fixUpPointer, ref->getTargetOffset());
- }
- }
- else {
- // internal relocation
- if ( finalLinkedImage || (ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition) ) {
- // pointer contains target address
- //printf("Atom::fixUpReference_powerpc() target.name=%s, target.address=0x%08llX\n", ref->getTarget().getDisplayName(), targetAddr);
- P::setP(*fixUpPointer, targetAddr);
- }
- else {
- // pointer contains addend
- P::setP(*fixUpPointer, ref->getTargetOffset());
- }
- }
- }
- break;
- case A::kPointerDiff64:
- P::setP(*fixUpPointer, targetAddr - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) );
- break;
- case A::kPointerDiff32:
- P::E::set32(*fixUp, targetAddr - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) );
- break;
- case A::kPointerDiff16:
- P::E::set16(*((uint16_t*)fixUp), targetAddr - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) );
- break;
- case A::kDtraceProbeSite:
- if ( finalLinkedImage ) {
- // change call site to a NOP
- BigEndian::set32(*fixUp, 0x60000000);
- }
- else {
- // set bl instuction to branch to address zero in .o file
- int64_t displacement = ref->getTargetOffset() - (inAtom->getAddress() + ref->getFixUpOffset());
- instruction = BigEndian::get32(*fixUp);
- newInstruction = (instruction & 0xFC000003) | ((uint32_t)displacement & 0x03FFFFFC);
- BigEndian::set32(*fixUp, newInstruction);
- }
- break;
- case A::kDtraceIsEnabledSite:
- if ( finalLinkedImage ) {
- // change call site to a li r3,0
- BigEndian::set32(*fixUp, 0x38600000);
- }
- else {
- // set bl instuction to branch to address zero in .o file
- int64_t displacement = ref->getTargetOffset() - (inAtom->getAddress() + ref->getFixUpOffset());
- instruction = BigEndian::get32(*fixUp);
- newInstruction = (instruction & 0xFC000003) | ((uint32_t)displacement & 0x03FFFFFC);
- BigEndian::set32(*fixUp, newInstruction);
- }
- break;
- case A::kBranch24WeakImport:
- case A::kBranch24:
- {
- //fprintf(stderr, "bl fixup to %s at 0x%08llX, ", target.getDisplayName(), target.getAddress());
- int64_t displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset());
- if ( relocateableExternal ) {
- // doing "ld -r" to an external symbol
- // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target
- displacement -= ref->getTarget().getAddress();
- }
- else {
- const int64_t bl_eightMegLimit = 0x00FFFFFF;
- if ( (displacement > bl_eightMegLimit) || (displacement < (-bl_eightMegLimit)) ) {
- //fprintf(stderr, "bl out of range (%lld max is +/-16M) from %s in %s to %s in %s\n", displacement, this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath());
- throwf("bl out of range (%lld max is +/-16M) from %s at 0x%08llX in %s of %s to %s at 0x%08llX in %s of %s",
- displacement, inAtom->getDisplayName(), inAtom->getAddress(), inAtom->getSectionName(), inAtom->getFile()->getPath(),
- ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), ref->getTarget().getSectionName(), ref->getTarget().getFile()->getPath());
- }
- }
- instruction = BigEndian::get32(*fixUp);
- newInstruction = (instruction & 0xFC000003) | ((uint32_t)displacement & 0x03FFFFFC);
- //fprintf(stderr, "bl fixup: 0x%08X -> 0x%08X\n", instruction, newInstruction);
- BigEndian::set32(*fixUp, newInstruction);
- }
- break;
- case A::kBranch14:
- {
- int64_t displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset());
- if ( relocateableExternal ) {
- // doing "ld -r" to an external symbol
- // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target
- displacement -= ref->getTarget().getAddress();
- }
- const int64_t b_sixtyFourKiloLimit = 0x0000FFFF;
- if ( (displacement > b_sixtyFourKiloLimit) || (displacement < (-b_sixtyFourKiloLimit)) ) {
- //fprintf(stderr, "bl out of range (%lld max is +/-16M) from %s in %s to %s in %s\n", displacement, this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath());
- throwf("bcc out of range (%lld max is +/-64K) from %s in %s to %s in %s",
- displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(),
- ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath());
- }
-
- //fprintf(stderr, "bcc fixup displacement=0x%08llX, atom.addr=0x%08llX, atom.offset=0x%08X\n", displacement, inAtom->getAddress(), (uint32_t)ref->getFixUpOffset());
- instruction = BigEndian::get32(*fixUp);
- newInstruction = (instruction & 0xFFFF0003) | ((uint32_t)displacement & 0x0000FFFC);
- //fprintf(stderr, "bc fixup: 0x%08X -> 0x%08X\n", instruction, newInstruction);
- BigEndian::set32(*fixUp, newInstruction);
- }
- break;
- case A::kPICBaseLow16:
- picBaseAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset();
- displacement = targetAddr - picBaseAddr;
- if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) )
- throw "32-bit pic-base out of range";
- instructionLowHalf = (displacement & 0xFFFF);
- instruction = BigEndian::get32(*fixUp);
- newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf;
- BigEndian::set32(*fixUp, newInstruction);
- break;
- case A::kPICBaseLow14:
- picBaseAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset();
- displacement = targetAddr - picBaseAddr;
- if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) )
- throw "32-bit pic-base out of range";
- if ( (displacement & 0x3) != 0 )
- throwf("bad offset (0x%08X) for lo14 instruction pic-base fix-up", (uint32_t)displacement);
- instructionLowHalf = (displacement & 0xFFFC);
- instruction = BigEndian::get32(*fixUp);
- newInstruction = (instruction & 0xFFFF0003) | instructionLowHalf;
- BigEndian::set32(*fixUp, newInstruction);
- break;
- case A::kPICBaseHigh16:
- picBaseAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset();
- displacement = targetAddr - picBaseAddr;
- if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) )
- throw "32-bit pic-base out of range";
- instructionLowHalf = displacement >> 16;
- if ( (displacement & 0x00008000) != 0 )
- ++instructionLowHalf;
- instruction = BigEndian::get32(*fixUp);
- newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf;
- BigEndian::set32(*fixUp, newInstruction);
- break;
- case A::kAbsLow16:
- if ( relocateableExternal && !finalLinkedImage )
- targetAddr -= ref->getTarget().getAddress();
- instructionLowHalf = (targetAddr & 0xFFFF);
- instruction = BigEndian::get32(*fixUp);
- newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf;
- BigEndian::set32(*fixUp, newInstruction);
- break;
- case A::kAbsLow14:
- if ( relocateableExternal && !finalLinkedImage )
- targetAddr -= ref->getTarget().getAddress();
- if ( (targetAddr & 0x3) != 0 )
- throw "bad address for absolute lo14 instruction fix-up";
- instructionLowHalf = (targetAddr & 0xFFFF);
- instruction = BigEndian::get32(*fixUp);
- newInstruction = (instruction & 0xFFFF0003) | instructionLowHalf;
- BigEndian::set32(*fixUp, newInstruction);
- break;
- case A::kAbsHigh16:
- if ( relocateableExternal ) {
- if ( finalLinkedImage ) {
- switch (ref->getTarget().getDefinitionKind()) {
- case ObjectFile::Atom::kExternalDefinition:
- case ObjectFile::Atom::kExternalWeakDefinition:
- throwf("absolute address to symbol %s in a different linkage unit not supported", ref->getTargetName());
- break;
- case ObjectFile::Atom::kTentativeDefinition:
- case ObjectFile::Atom::kRegularDefinition:
- case ObjectFile::Atom::kWeakDefinition:
- // use target address
- break;
- case ObjectFile::Atom::kAbsoluteSymbol:
- targetAddr = ref->getTarget().getSectionOffset();
- break;
- }
- }
- else {
- targetAddr -= ref->getTarget().getAddress();
- }
- }
- instructionHighHalf = (targetAddr >> 16);
- instruction = BigEndian::get32(*fixUp);
- newInstruction = (instruction & 0xFFFF0000) | instructionHighHalf;
- BigEndian::set32(*fixUp, newInstruction);
- break;
- case A::kAbsHigh16AddLow:
- if ( relocateableExternal ) {
- if ( finalLinkedImage ) {
- switch (ref->getTarget().getDefinitionKind()) {
- case ObjectFile::Atom::kExternalDefinition:
- case ObjectFile::Atom::kExternalWeakDefinition:
- throwf("absolute address to symbol %s in a different linkage unit not supported", ref->getTargetName());
- break;
- case ObjectFile::Atom::kTentativeDefinition:
- case ObjectFile::Atom::kRegularDefinition:
- case ObjectFile::Atom::kWeakDefinition:
- // use target address
- break;
- case ObjectFile::Atom::kAbsoluteSymbol:
- targetAddr = ref->getTarget().getSectionOffset();
- break;
- }
- }
- else {
- targetAddr -= ref->getTarget().getAddress();
- }
- }
- if ( targetAddr & 0x00008000 )
- targetAddr += 0x00010000;
- instruction = BigEndian::get32(*fixUp);
- newInstruction = (instruction & 0xFFFF0000) | (targetAddr >> 16);
- BigEndian::set32(*fixUp, newInstruction);
- break;
- case A::kDtraceTypeReference:
- case A::kDtraceProbe:
- // nothing to fix up
- break;
- }
-}
-
-template <>
-bool Writer<ppc>::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref)
-{
- uint8_t kind = ref->getKind();
- switch ( (ppc::ReferenceKinds)kind ) {
- case ppc::kNoFixUp:
- case ppc::kFollowOn:
- case ppc::kGroupSubordinate:
- case ppc::kPointer:
- case ppc::kPointerWeakImport:
- case ppc::kPointerDiff16:
- case ppc::kPointerDiff32:
- case ppc::kPointerDiff64:
- case ppc::kDtraceProbe:
- case ppc::kDtraceProbeSite:
- case ppc::kDtraceIsEnabledSite:
- case ppc::kDtraceTypeReference:
- // these are never used to call external functions
- return false;
- case ppc::kBranch24:
- case ppc::kBranch24WeakImport:
- case ppc::kBranch14:
- // these are used to call external functions
- return true;
- case ppc::kPICBaseLow16:
- case ppc::kPICBaseLow14:
- case ppc::kPICBaseHigh16:
- case ppc::kAbsLow16:
- case ppc::kAbsLow14:
- case ppc::kAbsHigh16:
- case ppc::kAbsHigh16AddLow:
- // these are only used to call external functions
- // in -mlong-branch stubs
- switch ( ref->getTarget().getDefinitionKind() ) {
- case ObjectFile::Atom::kExternalDefinition:
- case ObjectFile::Atom::kExternalWeakDefinition:
- // if the .o file this atom came from has long-branch stubs,
- // then assume these instructions in a stub.
- // Otherwise, these are a direct reference to something (maybe a runtime text reloc)
- return ( inAtom->getFile()->hasLongBranchStubs() );
- case ObjectFile::Atom::kTentativeDefinition:
- case ObjectFile::Atom::kRegularDefinition:
- case ObjectFile::Atom::kWeakDefinition:
- case ObjectFile::Atom::kAbsoluteSymbol:
- return false;
- }
- break;
- }
- return false;
-}
-
-template <>
-bool Writer<arm>::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref)
-{
- uint8_t kind = ref->getKind();
- switch ( (arm::ReferenceKinds)kind ) {
- case arm::kBranch24:
- case arm::kBranch24WeakImport:
- case arm::kThumbBranch22:
- case arm::kThumbBranch22WeakImport:
- return true;
- case arm::kNoFixUp:
- case arm::kFollowOn:
- case arm::kGroupSubordinate:
- case arm::kPointer:
- case arm::kReadOnlyPointer:
- case arm::kPointerWeakImport:
- case arm::kPointerDiff:
- case arm::kDtraceProbe:
- case arm::kDtraceProbeSite:
- case arm::kDtraceIsEnabledSite:
- case arm::kDtraceTypeReference:
- return false;
- }
- return false;
-}
-
-template <>
-bool Writer<ppc64>::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref)
-{
- uint8_t kind = ref->getKind();
- switch ( (ppc64::ReferenceKinds)kind ) {
- case ppc::kNoFixUp:
- case ppc::kFollowOn:
- case ppc::kGroupSubordinate:
- case ppc::kPointer:
- case ppc::kPointerWeakImport:
- case ppc::kPointerDiff16:
- case ppc::kPointerDiff32:
- case ppc::kPointerDiff64:
- case ppc::kPICBaseLow16:
- case ppc::kPICBaseLow14:
- case ppc::kPICBaseHigh16:
- case ppc::kAbsLow16:
- case ppc::kAbsLow14:
- case ppc::kAbsHigh16:
- case ppc::kAbsHigh16AddLow:
- case ppc::kDtraceProbe:
- case ppc::kDtraceProbeSite:
- case ppc::kDtraceIsEnabledSite:
- case ppc::kDtraceTypeReference:
- // these are never used to call external functions
- return false;
- case ppc::kBranch24:
- case ppc::kBranch24WeakImport:
- case ppc::kBranch14:
- // these are used to call external functions
- return true;
- }
- return false;
-}
-
-template <>
-bool Writer<x86>::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref)
-{
- uint8_t kind = ref->getKind();
- return (kind == x86::kPCRel32 || kind == x86::kPCRel32WeakImport);
-}
-
-template <>
-bool Writer<x86_64>::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref)
-{
- uint8_t kind = ref->getKind();
- return (kind == x86_64::kBranchPCRel32 || kind == x86_64::kBranchPCRel32WeakImport);
-}
-
-
-template <>
-bool Writer<ppc>::weakImportReferenceKind(uint8_t kind)
-{
- return (kind == ppc::kBranch24WeakImport || kind == ppc::kPointerWeakImport);
-}
-
-template <>
-bool Writer<ppc64>::weakImportReferenceKind(uint8_t kind)
-{
- return (kind == ppc64::kBranch24WeakImport || kind == ppc64::kPointerWeakImport);
-}
-
-template <>
-bool Writer<x86>::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 <>
-bool Writer<arm>::weakImportReferenceKind(uint8_t kind)
-{
- return (kind == arm::kBranch24WeakImport || kind == arm::kThumbBranch22WeakImport ||
- kind == arm::kPointerWeakImport);
-}
-
-template <>
-bool Writer<ppc>::GOTReferenceKind(uint8_t kind)
-{
- return false;
-}
-
-template <>
-bool Writer<ppc64>::GOTReferenceKind(uint8_t kind)
-{
- return false;
-}
-
-template <>
-bool Writer<x86>::GOTReferenceKind(uint8_t kind)
-{
- 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 <>
-bool Writer<arm>::GOTReferenceKind(uint8_t kind)
-{
- return false;
-}
-
-template <>
-bool Writer<ppc>::optimizableGOTReferenceKind(uint8_t kind)
-{
- return false;
-}
-
-template <>
-bool Writer<ppc64>::optimizableGOTReferenceKind(uint8_t kind)
-{
- return false;
-}
-
-template <>
-bool Writer<x86>::optimizableGOTReferenceKind(uint8_t kind)
-{
- return false;
-}
-
-template <>
-bool Writer<x86_64>::optimizableGOTReferenceKind(uint8_t kind)
-{
- switch ( kind ) {
- case x86_64::kPCRel32GOTLoad:
- case x86_64::kPCRel32GOTLoadWeakImport:
- return true;
- }
- return false;
-}
-
-template <>
-bool Writer<arm>::optimizableGOTReferenceKind(uint8_t kind)
-{
- return false;
-}
-
-// 64-bit architectures never need module table, 32-bit sometimes do for backwards compatiblity
-template <typename A> bool Writer<A>::needsModuleTable() {return fOptions.needsModuleTable(); }
-template <> bool Writer<ppc64>::needsModuleTable() { return false; }
-template <> bool Writer<x86_64>::needsModuleTable() { return false; }
-
-
-template <typename A>
-void Writer<A>::optimizeDylibReferences()
-{
- //fprintf(stderr, "original ordinals table:\n");
- //for (std::map<class ObjectFile::Reader*, uint32_t>::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) {
- // fprintf(stderr, "%u <== %p/%s\n", it->second, it->first, it->first->getPath());
- //}
- // find unused dylibs that can be removed
- std::map<uint32_t, ObjectFile::Reader*> ordinalToReader;
- std::map<ObjectFile::Reader*, ObjectFile::Reader*> readerAliases;
- for (std::map<ObjectFile::Reader*, uint32_t>::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) {
- ObjectFile::Reader* reader = it->first;
- std::map<ObjectFile::Reader*, ObjectFile::Reader*>::iterator aliasPos = fLibraryAliases.find(reader);
- if ( aliasPos != fLibraryAliases.end() ) {
- // already noticed that this reader has same install name as another reader
- readerAliases[reader] = aliasPos->second;
- }
- else if ( !reader->providedExportAtom() && (reader->implicitlyLinked() || fOptions.deadStripDylibs()) ) {
- // this reader can be optimized away
- it->second = 0xFFFFFFFF;
- typename std::map<class ObjectFile::Reader*, class DylibLoadCommandsAtom<A>* >::iterator pos = fLibraryToLoadCommand.find(reader);
- if ( pos != fLibraryToLoadCommand.end() )
- pos->second->optimizeAway();
- }
- else {
- // mark this reader as using it ordinal
- std::map<uint32_t, ObjectFile::Reader*>::iterator pos = ordinalToReader.find(it->second);
- if ( pos == ordinalToReader.end() )
- ordinalToReader[it->second] = reader;
- else
- readerAliases[reader] = pos->second;
- }
- }
- // renumber ordinals (depends on iterator walking in ordinal order)
- // all LC_LAZY_LOAD_DYLIB load commands must have highest ordinals
- uint32_t newOrdinal = 0;
- for (std::map<uint32_t, ObjectFile::Reader*>::iterator it = ordinalToReader.begin(); it != ordinalToReader.end(); ++it) {
- if ( it->first <= fLibraryToOrdinal.size() ) {
- if ( ! it->second->isLazyLoadedDylib() )
- fLibraryToOrdinal[it->second] = ++newOrdinal;
- }
- }
- for (std::map<uint32_t, ObjectFile::Reader*>::iterator it = ordinalToReader.begin(); it != ordinalToReader.end(); ++it) {
- if ( it->first <= fLibraryToOrdinal.size() ) {
- if ( it->second->isLazyLoadedDylib() ) {
- fLibraryToOrdinal[it->second] = ++newOrdinal;
- }
- }
- }
-
- // <rdar://problem/5504954> linker does not error when dylib ordinal exceeds 250
- if ( (newOrdinal >= MAX_LIBRARY_ORDINAL) && (fOptions.nameSpace() == Options::kTwoLevelNameSpace) )
- throwf("two level namespace mach-o files can link with at most %d dylibs, this link would use %d dylibs", MAX_LIBRARY_ORDINAL, newOrdinal);
-
- // add aliases (e.g. -lm points to libSystem.dylib)
- for (std::map<ObjectFile::Reader*, ObjectFile::Reader*>::iterator it = readerAliases.begin(); it != readerAliases.end(); ++it) {
- fLibraryToOrdinal[it->first] = fLibraryToOrdinal[it->second];
- }
-
- //fprintf(stderr, "new ordinals table:\n");
- //for (std::map<class ObjectFile::Reader*, uint32_t>::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) {
- // fprintf(stderr, "%u <== %p/%s\n", it->second, it->first, it->first->getPath());
- //}
-}
-
-
-template <>
-void Writer<arm>::scanForAbsoluteReferences()
-{
- // arm codegen never has absolute references. FIXME: Is this correct?
-}
-
-template <>
-void Writer<x86_64>::scanForAbsoluteReferences()
-{
- // x86_64 codegen never has absolute references
-}
-
-template <>
-void Writer<x86>::scanForAbsoluteReferences()
-{
- // when linking -pie verify there are no absolute addressing, unless -read_only_relocs is also used
- if ( fOptions.positionIndependentExecutable() && !fOptions.allowTextRelocs() ) {
- 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 x86::kAbsolute32:
- throwf("cannot link -pie: -mdynamic-no-pic codegen found in %s from %s", atom->getDisplayName(), atom->getFile()->getPath());
- return;
- }
- }
- }
- }
-}
-
-template <>
-void Writer<ppc>::scanForAbsoluteReferences()
-{
- // when linking -pie verify there are no absolute addressing, unless -read_only_relocs is also used
- if ( fOptions.positionIndependentExecutable() && !fOptions.allowTextRelocs() ) {
- 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 ppc::kAbsLow16:
- case ppc::kAbsLow14:
- case ppc::kAbsHigh16:
- case ppc::kAbsHigh16AddLow:
- throwf("cannot link -pie: -mdynamic-no-pic codegen found in %s from %s", atom->getDisplayName(), atom->getFile()->getPath());
- return;
- }
- }
- }
- }
-}
-
-
-// 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 -mdynamic-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, "__4GBFILL");
- fPageZeroAtom->setSize(0x1000);
- return;
- }
- }
- }
- }
-}
-
-
-template <typename A>
-void Writer<A>::insertDummyStubs()
-{
- // only needed for x86
-}
-
-template <>
-void Writer<x86>::insertDummyStubs()
-{
- // any 5-byte stubs that cross a 32-byte cache line may update incorrectly
- std::vector<class StubAtom<x86>*> betterStubs;
- for (std::vector<class StubAtom<x86>*>::iterator it=fAllSynthesizedStubs.begin(); it != fAllSynthesizedStubs.end(); it++) {
- switch (betterStubs.size() % 64 ) {
- case 12:// stub would occupy 0x3C->0x41
- case 25:// stub would occupy 0x7D->0x82
- case 38:// stub would occupy 0xBE->0xC3
- case 51:// stub would occupy 0xFF->0x04
- betterStubs.push_back(new StubAtom<x86>(*this, *((ObjectFile::Atom*)NULL), false)); //pad with dummy stub
- break;
- }
- betterStubs.push_back(*it);
- }
- // replace
- fAllSynthesizedStubs.clear();
- fAllSynthesizedStubs.insert(fAllSynthesizedStubs.begin(), betterStubs.begin(), betterStubs.end());
-}
-
-template <typename A>
-void Writer<A>::synthesizeStubs()
-{
- switch ( fOptions.outputKind() ) {
- case Options::kObjectFile:
- // these output kinds never have stubs
- return;
- case Options::kStaticExecutable:
- case Options::kDyld:
- case Options::kDynamicLibrary:
- case Options::kDynamicBundle:
- case Options::kDynamicExecutable:
- // try to synthesize stubs for these
- break;
- }
-
- // walk every atom and reference
- 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->getTargetBinding()) {
- case ObjectFile::Reference::kUnboundByName:
- case ObjectFile::Reference::kDontBind:
- break;
- case ObjectFile::Reference::kBoundByName:
- case ObjectFile::Reference::kBoundDirectly:
- ObjectFile::Atom& target = ref->getTarget();
- // build map of which symbols need weak importing
- if ( (target.getDefinitionKind() == ObjectFile::Atom::kExternalDefinition)
- || (target.getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) {
- bool weakImport = this->weakImportReferenceKind(ref->getKind());
- // <rdar://problem/5633081> Obj-C Symbols in Leopard Can't Be Weak Linked
- // dyld in Mac OS X 10.3 and earlier need N_WEAK_REF bit set on undefines to objc symbols
- // in dylibs that are weakly linked.
- if ( (ref->getKind() == A::kNoFixUp) && (strncmp(target.getName(), ".objc_class_name_", 17) == 0) ) {
- typename std::map<class ObjectFile::Reader*, class DylibLoadCommandsAtom<A>* >::iterator pos;
- pos = fLibraryToLoadCommand.find(target.getFile());
- if ( pos != fLibraryToLoadCommand.end() ) {
- if ( pos->second->linkedWeak() )
- weakImport = true;
- }
- }
- std::map<const ObjectFile::Atom*,bool>::iterator pos = fWeakImportMap.find(&target);
- if ( pos == fWeakImportMap.end() ) {
- // target not in fWeakImportMap, so add
- fWeakImportMap[&target] = weakImport;
- }
- else {
- // target in fWeakImportMap, check for weakness mismatch
- if ( pos->second != weakImport ) {
- // found mismatch
- switch ( fOptions.weakReferenceMismatchTreatment() ) {
- case Options::kWeakReferenceMismatchError:
- throwf("mismatching weak references for symbol: %s", target.getName());
- case Options::kWeakReferenceMismatchWeak:
- pos->second = true;
- break;
- case Options::kWeakReferenceMismatchNonWeak:
- pos->second = false;
- break;
- }
- }
- }
- // update if we use a weak_import or a strong import from this dylib
- if ( fWeakImportMap[&target] )
- fDylibReadersWithWeakImports.insert(target.getFile());
- else
- fDylibReadersWithNonWeakImports.insert(target.getFile());
- }
- // create stubs as needed
- if ( this->stubableReference(atom, ref)
- && (ref->getTargetOffset() == 0)
- && this->relocationNeededInFinalLinkedImage(target) == kRelocExternal ) {
- ObjectFile::Atom* stub = NULL;
- std::map<const ObjectFile::Atom*,ObjectFile::Atom*>::iterator pos = fStubsMap.find(&target);
- if ( pos == fStubsMap.end() ) {
- bool forLazyDylib = false;
- switch ( target.getDefinitionKind() ) {
- case ObjectFile::Atom::kRegularDefinition:
- case ObjectFile::Atom::kWeakDefinition:
- case ObjectFile::Atom::kAbsoluteSymbol:
- case ObjectFile::Atom::kTentativeDefinition:
- break;
- case ObjectFile::Atom::kExternalDefinition:
- case ObjectFile::Atom::kExternalWeakDefinition:
- if ( target.getFile()->isLazyLoadedDylib() )
- forLazyDylib = true;
- break;
- }
- stub = new StubAtom<A>(*this, target, forLazyDylib);
- fStubsMap[&target] = stub;
- }
- else {
- stub = pos->second;
- }
- // alter reference to use stub instead
- ref->setTarget(*stub, 0);
- }
- else if ( fOptions.usingLazyDylibLinking() && target.getFile()->isLazyLoadedDylib() ) {
- throwf("illegal reference to %s in lazy loaded dylib from %s in %s",
- target.getDisplayName(), atom->getDisplayName(),
- atom->getFile()->getPath());
- }
- // create GOT slots (non-lazy pointers) as needed
- else if ( this->GOTReferenceKind(ref->getKind()) ) {
- //
- bool mustUseGOT = ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal );
- bool useGOT;
- if ( fBiggerThanTwoGigs ) {
- // in big images use GOT for all zero fill atoms
- // this is just a heuristic and may need to be re-examined
- useGOT = mustUseGOT || ref->getTarget().isZeroFill();
- }
- else {
- // < 2GB image so remove all GOT entries that we can
- useGOT = mustUseGOT;
- }
- // if this GOT usage cannot be optimized away then make a GOT enry
- if ( ! this->optimizableGOTReferenceKind(ref->getKind()) )
- useGOT = true;
- if ( useGOT ) {
- ObjectFile::Atom* nlp = NULL;
- std::map<ObjectFile::Atom*,ObjectFile::Atom*>::iterator pos = fGOTMap.find(&target);
- if ( pos == fGOTMap.end() ) {
- nlp = new NonLazyPointerAtom<A>(*this, target);
- fGOTMap[&target] = nlp;
- }
- else {
- nlp = pos->second;
- }
- // alter reference to use non lazy pointer instead
- ref->setTarget(*nlp, ref->getTargetOffset());
- }
- }
- }
- }
- }
-
- // sort stubs
- std::sort(fAllSynthesizedStubs.begin(), fAllSynthesizedStubs.end(), AtomByNameSorter());
-
- // add dummy fast stubs (x86 only)
- if ( !fOptions.slowx86Stubs() )
- this->insertDummyStubs();
-
- // sort lazy pointers
- std::sort(fAllSynthesizedLazyPointers.begin(), fAllSynthesizedLazyPointers.end(), AtomByNameSorter());
- std::sort(fAllSynthesizedLazyDylibPointers.begin(), fAllSynthesizedLazyDylibPointers.end(), AtomByNameSorter());
-
-
- // add stubs to fAllAtoms
- if ( fAllSynthesizedStubs.size() != 0 ) {
- std::vector<ObjectFile::Atom*> textStubs;
- std::vector<ObjectFile::Atom*> importStubs;
- for (typename std::vector<class StubAtom<A>*>::iterator sit=fAllSynthesizedStubs.begin(); sit != fAllSynthesizedStubs.end(); ++sit) {
- ObjectFile::Atom* stubAtom = *sit;
- if ( strcmp(stubAtom->getSegment().getName(), "__TEXT") == 0 )
- textStubs.push_back(stubAtom);
- else
- importStubs.push_back(stubAtom);
- }
- // any helper stubs go right after regular stubs
- if ( fAllSynthesizedStubHelpers.size() != 0 )
- textStubs.insert(textStubs.end(), fAllSynthesizedStubHelpers.begin(), fAllSynthesizedStubHelpers.end());
- // insert text stubs right after __text section
- ObjectFile::Section* curSection = NULL;
- ObjectFile::Atom* prevAtom = NULL;
- for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) {
- ObjectFile::Atom* atom = *it;
- ObjectFile::Section* nextSection = atom->getSection();
- if ( nextSection != curSection ) {
- if ( (prevAtom != NULL) && (strcmp(prevAtom->getSectionName(), "__text") == 0) ) {
- // found end of __text section, insert stubs here
- fAllAtoms->insert(it, textStubs.begin(), textStubs.end());
- break;
- }
- curSection = nextSection;
- }
- prevAtom = atom;
- }
- if ( importStubs.size() != 0 ) {
- // insert __IMPORTS stubs right before __LINKEDIT
- for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) {
- ObjectFile::Atom* atom = *it;
- ObjectFile::Section* nextSection = atom->getSection();
- if ( nextSection != curSection ) {
- // for i386 where stubs are not in __TEXT segment
- if ( ((prevAtom != NULL) && (strcmp(prevAtom->getSegment().getName(), "__IMPORT") == 0))
- || (strcmp(atom->getSegment().getName(), "__LINKEDIT") == 0) ) {
- // insert stubs at end of __IMPORT segment, or before __LINKEDIT
- fAllAtoms->insert(it, importStubs.begin(), importStubs.end());
- break;
- }
- curSection = nextSection;
- }
- prevAtom = atom;
- }
- }
- }
-
-
- // add lazy dylib pointers to fAllAtoms
- if ( fAllSynthesizedLazyDylibPointers.size() != 0 ) {
- ObjectFile::Section* curSection = NULL;
- ObjectFile::Atom* prevAtom = NULL;
- bool inserted = false;
- for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) {
- ObjectFile::Atom* atom = *it;
- ObjectFile::Section* nextSection = atom->getSection();
- if ( nextSection != curSection ) {
- if ( (prevAtom != NULL) && (strcmp(prevAtom->getSectionName(), "__dyld") == 0) ) {
- // found end of __dyld section, insert lazy pointers here
- fAllAtoms->insert(it, fAllSynthesizedLazyDylibPointers.begin(), fAllSynthesizedLazyDylibPointers.end());
- inserted = true;
- break;
- }
- curSection = nextSection;
- }
- prevAtom = atom;
- }
- if ( !inserted ) {
- throw "can't insert lazy pointers, __dyld section not found";
- }
- }
-
- // add lazy pointers to fAllAtoms
- if ( fAllSynthesizedLazyPointers.size() != 0 ) {
- ObjectFile::Section* curSection = NULL;
- ObjectFile::Atom* prevAtom = NULL;
- bool inserted = false;
- for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) {
- ObjectFile::Atom* atom = *it;
- ObjectFile::Section* nextSection = atom->getSection();
- if ( nextSection != curSection ) {
- if ( (prevAtom != NULL) && (strcmp(prevAtom->getSectionName(), "__dyld") == 0) ) {
- // found end of __dyld section, insert lazy pointers here
- fAllAtoms->insert(it, fAllSynthesizedLazyPointers.begin(), fAllSynthesizedLazyPointers.end());
- inserted = true;
- break;
- }
- curSection = nextSection;
- }
- prevAtom = atom;
- }
- if ( !inserted ) {
- throw "can't insert lazy pointers, __dyld section not found";
- }
- }
-
- // add non-lazy pointers to fAllAtoms
- if ( fAllSynthesizedNonLazyPointers.size() != 0 ) {
- ObjectFile::Section* curSection = NULL;
- ObjectFile::Atom* prevAtom = NULL;
- bool inserted = false;
- for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) {
- ObjectFile::Atom* atom = *it;
- ObjectFile::Section* nextSection = atom->getSection();
- if ( nextSection != curSection ) {
- if ( (prevAtom != NULL)
- && ((strcmp(prevAtom->getSectionName(), "__dyld") == 0)
- || ((strcmp(prevAtom->getSectionName(), "__data") == 0) &&
- ((fOptions.outputKind() == Options::kDyld) || (fOptions.outputKind() == Options::kStaticExecutable))) ) ) {
- // found end of __dyld section, insert lazy pointers here
- fAllAtoms->insert(it, fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end());
- inserted = true;
- break;
- }
- curSection = nextSection;
- }
- prevAtom = atom;
- }
- if ( !inserted ) {
- throw "can't insert non-lazy pointers, __dyld section not found";
- }
- }
-
- // build LC_SEGMENT_SPLIT_INFO content now that all atoms exist
- if ( fSplitCodeToDataContentAtom != 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->getTargetBinding()) {
- case ObjectFile::Reference::kUnboundByName:
- case ObjectFile::Reference::kDontBind:
- break;
- case ObjectFile::Reference::kBoundByName:
- case ObjectFile::Reference::kBoundDirectly:
- if ( this->segmentsCanSplitApart(*atom, ref->getTarget()) ) {
- this->addCrossSegmentRef(atom, ref);
- }
- break;
- }
- }
- }
- }
-
-}
-
-
-template <typename A>
-void Writer<A>::partitionIntoSections()
-{
- const bool oneSegmentCommand = (fOptions.outputKind() == Options::kObjectFile);
-
- // for every atom, set its sectionInfo object and section offset
- // build up fSegmentInfos along the way
- ObjectFile::Section* curSection = NULL;
- SectionInfo* currentSectionInfo = NULL;
- SegmentInfo* currentSegmentInfo = NULL;
- SectionInfo* cstringSectionInfo = NULL;
- unsigned int sectionIndex = 1;
- fSegmentInfos.reserve(8);
- for (unsigned int i=0; i < fAllAtoms->size(); ++i) {
- ObjectFile::Atom* atom = (*fAllAtoms)[i];
- if ( (atom->getSection() != curSection) || ((curSection==NULL) && (strcmp(atom->getSectionName(),currentSectionInfo->fSectionName) != 0)) ) {
- if ( oneSegmentCommand ) {
- if ( currentSegmentInfo == NULL ) {
- currentSegmentInfo = new SegmentInfo();
- currentSegmentInfo->fInitProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE;
- currentSegmentInfo->fMaxProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE;
- this->fSegmentInfos.push_back(currentSegmentInfo);
- }
- currentSectionInfo = new SectionInfo();
- strcpy(currentSectionInfo->fSectionName, atom->getSectionName());
- strcpy(currentSectionInfo->fSegmentName, atom->getSegment().getName());
- currentSectionInfo->fAlignment = atom->getAlignment().powerOf2;
- currentSectionInfo->fAllZeroFill = atom->isZeroFill();
- currentSectionInfo->fVirtualSection = (currentSectionInfo->fSectionName[0] == '.');
- if ( !currentSectionInfo->fVirtualSection || fEmitVirtualSections )
- currentSectionInfo->setIndex(sectionIndex++);
- currentSegmentInfo->fSections.push_back(currentSectionInfo);
- if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__cstring") == 0) )
- cstringSectionInfo = currentSectionInfo;
- }
- else {
- if ( (currentSegmentInfo == NULL) || (strcmp(currentSegmentInfo->fName, atom->getSegment().getName()) != 0) ) {
- currentSegmentInfo = new SegmentInfo();
- strcpy(currentSegmentInfo->fName, atom->getSegment().getName());
- uint32_t initprot = 0;
- if ( atom->getSegment().isContentReadable() )
- initprot |= VM_PROT_READ;
- if ( atom->getSegment().isContentWritable() )
- initprot |= VM_PROT_WRITE;
- if ( atom->getSegment().isContentExecutable() )
- initprot |= VM_PROT_EXECUTE;
- if ( fOptions.readOnlyx86Stubs() && (strcmp(atom->getSegment().getName(), "__IMPORT") == 0) )
- initprot &= ~VM_PROT_WRITE; // hack until i386 __pointers section is synthesized by linker
- currentSegmentInfo->fInitProtection = initprot;
- if ( initprot == 0 )
- currentSegmentInfo->fMaxProtection = 0; // pagezero should have maxprot==initprot==0
- else if ( fOptions.architecture() == CPU_TYPE_ARM )
- currentSegmentInfo->fMaxProtection = currentSegmentInfo->fInitProtection; // iPhoneOS wants max==init
- else
- currentSegmentInfo->fMaxProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE;
- std::vector<Options::SegmentProtect>& customSegProtections = fOptions.customSegmentProtections();
- for(std::vector<Options::SegmentProtect>::iterator it = customSegProtections.begin(); it != customSegProtections.end(); ++it) {
- if ( strcmp(it->name, currentSegmentInfo->fName) == 0 ) {
- currentSegmentInfo->fInitProtection = it->init;
- currentSegmentInfo->fMaxProtection = it->max;
- }
- }
- currentSegmentInfo->fBaseAddress = atom->getSegment().getBaseAddress();
- currentSegmentInfo->fFixedAddress = atom->getSegment().hasFixedAddress();
- if ( currentSegmentInfo->fFixedAddress && (&(atom->getSegment()) == &Segment::fgStackSegment) )
- currentSegmentInfo->fIndependentAddress = true;
- this->fSegmentInfos.push_back(currentSegmentInfo);
- }
- currentSectionInfo = new SectionInfo();
- currentSectionInfo->fAtoms.reserve(fAllAtoms->size()/4); // reduce reallocations by starting large
- strcpy(currentSectionInfo->fSectionName, atom->getSectionName());
- strcpy(currentSectionInfo->fSegmentName, atom->getSegment().getName());
- currentSectionInfo->fAlignment = atom->getAlignment().powerOf2;
- // check for -sectalign override
- std::vector<Options::SectionAlignment>& alignmentOverrides = fOptions.sectionAlignments();
- for(std::vector<Options::SectionAlignment>::iterator it=alignmentOverrides.begin(); it != alignmentOverrides.end(); ++it) {
- if ( (strcmp(it->segmentName, currentSectionInfo->fSegmentName) == 0) && (strcmp(it->sectionName, currentSectionInfo->fSectionName) == 0) )
- currentSectionInfo->fAlignment = it->alignment;
- }
- currentSectionInfo->fAllZeroFill = atom->isZeroFill();
- currentSectionInfo->fVirtualSection = ( currentSectionInfo->fSectionName[0] == '.');
- if ( !currentSectionInfo->fVirtualSection || fEmitVirtualSections )
- currentSectionInfo->setIndex(sectionIndex++);
- currentSegmentInfo->fSections.push_back(currentSectionInfo);
- }
- if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "._load_commands") == 0) ) {
- fLoadCommandsSection = currentSectionInfo;
- fLoadCommandsSegment = currentSegmentInfo;
- }
- if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__la_symbol_ptr") == 0) )
- currentSectionInfo->fAllLazyPointers = true;
- if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__la_sym_ptr2") == 0) )
- currentSectionInfo->fAllLazyPointers = true;
- if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__ld_symbol_ptr") == 0) )
- currentSectionInfo->fAllLazyDylibPointers = true;
- if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__nl_symbol_ptr") == 0) )
- currentSectionInfo->fAllNonLazyPointers = true;
- if ( (strcmp(currentSectionInfo->fSegmentName, "__IMPORT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__pointers") == 0) )
- currentSectionInfo->fAllNonLazyPointers = true;
- if ( (fOptions.outputKind() == Options::kDyld) && (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__pointers") == 0) )
- currentSectionInfo->fAllNonLazyPointers = true;
- if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__picsymbolstub1") == 0) )
- currentSectionInfo->fAllStubs = true;
- if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__symbol_stub1") == 0) )
- currentSectionInfo->fAllStubs = true;
- if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__picsymbolstub2") == 0) )
- currentSectionInfo->fAllStubs = true;
- if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__symbol_stub") == 0) )
- currentSectionInfo->fAllStubs = true;
- if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__picsymbolstub4") == 0) )
- currentSectionInfo->fAllStubs = true;
- if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__symbol_stub4") == 0) )
- currentSectionInfo->fAllStubs = true;
- if ( (strcmp(currentSectionInfo->fSegmentName, "__IMPORT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__jump_table") == 0) ) {
- currentSectionInfo->fAllSelfModifyingStubs = true;
- currentSectionInfo->fAlignment = 6; // force x86 fast stubs to start on 64-byte boundary
- }
- if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__eh_frame") == 0) )
- currentSectionInfo->fAlignment = __builtin_ctz(sizeof(pint_t)); // always start CFI info pointer aligned
- curSection = atom->getSection();
- if ( currentSectionInfo->fAllNonLazyPointers || currentSectionInfo->fAllLazyPointers || currentSectionInfo->fAllLazyDylibPointers
- || currentSectionInfo->fAllStubs || currentSectionInfo->fAllSelfModifyingStubs ) {
- fSymbolTableCommands->needDynamicTable();
- }
- }
- // any non-zero fill atoms make whole section marked not-zero-fill
- if ( currentSectionInfo->fAllZeroFill && ! atom->isZeroFill() )
- currentSectionInfo->fAllZeroFill = false;
- // change section object to be Writer's SectionInfo object
- atom->setSection(currentSectionInfo);
- // section alignment is that of a contained atom with the greatest alignment
- uint8_t atomAlign = atom->getAlignment().powerOf2;
- if ( currentSectionInfo->fAlignment < atomAlign )
- currentSectionInfo->fAlignment = atomAlign;
- // calculate section offset for this atom
- uint64_t offset = currentSectionInfo->fSize;
- uint64_t alignment = 1 << atomAlign;
- uint64_t currentModulus = (offset % alignment);
- uint64_t requiredModulus = atom->getAlignment().modulus;
- if ( currentModulus != requiredModulus ) {
- if ( requiredModulus > currentModulus )
- offset += requiredModulus-currentModulus;
- else
- offset += requiredModulus+alignment-currentModulus;
- }
- atom->setSectionOffset(offset);
- uint64_t curAtomSize = atom->getSize();
- currentSectionInfo->fSize = offset + curAtomSize;
- // add atom to section vector
- currentSectionInfo->fAtoms.push_back(atom);
- // update largest size
- if ( !currentSectionInfo->fAllZeroFill && (curAtomSize > fLargestAtomSize) )
- fLargestAtomSize = curAtomSize;
- }
- if ( (cstringSectionInfo != NULL) && (cstringSectionInfo->fAlignment > 0) ) {
- // when merging cstring sections in .o files, all strings need to use the max alignment
- uint64_t offset = 0;
- uint64_t cstringAlignment = 1 << cstringSectionInfo->fAlignment;
- for (std::vector<ObjectFile::Atom*>::iterator it=cstringSectionInfo->fAtoms.begin(); it != cstringSectionInfo->fAtoms.end(); it++) {
- offset = (offset + (cstringAlignment-1)) & (-cstringAlignment);
- ObjectFile::Atom* atom = *it;
- atom->setSectionOffset(offset);
- offset += atom->getSize();
- }
- cstringSectionInfo->fSize = offset;
- }
-}
-
-
-struct TargetAndOffset { ObjectFile::Atom* atom; uint32_t offset; };
-class TargetAndOffsetComparor
-{
-public:
- bool operator()(const TargetAndOffset& left, const TargetAndOffset& right) const
- {
- if ( left.atom != right.atom )
- return ( left.atom < right.atom );
- return ( left.offset < right.offset );
- }
-};
-
-template <>
-bool Writer<ppc>::addBranchIslands()
-{
- return this->addPPCBranchIslands();
-}
-
-template <>
-bool Writer<ppc64>::addBranchIslands()
-{
- return this->addPPCBranchIslands();
-}
-
-template <>
-bool Writer<x86>::addBranchIslands()
-{
- // x86 branches can reach entire 4G address space, so no need for branch islands
- return false;
-}
-
-template <>
-bool Writer<x86_64>::addBranchIslands()
-{
- // x86 branches can reach entire 4G size of largest image
- return false;
-}
-
-template <>
-bool Writer<arm>::addBranchIslands()
-{
- // arm branch islands not (yet) supported
- // you can instead compile with -mlong-call
- return false;
-}
-
-template <>
-bool Writer<ppc>::isBranch24Reference(uint8_t kind)
-{
- switch (kind) {
- case ppc::kBranch24:
- case ppc::kBranch24WeakImport:
- return true;
- }
- return false;
-}
-
-template <>
-bool Writer<ppc64>::isBranch24Reference(uint8_t kind)
-{
- switch (kind) {
- case ppc64::kBranch24:
- case ppc64::kBranch24WeakImport:
- return true;
- }
- return false;
-}
-
-//
-// PowerPC can do PC relative branches as far as +/-16MB.
-// If a branch target is >16MB then we insert one or more
-// "branch islands" between the branch and its target that
-// allows island hoping to the target.
-//
-// Branch Island Algorithm
-//
-// If the __TEXT segment < 16MB, then no branch islands needed
-// Otherwise, every 15MB into the __TEXT segment is region is
-// added which can contain branch islands. Every out of range
-// bl instruction is checked. If it crosses a region, an island
-// is added to that region with the same target and the bl is
-// adjusted to target the island instead.
-//
-// In theory, if too many islands are added to one region, it
-// could grow the __TEXT enough that other previously in-range
-// bl branches could be pushed out of range. We reduce the
-// probability this could happen by placing the ranges every
-// 15MB which means the region would have to be 1MB (256K islands)
-// before any branches could be pushed out of range.
-//
-template <typename A>
-bool Writer<A>::addPPCBranchIslands()
-{
- bool log = false;
- bool result = false;
- // Can only possibly need branch islands if __TEXT segment > 16M
- if ( fLoadCommandsSegment->fSize > 16000000 ) {
- if ( log) fprintf(stderr, "ld: checking for branch islands, __TEXT segment size=%llu\n", fLoadCommandsSegment->fSize);
- const uint32_t kBetweenRegions = 15*1024*1024; // place regions of islands every 15MB in __text section
- SectionInfo* textSection = NULL;
- for (std::vector<SectionInfo*>::iterator it=fLoadCommandsSegment->fSections.begin(); it != fLoadCommandsSegment->fSections.end(); it++) {
- if ( strcmp((*it)->fSectionName, "__text") == 0 ) {
- textSection = *it;
- if ( log) fprintf(stderr, "ld: checking for branch islands, __text section size=%llu\n", textSection->fSize);
- break;
- }
- }
- const int kIslandRegionsCount = fLoadCommandsSegment->fSize / kBetweenRegions;
- typedef std::map<TargetAndOffset,ObjectFile::Atom*, TargetAndOffsetComparor> AtomToIsland;
- AtomToIsland regionsMap[kIslandRegionsCount];
- std::vector<ObjectFile::Atom*> regionsIslands[kIslandRegionsCount];
- unsigned int islandCount = 0;
- if ( log) fprintf(stderr, "ld: will use %u branch island regions\n", kIslandRegionsCount);
-
- // create islands for branch references that are out of range
- 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;
- if ( this->isBranch24Reference(ref->getKind()) ) {
- ObjectFile::Atom& target = ref->getTarget();
- int64_t srcAddr = atom->getAddress() + ref->getFixUpOffset();
- int64_t dstAddr = target.getAddress() + ref->getTargetOffset();
- int64_t displacement = dstAddr - srcAddr;
- TargetAndOffset finalTargetAndOffset = { &target, ref->getTargetOffset() };
- const int64_t kFifteenMegLimit = kBetweenRegions;
- if ( displacement > kFifteenMegLimit ) {
- // create forward branch chain
- ObjectFile::Atom* nextTarget = ⌖
- uint64_t nextTargetOffset = ref->getTargetOffset();
- for (int i=kIslandRegionsCount-1; i >=0 ; --i) {
- AtomToIsland* region = ®ionsMap[i];
- int64_t islandRegionAddr = kBetweenRegions * (i+1) + textSection->getBaseAddress();
- if ( (srcAddr < islandRegionAddr) && (islandRegionAddr <= dstAddr) ) {
- AtomToIsland::iterator pos = region->find(finalTargetAndOffset);
- if ( pos == region->end() ) {
- BranchIslandAtom<A>* island = new BranchIslandAtom<A>(*this, target.getDisplayName(), i, *nextTarget, nextTargetOffset);
- island->setSection(textSection);
- (*region)[finalTargetAndOffset] = island;
- if (log) fprintf(stderr, "added island %s to region %d for %s\n", island->getDisplayName(), i, atom->getDisplayName());
- regionsIslands[i].push_back(island);
- ++islandCount;
- nextTarget = island;
- nextTargetOffset = 0;
- }
- else {
- nextTarget = pos->second;
- nextTargetOffset = 0;
- }
- }
- }
- if (log) fprintf(stderr, "using island %s for branch to %s from %s\n", nextTarget->getDisplayName(), target.getDisplayName(), atom->getDisplayName());
- ref->setTarget(*nextTarget, nextTargetOffset);
- }
- else if ( displacement < (-kFifteenMegLimit) ) {
- // create back branching chain
- ObjectFile::Atom* prevTarget = ⌖
- uint64_t prevTargetOffset = ref->getTargetOffset();
- for (int i=0; i < kIslandRegionsCount ; ++i) {
- AtomToIsland* region = ®ionsMap[i];
- int64_t islandRegionAddr = kBetweenRegions * (i+1);
- if ( (dstAddr <= islandRegionAddr) && (islandRegionAddr < srcAddr) ) {
- AtomToIsland::iterator pos = region->find(finalTargetAndOffset);
- if ( pos == region->end() ) {
- BranchIslandAtom<A>* island = new BranchIslandAtom<A>(*this, target.getDisplayName(), i, *prevTarget, prevTargetOffset);
- island->setSection(textSection);
- (*region)[finalTargetAndOffset] = island;
- if (log) fprintf(stderr, "added back island %s to region %d for %s\n", island->getDisplayName(), i, atom->getDisplayName());
- regionsIslands[i].push_back(island);
- ++islandCount;
- prevTarget = island;
- prevTargetOffset = 0;
- }
- else {
- prevTarget = pos->second;
- prevTargetOffset = 0;
- }
- }
- }
- if (log) fprintf(stderr, "using back island %s for %s\n", prevTarget->getDisplayName(), atom->getDisplayName());
- ref->setTarget(*prevTarget, prevTargetOffset);
- }
- }
- }
- }
-
- // insert islands into __text section and adjust section offsets
- if ( islandCount > 0 ) {
- if ( log ) fprintf(stderr, "ld: %u branch islands required in %u regions\n", islandCount, kIslandRegionsCount);
- std::vector<ObjectFile::Atom*> newAtomList;
- newAtomList.reserve(textSection->fAtoms.size()+islandCount);
- uint64_t islandRegionAddr = kBetweenRegions + textSection->getBaseAddress();
- uint64_t textSectionAlignment = (1 << textSection->fAlignment);
- int regionIndex = 0;
- uint64_t atomSlide = 0;
- uint64_t sectionOffset = 0;
- for (std::vector<ObjectFile::Atom*>::iterator it=textSection->fAtoms.begin(); it != textSection->fAtoms.end(); it++) {
- ObjectFile::Atom* atom = *it;
- if ( atom->getAddress() > islandRegionAddr ) {
- uint64_t islandStartOffset = atom->getSectionOffset() + atomSlide;
- sectionOffset = islandStartOffset;
- std::vector<ObjectFile::Atom*>* regionIslands = ®ionsIslands[regionIndex];
- for (std::vector<ObjectFile::Atom*>::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) {
- ObjectFile::Atom* islandAtom = *rit;
- newAtomList.push_back(islandAtom);
- uint64_t alignment = 1 << (islandAtom->getAlignment().powerOf2);
- sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) );
- islandAtom->setSectionOffset(sectionOffset);
- sectionOffset += islandAtom->getSize();
- }
- ++regionIndex;
- islandRegionAddr += kBetweenRegions;
- uint64_t islandRegionAlignmentBlocks = (sectionOffset - islandStartOffset + textSectionAlignment - 1) / textSectionAlignment;
- atomSlide += (islandRegionAlignmentBlocks * textSectionAlignment);
- }
- newAtomList.push_back(atom);
- if ( atomSlide != 0 )
- atom->setSectionOffset(atom->getSectionOffset()+atomSlide);
- }
- sectionOffset = textSection->fSize+atomSlide;
- // put any remaining islands at end of __text section
- if ( regionIndex < kIslandRegionsCount ) {
- std::vector<ObjectFile::Atom*>* regionIslands = ®ionsIslands[regionIndex];
- for (std::vector<ObjectFile::Atom*>::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) {
- ObjectFile::Atom* islandAtom = *rit;
- newAtomList.push_back(islandAtom);
- uint64_t alignment = 1 << (islandAtom->getAlignment().powerOf2);
- sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) );
- islandAtom->setSectionOffset(sectionOffset);
- sectionOffset += islandAtom->getSize();
- }
- }
-
- textSection->fAtoms = newAtomList;
- textSection->fSize = sectionOffset;
- result = true;
- }
-
- }
- return result;
-}
-
-
-template <typename A>
-void Writer<A>::adjustLoadCommandsAndPadding()
-{
- fSegmentCommands->computeSize();
-
- // recompute load command section offsets
- uint64_t offset = 0;
- std::vector<class ObjectFile::Atom*>& loadCommandAtoms = fLoadCommandsSection->fAtoms;
- const unsigned int atomCount = loadCommandAtoms.size();
- for (unsigned int i=0; i < atomCount; ++i) {
- ObjectFile::Atom* atom = loadCommandAtoms[i];
- uint64_t alignment = 1 << atom->getAlignment().powerOf2;
- offset = ( (offset+alignment-1) & (-alignment) );
- atom->setSectionOffset(offset);
- uint32_t atomSize = atom->getSize();
- if ( atomSize > fLargestAtomSize )
- fLargestAtomSize = atomSize;
- offset += atomSize;
- fLoadCommandsSection->fSize = offset;
- }
-
- std::vector<SectionInfo*>& sectionInfos = fLoadCommandsSegment->fSections;
- const int sectionCount = sectionInfos.size();
- uint32_t totalSizeOfHeaderAndLoadCommands = 0;
- for(int j=0; j < sectionCount; ++j) {
- SectionInfo* curSection = sectionInfos[j];
- totalSizeOfHeaderAndLoadCommands += curSection->fSize;
- if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 )
- break;
- }
- uint64_t paddingSize = 0;
- if ( fOptions.outputKind() == Options::kDyld ) {
- // dyld itself has special padding requirements. We want the beginning __text section to start at a stable address
- paddingSize = 4096 - (totalSizeOfHeaderAndLoadCommands % 4096);
- }
- else if ( fOptions.outputKind() == Options::kObjectFile ) {
- // mach-o .o files need no padding between load commands and first section
- paddingSize = 0;
- }
- else if ( fOptions.makeEncryptable() ) {
- // want load commands to end on a page boundary, so __text starts on page boundary
- paddingSize = 4096 - ((totalSizeOfHeaderAndLoadCommands+fOptions.minimumHeaderPad()) % 4096) + fOptions.minimumHeaderPad();
- fEncryptionLoadCommand->setStartEncryptionOffset(totalSizeOfHeaderAndLoadCommands+paddingSize);
- }
- else {
- // work backwards from end of segment and lay out sections so that extra room goes to padding atom
- uint64_t addr = 0;
- for(int j=sectionCount-1; j >=0; --j) {
- SectionInfo* curSection = sectionInfos[j];
- addr -= curSection->fSize;
- addr = addr & (0 - (1 << curSection->fAlignment));
- if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 ) {
- addr -= totalSizeOfHeaderAndLoadCommands;
- paddingSize = addr % 4096;
- break;
- }
- }
-
- // if command line requires more padding than this
- uint32_t minPad = fOptions.minimumHeaderPad();
- if ( fOptions.maxMminimumHeaderPad() ) {
- // -headerpad_max_install_names means there should be room for every path load command to grow to 1204 bytes
- uint32_t altMin = fLibraryToOrdinal.size() * MAXPATHLEN;
- if ( fOptions.outputKind() == Options::kDynamicLibrary )
- altMin += MAXPATHLEN;
- if ( altMin > minPad )
- minPad = altMin;
- }
- if ( paddingSize < minPad ) {
- int extraPages = (minPad - paddingSize + 4095)/4096;
- paddingSize += extraPages * 4096;
- }
- }
-
- // adjust atom size and update section size
- fHeaderPadding->setSize(paddingSize);
- for(int j=0; j < sectionCount; ++j) {
- SectionInfo* curSection = sectionInfos[j];
- if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 )
- curSection->fSize = paddingSize;
- }
-}
-
-// assign file offsets and logical address to all segments
-template <typename A>
-void Writer<A>::assignFileOffsets()
-{
- bool finalLinkedImage = (fOptions.outputKind() != Options::kObjectFile);
- bool haveFixedSegments = false;
- uint64_t fileOffset = 0;
- uint64_t nextContiguousAddress = fOptions.baseAddress();
- uint64_t nextReadOnlyAddress = fOptions.baseAddress();
- uint64_t nextWritableAddress = fOptions.baseWritableAddress();
-
- // process segments with fixed addresses (-segaddr)
- for (std::vector<Options::SegmentStart>::iterator it = fOptions.customSegmentAddresses().begin(); it != fOptions.customSegmentAddresses().end(); ++it) {
- for (std::vector<SegmentInfo*>::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) {
- SegmentInfo* curSegment = *segit;
- if ( strcmp(curSegment->fName, it->name) == 0 ) {
- curSegment->fBaseAddress = it->address;
- curSegment->fFixedAddress = true;
- break;
- }
- }
- }
-
- // Run through the segments and each segment's sections to assign addresses
- for (std::vector<SegmentInfo*>::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) {
- SegmentInfo* curSegment = *segit;
-
- if ( fOptions.splitSeg() ) {
- if ( curSegment->fInitProtection & VM_PROT_WRITE )
- nextContiguousAddress = nextWritableAddress;
- else
- nextContiguousAddress = nextReadOnlyAddress;
- }
-
- fileOffset = (fileOffset+4095) & (-4096);
- curSegment->fFileOffset = fileOffset;
-
- // Set the segment base address
- if ( curSegment->fFixedAddress )
- haveFixedSegments = true;
- else
- curSegment->fBaseAddress = nextContiguousAddress;
-
- // We've set the segment address, now run through each section.
- uint64_t address = curSegment->fBaseAddress;
- SectionInfo* firstZeroFillSection = NULL;
- SectionInfo* prevSection = NULL;
-
- std::vector<SectionInfo*>& sectionInfos = curSegment->fSections;
-
- for (std::vector<SectionInfo*>::iterator it = sectionInfos.begin(); it != sectionInfos.end(); ++it) {
- SectionInfo* curSection = *it;
-
- // adjust section address based on alignment
- uint64_t alignment = 1 << curSection->fAlignment;
- address = ( (address+alignment-1) & (-alignment) );
-
- // adjust file offset to match address
- if ( prevSection != NULL ) {
- if ( finalLinkedImage || !prevSection->fVirtualSection )
- fileOffset = (address - prevSection->getBaseAddress()) + prevSection->fFileOffset;
- else
- fileOffset = ( (fileOffset+alignment-1) & (-alignment) );
- }
-
- // update section info
- curSection->fFileOffset = fileOffset;
- curSection->setBaseAddress(address);
- //fprintf(stderr, "%s %s %llX\n", curSegment->fName, curSection->fSectionName, address);
-
- // keep track of trailing zero fill sections
- if ( curSection->fAllZeroFill && (firstZeroFillSection == NULL) )
- firstZeroFillSection = curSection;
- if ( !curSection->fAllZeroFill && (firstZeroFillSection != NULL) && finalLinkedImage )
- throwf("zero-fill section %s not at end of segment", curSection->fSectionName);
-
- // update running pointers
- if ( finalLinkedImage || !curSection->fVirtualSection )
- address += curSection->fSize;
- fileOffset += curSection->fSize;
-
- // sanity check size of 32-bit binaries
- if ( address > maxAddress() )
- throwf("section %s exceeds 4GB limit", curSection->fSectionName);
-
- // update segment info
- curSegment->fFileSize = fileOffset - curSegment->fFileOffset;
- curSegment->fSize = curSegment->fFileSize;
- prevSection = curSection;
- }
-
- if ( fOptions.outputKind() == Options::kObjectFile ) {
- // don't page align .o files
- }
- else {
- // optimize trailing zero-fill sections to not occupy disk space
- if ( firstZeroFillSection != NULL ) {
- curSegment->fFileSize = firstZeroFillSection->fFileOffset - curSegment->fFileOffset;
- fileOffset = firstZeroFillSection->fFileOffset;
- }
- // page align segment size
- curSegment->fFileSize = (curSegment->fFileSize+4095) & (-4096);
- curSegment->fSize = (curSegment->fSize+4095) & (-4096);
- if ( !curSegment->fIndependentAddress && (curSegment->fBaseAddress >= nextContiguousAddress) ) {
- nextContiguousAddress = (curSegment->fBaseAddress+curSegment->fSize+4095) & (-4096);
- if ( curSegment->fInitProtection & VM_PROT_WRITE )
- nextWritableAddress = nextContiguousAddress;
- else
- nextReadOnlyAddress = nextContiguousAddress;
- }
- }
- }
-
- // check for segment overlaps caused by user specified fixed segments (e.g. __PAGEZERO, __UNIXSTACK)
- if ( haveFixedSegments ) {
- int segCount = fSegmentInfos.size();
- for(int i=0; i < segCount; ++i) {
- SegmentInfo* segment1 = fSegmentInfos[i];
-
- for(int j=0; j < segCount; ++j) {
- if ( i != j ) {
- SegmentInfo* segment2 = fSegmentInfos[j];
-
- if ( segment1->fBaseAddress < segment2->fBaseAddress ) {
- if ( (segment1->fBaseAddress+segment1->fSize) > segment2->fBaseAddress )
- throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)",
- segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize);
- }
- else if ( segment1->fBaseAddress > segment2->fBaseAddress ) {
- if ( (segment2->fBaseAddress+segment2->fSize) > segment1->fBaseAddress )
- throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)",
- segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize);
- }
- else 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;
- }
- }
-
- // record size of encrypted part of __TEXT segment
- if ( fOptions.makeEncryptable() ) {
- for (std::vector<SegmentInfo*>::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) {
- SegmentInfo* curSegment = *segit;
- if ( strcmp(curSegment->fName, "__TEXT") == 0 ) {
- fEncryptionLoadCommand->setEndEncryptionOffset(curSegment->fFileSize);
- break;
- }
- }
- }
-
-}
-
-template <typename A>
-void Writer<A>::adjustLinkEditSections()
-{
- // link edit content is always in last segment
- SegmentInfo* lastSeg = fSegmentInfos[fSegmentInfos.size()-1];
- unsigned int firstLinkEditSectionIndex = 0;
- while ( strcmp(lastSeg->fSections[firstLinkEditSectionIndex]->fSegmentName, "__LINKEDIT") != 0 )
- ++firstLinkEditSectionIndex;
-
- const unsigned int linkEditSectionCount = 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 < linkEditSectionCount; ++i) {
- std::vector<class ObjectFile::Atom*>& atoms = lastSeg->fSections[i]->fAtoms;
- // adjust section address based on alignment
- uint64_t sectionAlignment = 1 << lastSeg->fSections[i]->fAlignment;
- uint64_t pad = ((address+sectionAlignment-1) & (-sectionAlignment)) - address;
- address += pad;
- fileOffset += pad; // adjust file offset to match address
- lastSeg->fSections[i]->setBaseAddress(address);
- if ( strcmp(lastSeg->fSections[i]->fSectionName, "._absolute") == 0 )
- lastSeg->fSections[i]->setBaseAddress(0);
- lastSeg->fSections[i]->fFileOffset = fileOffset;
- uint64_t sectionOffset = 0;
- for (unsigned int j=0; j < atoms.size(); ++j) {
- ObjectFile::Atom* atom = atoms[j];
- uint64_t alignment = 1 << atom->getAlignment().powerOf2;
- sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) );
- atom->setSectionOffset(sectionOffset);
- uint64_t size = atom->getSize();
- sectionOffset += 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;
- }
- if ( fOptions.outputKind() == Options::kObjectFile ) {
- //lastSeg->fBaseAddress = 0;
- //lastSeg->fSize = lastSeg->fSections[firstLinkEditSectionIndex]->
- //lastSeg->fFileOffset = 0;
- //lastSeg->fFileSize =
- }
- else {
- lastSeg->fFileSize = fileOffset - lastSeg->fFileOffset;
- lastSeg->fSize = (address - lastSeg->fBaseAddress+4095) & (-4096);
- }
-}
-
-
-template <typename A>
-ObjectFile::Atom::Scope MachHeaderAtom<A>::getScope() const
-{
- switch ( fWriter.fOptions.outputKind() ) {
- case Options::kDynamicExecutable:
- case Options::kStaticExecutable:
- return ObjectFile::Atom::scopeGlobal;
- case Options::kDynamicLibrary:
- case Options::kDynamicBundle:
- case Options::kDyld:
- case Options::kObjectFile:
- return ObjectFile::Atom::scopeLinkageUnit;
- }
- throw "unknown header type";
-}
-
-template <typename A>
-ObjectFile::Atom::SymbolTableInclusion MachHeaderAtom<A>::getSymbolTableInclusion() const
-{
- switch ( fWriter.fOptions.outputKind() ) {
- case Options::kDynamicExecutable:
- return ObjectFile::Atom::kSymbolTableInAndNeverStrip;
- case Options::kStaticExecutable:
- return ObjectFile::Atom::kSymbolTableInAsAbsolute;
- case Options::kDynamicLibrary:
- case Options::kDynamicBundle:
- case Options::kDyld:
- return ObjectFile::Atom::kSymbolTableIn;
- case Options::kObjectFile:
- return ObjectFile::Atom::kSymbolTableNotIn;
- }
- throw "unknown header type";
-}
-
-template <typename A>
-const char* MachHeaderAtom<A>::getName() const
-{
- switch ( fWriter.fOptions.outputKind() ) {
- case Options::kDynamicExecutable:
- case Options::kStaticExecutable:
- return "__mh_execute_header";
- case Options::kDynamicLibrary:
- return "__mh_dylib_header";
- case Options::kDynamicBundle:
- return "__mh_bundle_header";
- case Options::kObjectFile:
- return NULL;
- case Options::kDyld:
- return "__mh_dylinker_header";
- }
- throw "unknown header type";
-}
-
-template <typename A>
-const char* MachHeaderAtom<A>::getDisplayName() const
-{
- switch ( fWriter.fOptions.outputKind() ) {
- case Options::kDynamicExecutable:
- case Options::kStaticExecutable:
- case Options::kDynamicLibrary:
- case Options::kDynamicBundle:
- case Options::kDyld:
- return this->getName();
- case Options::kObjectFile:
- return "mach header";
- }
- throw "unknown header type";
-}
-
-template <typename A>
-void MachHeaderAtom<A>::copyRawContent(uint8_t buffer[]) const
-{
- // get file type
- uint32_t fileType = 0;
- switch ( fWriter.fOptions.outputKind() ) {
- case Options::kDynamicExecutable:
- case Options::kStaticExecutable:
- fileType = MH_EXECUTE;
- break;
- case Options::kDynamicLibrary:
- fileType = MH_DYLIB;
- break;
- case Options::kDynamicBundle:
- fileType = MH_BUNDLE;
- break;
- case Options::kObjectFile:
- fileType = MH_OBJECT;
- break;
- case Options::kDyld:
- fileType = MH_DYLINKER;
- break;
- }
-
- // get flags
- uint32_t flags = 0;
- if ( fWriter.fOptions.outputKind() == Options::kObjectFile ) {
- if ( fWriter.fCanScatter )
- flags = MH_SUBSECTIONS_VIA_SYMBOLS;
- }
- else {
- if ( fWriter.fOptions.outputKind() == Options::kStaticExecutable ) {
- flags |= MH_NOUNDEFS;
- }
- else {
- flags = MH_DYLDLINK;
- if ( fWriter.fOptions.bindAtLoad() )
- flags |= MH_BINDATLOAD;
- switch ( fWriter.fOptions.nameSpace() ) {
- case Options::kTwoLevelNameSpace:
- flags |= MH_TWOLEVEL | MH_NOUNDEFS;
- break;
- case Options::kFlatNameSpace:
- break;
- case Options::kForceFlatNameSpace:
- flags |= MH_FORCE_FLAT;
- break;
- }
- if ( fWriter.fHasWeakExports )
- flags |= MH_WEAK_DEFINES;
- if ( fWriter.fReferencesWeakImports || fWriter.fHasWeakExports )
- flags |= MH_BINDS_TO_WEAK;
- if ( fWriter.fOptions.prebind() )
- flags |= MH_PREBOUND;
- if ( fWriter.fOptions.splitSeg() )
- flags |= MH_SPLIT_SEGS;
- if ( (fWriter.fOptions.outputKind() == Options::kDynamicLibrary) && fWriter.fNoReExportedDylibs )
- flags |= MH_NO_REEXPORTED_DYLIBS;
- if ( fWriter.fOptions.positionIndependentExecutable() )
- flags |= MH_PIE;
- }
- if ( fWriter.fOptions.hasExecutableStack() )
- flags |= MH_ALLOW_STACK_EXECUTION;
- if ( fWriter.fOptions.readerOptions().fRootSafe )
- flags |= MH_ROOT_SAFE;
- if ( fWriter.fOptions.readerOptions().fSetuidSafe )
- flags |= MH_SETUID_SAFE;
- }
-
- // get commands info
- uint32_t commandsSize = 0;
- uint32_t commandsCount = 0;
-
- std::vector<class ObjectFile::Atom*>& loadCommandAtoms = fWriter.fLoadCommandsSection->fAtoms;
- for (std::vector<ObjectFile::Atom*>::iterator it=loadCommandAtoms.begin(); it != loadCommandAtoms.end(); it++) {
- ObjectFile::Atom* atom = *it;
- commandsSize += atom->getSize();
- // segment and symbol table atoms can contain more than one load command
- if ( atom == fWriter.fSegmentCommands )
- commandsCount += fWriter.fSegmentCommands->commandCount();
- else if ( atom == fWriter.fSymbolTableCommands )
- commandsCount += fWriter.fSymbolTableCommands->commandCount();
- else if ( atom->getSize() != 0 )
- ++commandsCount;
- }
-
- // fill out mach_header
- macho_header<typename A::P>* mh = (macho_header<typename A::P>*)buffer;
- setHeaderInfo(*mh);
- mh->set_filetype(fileType);
- mh->set_ncmds(commandsCount);
- mh->set_sizeofcmds(commandsSize);
- mh->set_flags(flags);
-}
-
-template <>
-void MachHeaderAtom<ppc>::setHeaderInfo(macho_header<ppc::P>& header) const
-{
- header.set_magic(MH_MAGIC);
- header.set_cputype(CPU_TYPE_POWERPC);
- header.set_cpusubtype(fWriter.fCpuConstraint);
-}
-
-template <>
-void MachHeaderAtom<ppc64>::setHeaderInfo(macho_header<ppc64::P>& header) const
-{
- header.set_magic(MH_MAGIC_64);
- header.set_cputype(CPU_TYPE_POWERPC64);
- if ( (fWriter.fOptions.outputKind() == Options::kDynamicExecutable) && (fWriter.fOptions.macosxVersionMin() >= ObjectFile::ReaderOptions::k10_5) )
- header.set_cpusubtype(CPU_SUBTYPE_POWERPC_ALL | 0x80000000);
- else
- header.set_cpusubtype(CPU_SUBTYPE_POWERPC_ALL);
- header.set_reserved(0);
-}
-
-template <>
-void MachHeaderAtom<x86>::setHeaderInfo(macho_header<x86::P>& header) const
-{
- header.set_magic(MH_MAGIC);
- header.set_cputype(CPU_TYPE_I386);
- 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);
- if ( (fWriter.fOptions.outputKind() == Options::kDynamicExecutable) && (fWriter.fOptions.macosxVersionMin() >= ObjectFile::ReaderOptions::k10_5) )
- header.set_cpusubtype(CPU_SUBTYPE_X86_64_ALL | 0x80000000);
- else
- header.set_cpusubtype(CPU_SUBTYPE_X86_64_ALL);
- header.set_reserved(0);
-}
-
-template <>
-void MachHeaderAtom<arm>::setHeaderInfo(macho_header<arm::P>& header) const
-{
- header.set_magic(MH_MAGIC);
- header.set_cputype(CPU_TYPE_ARM);
- header.set_cpusubtype(fWriter.fCpuConstraint);
-}
-
-template <typename A>
-CustomStackAtom<A>::CustomStackAtom(Writer<A>& writer)
- : WriterAtom<A>(writer, Segment::fgStackSegment)
-{
- if ( stackGrowsDown() )
- Segment::fgStackSegment.setBaseAddress(writer.fOptions.customStackAddr() - writer.fOptions.customStackSize());
- else
- Segment::fgStackSegment.setBaseAddress(writer.fOptions.customStackAddr());
-}
-
-
-template <> bool CustomStackAtom<ppc>::stackGrowsDown() { return true; }
-template <> bool CustomStackAtom<ppc64>::stackGrowsDown() { return true; }
-template <> bool CustomStackAtom<x86>::stackGrowsDown() { return true; }
-template <> bool CustomStackAtom<x86_64>::stackGrowsDown() { return true; }
-template <> bool CustomStackAtom<arm>::stackGrowsDown() { return true; }
-
-template <typename A>
-void SegmentLoadCommandsAtom<A>::computeSize()
-{
- uint64_t size = 0;
- std::vector<SegmentInfo*>& segmentInfos = fWriter.fSegmentInfos;
- const int segCount = segmentInfos.size();
- for(int i=0; i < segCount; ++i) {
- size += sizeof(macho_segment_command<P>);
- std::vector<SectionInfo*>& sectionInfos = segmentInfos[i]->fSections;
- const int sectionCount = sectionInfos.size();
- for(int j=0; j < sectionCount; ++j) {
- if ( fWriter.fEmitVirtualSections || ! sectionInfos[j]->fVirtualSection )
- size += sizeof(macho_section<P>);
- }
- }
- fSize = size;
- fCommandCount = segCount;
- if ( fWriter.fPadSegmentInfo != NULL ) {
- ++fCommandCount;
- fSize += sizeof(macho_segment_command<P>);
- }
-}
-
-template <>
-uint64_t LoadCommandAtom<ppc>::alignedSize(uint64_t size)
-{
- return ((size+3) & (-4)); // 4-byte align all load commands for 32-bit mach-o
-}
-
-template <>
-uint64_t LoadCommandAtom<ppc64>::alignedSize(uint64_t size)
-{
- return ((size+7) & (-8)); // 8-byte align all load commands for 64-bit mach-o
-}
-
-template <>
-uint64_t LoadCommandAtom<x86>::alignedSize(uint64_t size)
-{
- 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 <>
-uint64_t LoadCommandAtom<arm>::alignedSize(uint64_t size)
-{
- return ((size+3) & (-4)); // 4-byte align all load commands for 32-bit mach-o
-}
-
-template <typename A>
-void SegmentLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
-{
- uint64_t size = this->getSize();
- const bool oneSegment =( fWriter.fOptions.outputKind() == Options::kObjectFile );
- bzero(buffer, size);
- uint8_t* p = buffer;
- typename std::vector<SegmentInfo*>& segmentInfos = fWriter.fSegmentInfos;
- const int segCount = segmentInfos.size();
- for(int i=0; i < segCount; ++i) {
- SegmentInfo* segInfo = segmentInfos[i];
- const int sectionCount = segInfo->fSections.size();
- macho_segment_command<P>* cmd = (macho_segment_command<P>*)p;
- cmd->set_cmd(macho_segment_command<P>::CMD);
- cmd->set_segname(segInfo->fName);
- cmd->set_vmaddr(segInfo->fBaseAddress);
- cmd->set_vmsize(segInfo->fSize);
- cmd->set_fileoff(segInfo->fFileOffset);
- cmd->set_filesize(segInfo->fFileSize);
- cmd->set_maxprot(segInfo->fMaxProtection);
- cmd->set_initprot(segInfo->fInitProtection);
- // add sections array
- macho_section<P>* const sections = (macho_section<P>*)&p[sizeof(macho_segment_command<P>)];
- unsigned int sectionsEmitted = 0;
- for (int j=0; j < sectionCount; ++j) {
- SectionInfo* sectInfo = segInfo->fSections[j];
- if ( fWriter.fEmitVirtualSections || !sectInfo->fVirtualSection ) {
- macho_section<P>* sect = §ions[sectionsEmitted++];
- if ( oneSegment ) {
- // .o file segment does not cover load commands, so recalc at first real section
- if ( sectionsEmitted == 1 ) {
- cmd->set_vmaddr(sectInfo->getBaseAddress());
- cmd->set_fileoff(sectInfo->fFileOffset);
- }
- cmd->set_filesize((sectInfo->fFileOffset+sectInfo->fSize)-cmd->fileoff());
- cmd->set_vmsize(sectInfo->getBaseAddress() + sectInfo->fSize);
- }
- sect->set_sectname(sectInfo->fSectionName);
- sect->set_segname(sectInfo->fSegmentName);
- sect->set_addr(sectInfo->getBaseAddress());
- sect->set_size(sectInfo->fSize);
- sect->set_offset(sectInfo->fFileOffset);
- sect->set_align(sectInfo->fAlignment);
- if ( sectInfo->fRelocCount != 0 ) {
- sect->set_reloff(sectInfo->fRelocOffset * sizeof(macho_relocation_info<P>) + fWriter.fSectionRelocationsAtom->getFileOffset());
- sect->set_nreloc(sectInfo->fRelocCount);
- }
- if ( sectInfo->fAllZeroFill ) {
- sect->set_flags(S_ZEROFILL);
- sect->set_offset(0);
- }
- else if ( sectInfo->fAllLazyPointers ) {
- sect->set_flags(S_LAZY_SYMBOL_POINTERS);
- sect->set_reserved1(sectInfo->fIndirectSymbolOffset);
- }
- else if ( sectInfo->fAllLazyDylibPointers ) {
- sect->set_flags(S_LAZY_DYLIB_SYMBOL_POINTERS);
- sect->set_reserved1(sectInfo->fIndirectSymbolOffset);
- }
- else if ( sectInfo->fAllNonLazyPointers ) {
- sect->set_flags(S_NON_LAZY_SYMBOL_POINTERS);
- sect->set_reserved1(sectInfo->fIndirectSymbolOffset);
- }
- else if ( sectInfo->fAllStubs ) {
- sect->set_flags(S_SYMBOL_STUBS | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS);
- sect->set_reserved1(sectInfo->fIndirectSymbolOffset);
- sect->set_reserved2(sectInfo->fSize / sectInfo->fAtoms.size());
- }
- else if ( sectInfo->fAllSelfModifyingStubs ) {
- sect->set_flags(S_SYMBOL_STUBS | S_ATTR_SELF_MODIFYING_CODE);
- sect->set_reserved1(sectInfo->fIndirectSymbolOffset);
- sect->set_reserved2(sectInfo->fSize / sectInfo->fAtoms.size());
- }
- else if ( (strcmp(sectInfo->fSectionName, "__mod_init_func") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) {
- sect->set_flags(S_MOD_INIT_FUNC_POINTERS);
- }
- else if ( (strcmp(sectInfo->fSectionName, "__mod_term_func") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) {
- sect->set_flags(S_MOD_TERM_FUNC_POINTERS);
- }
- else if ( (strcmp(sectInfo->fSectionName, "__eh_frame") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) {
- sect->set_flags(S_COALESCED | S_ATTR_NO_TOC | S_ATTR_STRIP_STATIC_SYMS);
- }
- else if ( (strcmp(sectInfo->fSectionName, "__textcoal_nt") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) {
- sect->set_flags(S_COALESCED);
- }
- else if ( (strcmp(sectInfo->fSectionName, "__const_coal") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) {
- sect->set_flags(S_COALESCED);
- }
- else if ( (strcmp(sectInfo->fSectionName, "__interpose") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) {
- sect->set_flags(S_INTERPOSING);
- }
- else if ( (strcmp(sectInfo->fSectionName, "__cstring") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) {
- sect->set_flags(S_CSTRING_LITERALS);
- }
- else if ( (strcmp(sectInfo->fSectionName, "__literal4") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) {
- sect->set_flags(S_4BYTE_LITERALS);
- }
- else if ( (strcmp(sectInfo->fSectionName, "__literal8") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) {
- sect->set_flags(S_8BYTE_LITERALS);
- }
- else if ( (strcmp(sectInfo->fSectionName, "__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 ( (strcmp(sectInfo->fSectionName, "__cls_refs") == 0) && (strcmp(sectInfo->fSegmentName, "__OBJC") == 0) ) {
- sect->set_flags(S_LITERAL_POINTERS);
- }
- else if ( (strncmp(sectInfo->fSectionName, "__dof_", 6) == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) {
- sect->set_flags(S_DTRACE_DOF);
- }
- else if ( (strncmp(sectInfo->fSectionName, "__dof_", 6) == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) {
- sect->set_flags(S_DTRACE_DOF);
- }
- 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);
- if ( sectInfo->fHasTextLocalRelocs )
- sect->set_flags(sect->flags() | S_ATTR_LOC_RELOC);
- if ( sectInfo->fHasTextExternalRelocs )
- sect->set_flags(sect->flags() | S_ATTR_EXT_RELOC);
- }
- }
- }
- p = &p[sizeof(macho_segment_command<P>) + sectionsEmitted*sizeof(macho_section<P>)];
- cmd->set_cmdsize(sizeof(macho_segment_command<P>) + sectionsEmitted*sizeof(macho_section<P>));
- cmd->set_nsects(sectionsEmitted);
- }
-}
-
-
-template <typename A>
-SymbolTableLoadCommandsAtom<A>::SymbolTableLoadCommandsAtom(Writer<A>& writer)
- : LoadCommandAtom<A>(writer, Segment::fgTextSegment)
-{
- bzero(&fSymbolTable, sizeof(macho_symtab_command<P>));
- bzero(&fDynamicSymbolTable, sizeof(macho_dysymtab_command<P>));
- switch ( fWriter.fOptions.outputKind() ) {
- case Options::kDynamicExecutable:
- case Options::kDynamicLibrary:
- case Options::kDynamicBundle:
- case Options::kDyld:
- fNeedsDynamicSymbolTable = true;
- break;
- case Options::kObjectFile:
- case Options::kStaticExecutable:
- fNeedsDynamicSymbolTable = false;
- break;
- }
- writer.fSymbolTableCommands = this;
-}
-
-
-
-template <typename A>
-void SymbolTableLoadCommandsAtom<A>::needDynamicTable()
-{
- fNeedsDynamicSymbolTable = true;
-}
-
-
-template <typename A>
-uint64_t SymbolTableLoadCommandsAtom<A>::getSize() const
-{
- if ( fNeedsDynamicSymbolTable )
- return this->alignedSize(sizeof(macho_symtab_command<P>) + sizeof(macho_dysymtab_command<P>));
- else
- return this->alignedSize(sizeof(macho_symtab_command<P>));
-}
-
-template <typename A>
-void SymbolTableLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
-{
- // build LC_DYSYMTAB command
- macho_symtab_command<P>* symbolTableCmd = (macho_symtab_command<P>*)buffer;
- bzero(symbolTableCmd, sizeof(macho_symtab_command<P>));
- symbolTableCmd->set_cmd(LC_SYMTAB);
- symbolTableCmd->set_cmdsize(sizeof(macho_symtab_command<P>));
- symbolTableCmd->set_nsyms(fWriter.fSymbolTableCount);
- symbolTableCmd->set_symoff(fWriter.fSymbolTableAtom->getFileOffset());
- symbolTableCmd->set_stroff(fWriter.fStringsAtom->getFileOffset());
- symbolTableCmd->set_strsize(fWriter.fStringsAtom->getSize());
-
- // build LC_DYSYMTAB command
- if ( fNeedsDynamicSymbolTable ) {
- macho_dysymtab_command<P>* dynamicSymbolTableCmd = (macho_dysymtab_command<P>*)&buffer[sizeof(macho_symtab_command<P>)];
- bzero(dynamicSymbolTableCmd, sizeof(macho_dysymtab_command<P>));
- dynamicSymbolTableCmd->set_cmd(LC_DYSYMTAB);
- dynamicSymbolTableCmd->set_cmdsize(sizeof(macho_dysymtab_command<P>));
- dynamicSymbolTableCmd->set_ilocalsym(fWriter.fSymbolTableStabsStartIndex);
- dynamicSymbolTableCmd->set_nlocalsym(fWriter.fSymbolTableStabsCount + fWriter.fSymbolTableLocalCount);
- dynamicSymbolTableCmd->set_iextdefsym(fWriter.fSymbolTableExportStartIndex);
- dynamicSymbolTableCmd->set_nextdefsym(fWriter.fSymbolTableExportCount);
- dynamicSymbolTableCmd->set_iundefsym(fWriter.fSymbolTableImportStartIndex);
- dynamicSymbolTableCmd->set_nundefsym(fWriter.fSymbolTableImportCount);
- if ( fWriter.fModuleInfoAtom != NULL ) {
- dynamicSymbolTableCmd->set_tocoff(fWriter.fModuleInfoAtom->getTableOfContentsFileOffset());
- dynamicSymbolTableCmd->set_ntoc(fWriter.fSymbolTableExportCount);
- dynamicSymbolTableCmd->set_modtaboff(fWriter.fModuleInfoAtom->getModuleTableFileOffset());
- dynamicSymbolTableCmd->set_nmodtab(1);
- dynamicSymbolTableCmd->set_extrefsymoff(fWriter.fModuleInfoAtom->getReferencesFileOffset());
- dynamicSymbolTableCmd->set_nextrefsyms(fWriter.fModuleInfoAtom->getReferencesCount());
- }
- dynamicSymbolTableCmd->set_indirectsymoff(fWriter.fIndirectTableAtom->getFileOffset());
- dynamicSymbolTableCmd->set_nindirectsyms(fWriter.fIndirectTableAtom->fTable.size());
- if ( fWriter.fOptions.outputKind() != Options::kObjectFile ) {
- dynamicSymbolTableCmd->set_extreloff((fWriter.fExternalRelocs.size()==0) ? 0 : fWriter.fExternalRelocationsAtom->getFileOffset());
- dynamicSymbolTableCmd->set_nextrel(fWriter.fExternalRelocs.size());
- dynamicSymbolTableCmd->set_locreloff((fWriter.fInternalRelocs.size()==0) ? 0 : fWriter.fLocalRelocationsAtom->getFileOffset());
- dynamicSymbolTableCmd->set_nlocrel(fWriter.fInternalRelocs.size());
- }
- }
-}
-
-
-template <typename A>
-unsigned int SymbolTableLoadCommandsAtom<A>::commandCount()
-{
- return fNeedsDynamicSymbolTable ? 2 : 1;
-}
-
-template <typename A>
-uint64_t DyldLoadCommandsAtom<A>::getSize() const
-{
- return this->alignedSize(sizeof(macho_dylinker_command<P>) + strlen("/usr/lib/dyld") + 1);
-}
-
-template <typename A>
-void DyldLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
-{
- uint64_t size = this->getSize();
- bzero(buffer, size);
- macho_dylinker_command<P>* cmd = (macho_dylinker_command<P>*)buffer;
- if ( fWriter.fOptions.outputKind() == Options::kDyld )
- cmd->set_cmd(LC_ID_DYLINKER);
- else
- cmd->set_cmd(LC_LOAD_DYLINKER);
- cmd->set_cmdsize(this->getSize());
- cmd->set_name_offset();
- strcpy((char*)&buffer[sizeof(macho_dylinker_command<P>)], "/usr/lib/dyld");
-}
-
-template <typename A>
-uint64_t AllowableClientLoadCommandsAtom<A>::getSize() const
-{
- return this->alignedSize(sizeof(macho_sub_client_command<P>) + strlen(this->clientString) + 1);
-}
-
-template <typename A>
-void AllowableClientLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
-{
- uint64_t size = this->getSize();
-
- bzero(buffer, size);
- macho_sub_client_command<P>* cmd = (macho_sub_client_command<P>*)buffer;
- cmd->set_cmd(LC_SUB_CLIENT);
- cmd->set_cmdsize(size);
- cmd->set_client_offset();
- strcpy((char*)&buffer[sizeof(macho_sub_client_command<P>)], this->clientString);
-
-}
-
-template <typename A>
-uint64_t DylibLoadCommandsAtom<A>::getSize() const
-{
- if ( fOptimizedAway ) {
- return 0;
- }
- else {
- const char* path = fInfo.reader->getInstallPath();
- return this->alignedSize(sizeof(macho_dylib_command<P>) + strlen(path) + 1);
- }
-}
-
-template <typename A>
-void DylibLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
-{
- if ( fOptimizedAway )
- return;
- uint64_t size = this->getSize();
- bzero(buffer, size);
- const char* path = fInfo.reader->getInstallPath();
- macho_dylib_command<P>* cmd = (macho_dylib_command<P>*)buffer;
- // <rdar://problem/5529626> If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB
- bool autoWeakLoadDylib = ( (fWriter.fDylibReadersWithWeakImports.count(fInfo.reader) > 0)
- && (fWriter.fDylibReadersWithNonWeakImports.count(fInfo.reader) == 0) );
- if ( fInfo.options.fLazyLoad )
- cmd->set_cmd(LC_LAZY_LOAD_DYLIB);
- else if ( fInfo.options.fWeakImport || autoWeakLoadDylib )
- cmd->set_cmd(LC_LOAD_WEAK_DYLIB);
- else if ( fInfo.options.fReExport && (fWriter.fOptions.macosxVersionMin() >= ObjectFile::ReaderOptions::k10_5) )
- cmd->set_cmd(LC_REEXPORT_DYLIB);
- else
- cmd->set_cmd(LC_LOAD_DYLIB);
- cmd->set_cmdsize(this->getSize());
- cmd->set_timestamp(2); // needs to be some constant value that is different than DylibIDLoadCommandsAtom uses
- cmd->set_current_version(fInfo.reader->getCurrentVersion());
- cmd->set_compatibility_version(fInfo.reader->getCompatibilityVersion());
- cmd->set_name_offset();
- strcpy((char*)&buffer[sizeof(macho_dylib_command<P>)], path);
-}
-
-
-
-template <typename A>
-uint64_t DylibIDLoadCommandsAtom<A>::getSize() const
-{
- return this->alignedSize(sizeof(macho_dylib_command<P>) + strlen(fWriter.fOptions.installPath()) + 1);
-}
-
-template <typename A>
-void DylibIDLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
-{
- uint64_t size = this->getSize();
- bzero(buffer, size);
- macho_dylib_command<P>* cmd = (macho_dylib_command<P>*)buffer;
- cmd->set_cmd(LC_ID_DYLIB);
- cmd->set_cmdsize(this->getSize());
- cmd->set_name_offset();
- cmd->set_timestamp(1); // needs to be some constant value that is different than DylibLoadCommandsAtom uses
- cmd->set_current_version(fWriter.fOptions.currentVersion());
- cmd->set_compatibility_version(fWriter.fOptions.compatibilityVersion());
- strcpy((char*)&buffer[sizeof(macho_dylib_command<P>)], fWriter.fOptions.installPath());
-}
-
-
-template <typename A>
-void RoutinesLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
-{
- uint64_t initAddr = fWriter.getAtomLoadAddress(fWriter.fEntryPoint);
- if (fWriter.fEntryPoint->isThumb())
- initAddr |= 1ULL;
- bzero(buffer, sizeof(macho_routines_command<P>));
- macho_routines_command<P>* cmd = (macho_routines_command<P>*)buffer;
- cmd->set_cmd(macho_routines_command<P>::CMD);
- cmd->set_cmdsize(this->getSize());
- cmd->set_init_address(initAddr);
-}
-
-
-template <typename A>
-uint64_t SubUmbrellaLoadCommandsAtom<A>::getSize() const
-{
- return this->alignedSize(sizeof(macho_sub_umbrella_command<P>) + strlen(fName) + 1);
-}
-
-template <typename A>
-void SubUmbrellaLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
-{
- uint64_t size = this->getSize();
- bzero(buffer, size);
- macho_sub_umbrella_command<P>* cmd = (macho_sub_umbrella_command<P>*)buffer;
- cmd->set_cmd(LC_SUB_UMBRELLA);
- cmd->set_cmdsize(this->getSize());
- cmd->set_sub_umbrella_offset();
- strcpy((char*)&buffer[sizeof(macho_sub_umbrella_command<P>)], fName);
-}
-
-template <typename A>
-void UUIDLoadCommandAtom<A>::generate()
-{
- switch ( fWriter.fOptions.getUUIDMode() ) {
- case Options::kUUIDNone:
- fEmit = false;
- break;
- case Options::kUUIDRandom:
- ::uuid_generate_random(fUUID);
- fEmit = true;
- break;
- case Options::kUUIDContent:
- bzero(fUUID, 16);
- fEmit = true;
- break;
- }
-}
-
-template <typename A>
-void UUIDLoadCommandAtom<A>::setContent(const uint8_t uuid[16])
-{
- memcpy(fUUID, uuid, 16);
-}
-
-template <typename A>
-void UUIDLoadCommandAtom<A>::copyRawContent(uint8_t buffer[]) const
-{
- if (fEmit) {
- uint64_t size = this->getSize();
- bzero(buffer, size);
- macho_uuid_command<P>* cmd = (macho_uuid_command<P>*)buffer;
- cmd->set_cmd(LC_UUID);
- cmd->set_cmdsize(this->getSize());
- cmd->set_uuid((uint8_t*)fUUID);
- }
-}
-
-
-template <typename A>
-uint64_t SubLibraryLoadCommandsAtom<A>::getSize() const
-{
- return this->alignedSize(sizeof(macho_sub_library_command<P>) + fNameLength + 1);
-}
-
-template <typename A>
-void SubLibraryLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
-{
- uint64_t size = this->getSize();
- bzero(buffer, size);
- macho_sub_library_command<P>* cmd = (macho_sub_library_command<P>*)buffer;
- cmd->set_cmd(LC_SUB_LIBRARY);
- cmd->set_cmdsize(this->getSize());
- cmd->set_sub_library_offset();
- strncpy((char*)&buffer[sizeof(macho_sub_library_command<P>)], fNameStart, fNameLength);
- buffer[sizeof(macho_sub_library_command<P>)+fNameLength] = '\0';
-}
-
-template <typename A>
-uint64_t UmbrellaLoadCommandsAtom<A>::getSize() const
-{
- return this->alignedSize(sizeof(macho_sub_framework_command<P>) + strlen(fName) + 1);
-}
-
-template <typename A>
-void UmbrellaLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
-{
- uint64_t size = this->getSize();
- bzero(buffer, size);
- macho_sub_framework_command<P>* cmd = (macho_sub_framework_command<P>*)buffer;
- cmd->set_cmd(LC_SUB_FRAMEWORK);
- cmd->set_cmdsize(this->getSize());
- cmd->set_umbrella_offset();
- strcpy((char*)&buffer[sizeof(macho_sub_framework_command<P>)], fName);
-}
-
-template <>
-uint64_t ThreadsLoadCommandsAtom<ppc>::getSize() const
-{
- return this->alignedSize(16 + 40*4); // base size + PPC_THREAD_STATE_COUNT * 4
-}
-
-template <>
-uint64_t ThreadsLoadCommandsAtom<ppc64>::getSize() const
-{
- return this->alignedSize(16 + 76*4); // base size + PPC_THREAD_STATE64_COUNT * 4
-}
-
-template <>
-uint64_t ThreadsLoadCommandsAtom<x86>::getSize() const
-{
- 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);
-}
-
-// We should be picking it up from a header
-template <>
-uint64_t ThreadsLoadCommandsAtom<arm>::getSize() const
-{
- return this->alignedSize(16 + 17 * 4); // base size + ARM_THREAD_STATE_COUNT * 4
-}
-
-template <>
-void ThreadsLoadCommandsAtom<ppc>::copyRawContent(uint8_t buffer[]) const
-{
- uint64_t size = this->getSize();
- uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint);
- bzero(buffer, size);
- macho_thread_command<ppc::P>* cmd = (macho_thread_command<ppc::P>*)buffer;
- cmd->set_cmd(LC_UNIXTHREAD);
- cmd->set_cmdsize(size);
- cmd->set_flavor(1); // PPC_THREAD_STATE
- cmd->set_count(40); // PPC_THREAD_STATE_COUNT;
- cmd->set_thread_register(0, start);
- if ( fWriter.fOptions.hasCustomStack() )
- cmd->set_thread_register(3, fWriter.fOptions.customStackAddr()); // r1
-}
-
-
-template <>
-void ThreadsLoadCommandsAtom<ppc64>::copyRawContent(uint8_t buffer[]) const
-{
- uint64_t size = this->getSize();
- uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint);
- bzero(buffer, size);
- macho_thread_command<ppc64::P>* cmd = (macho_thread_command<ppc64::P>*)buffer;
- cmd->set_cmd(LC_UNIXTHREAD);
- cmd->set_cmdsize(size);
- cmd->set_flavor(5); // PPC_THREAD_STATE64
- cmd->set_count(76); // PPC_THREAD_STATE64_COUNT;
- cmd->set_thread_register(0, start);
- if ( fWriter.fOptions.hasCustomStack() )
- cmd->set_thread_register(3, fWriter.fOptions.customStackAddr()); // r1
-}
-
-template <>
-void ThreadsLoadCommandsAtom<x86>::copyRawContent(uint8_t buffer[]) const
-{
- uint64_t size = this->getSize();
- uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint);
- bzero(buffer, size);
- macho_thread_command<x86::P>* cmd = (macho_thread_command<x86::P>*)buffer;
- cmd->set_cmd(LC_UNIXTHREAD);
- cmd->set_cmdsize(size);
- cmd->set_flavor(1); // i386_THREAD_STATE
- cmd->set_count(16); // i386_THREAD_STATE_COUNT;
- cmd->set_thread_register(10, start);
- if ( fWriter.fOptions.hasCustomStack() )
- cmd->set_thread_register(7, fWriter.fOptions.customStackAddr()); // esp
-}
-
-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 <>
-void ThreadsLoadCommandsAtom<arm>::copyRawContent(uint8_t buffer[]) const
-{
- uint64_t size = this->getSize();
- uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint);
- bzero(buffer, size);
- macho_thread_command<arm::P>* cmd = (macho_thread_command<arm::P>*)buffer;
- cmd->set_cmd(LC_UNIXTHREAD);
- cmd->set_cmdsize(size);
- cmd->set_flavor(1);
- cmd->set_count(17);
- cmd->set_thread_register(15, start); // pc
- if ( fWriter.fOptions.hasCustomStack() )
- cmd->set_thread_register(13, fWriter.fOptions.customStackAddr()); // FIXME: sp?
-}
-
-template <typename A>
-uint64_t RPathLoadCommandsAtom<A>::getSize() const
-{
- return this->alignedSize(sizeof(macho_rpath_command<P>) + strlen(fPath) + 1);
-}
-
-template <typename A>
-void RPathLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
-{
- uint64_t size = this->getSize();
- bzero(buffer, size);
- macho_rpath_command<P>* cmd = (macho_rpath_command<P>*)buffer;
- cmd->set_cmd(LC_RPATH);
- cmd->set_cmdsize(this->getSize());
- cmd->set_path_offset();
- strcpy((char*)&buffer[sizeof(macho_rpath_command<P>)], fPath);
-}
-
-
-
-template <typename A>
-void EncryptionLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
-{
- uint64_t size = this->getSize();
- bzero(buffer, size);
- macho_encryption_info_command<P>* cmd = (macho_encryption_info_command<P>*)buffer;
- cmd->set_cmd(LC_ENCRYPTION_INFO);
- cmd->set_cmdsize(this->getSize());
- cmd->set_cryptoff(fStartOffset);
- cmd->set_cryptsize(fEndOffset-fStartOffset);
- cmd->set_cryptid(0);
-}
-
-
-
-template <typename A>
-void LoadCommandsPaddingAtom<A>::copyRawContent(uint8_t buffer[]) const
-{
- bzero(buffer, fSize);
-}
-
-template <typename A>
-void LoadCommandsPaddingAtom<A>::setSize(uint64_t newSize)
-{
- fSize = newSize;
- // this resizing by-passes the way fLargestAtomSize is set, so re-check here
- if ( fWriter.fLargestAtomSize < newSize )
- fWriter.fLargestAtomSize = newSize;
-}
-
-template <typename A>
-uint64_t LinkEditAtom<A>::getFileOffset() const
-{
- return ((SectionInfo*)this->getSection())->fFileOffset + this->getSectionOffset();
-}
-
-
-template <typename A>
-uint64_t SectionRelocationsLinkEditAtom<A>::getSize() const
-{
- return fWriter.fSectionRelocs.size() * sizeof(macho_relocation_info<P>);
-}
-
-template <typename A>
-void SectionRelocationsLinkEditAtom<A>::copyRawContent(uint8_t buffer[]) const
-{
- memcpy(buffer, &fWriter.fSectionRelocs[0], this->getSize());
-}
-
-
-template <typename A>
-uint64_t LocalRelocationsLinkEditAtom<A>::getSize() const
-{
- return fWriter.fInternalRelocs.size() * sizeof(macho_relocation_info<P>);
-}
-
-template <typename A>
-void LocalRelocationsLinkEditAtom<A>::copyRawContent(uint8_t buffer[]) const
-{
- memcpy(buffer, &fWriter.fInternalRelocs[0], this->getSize());
-}
-
-
-
-template <typename A>
-uint64_t SymbolTableLinkEditAtom<A>::getSize() const
-{
- return fWriter.fSymbolTableCount * sizeof(macho_nlist<P>);
-}
-
-template <typename A>
-void SymbolTableLinkEditAtom<A>::copyRawContent(uint8_t buffer[]) const
-{
- memcpy(buffer, fWriter.fSymbolTable, this->getSize());
-}
-
-template <typename A>
-uint64_t ExternalRelocationsLinkEditAtom<A>::getSize() const
-{
- return fWriter.fExternalRelocs.size() * sizeof(macho_relocation_info<P>);
-}
-
-template <typename A>
-void ExternalRelocationsLinkEditAtom<A>::copyRawContent(uint8_t buffer[]) const
-{
- std::sort(fWriter.fExternalRelocs.begin(), fWriter.fExternalRelocs.end(), ExternalRelocSorter<P>());
- memcpy(buffer, &fWriter.fExternalRelocs[0], this->getSize());
-}
-
-
-
-template <typename A>
-uint64_t IndirectTableLinkEditAtom<A>::getSize() const
-{
- return fTable.size() * sizeof(uint32_t);
-}
-
-template <typename A>
-void IndirectTableLinkEditAtom<A>::copyRawContent(uint8_t buffer[]) const
-{
- uint64_t size = this->getSize();
- bzero(buffer, size);
- const uint32_t indirectTableSize = fTable.size();
- uint32_t* indirectTable = (uint32_t*)buffer;
- for(std::vector<IndirectEntry>::const_iterator it = fTable.begin(); it != fTable.end(); ++it) {
- if ( it->indirectIndex < indirectTableSize ) {
- A::P::E::set32(indirectTable[it->indirectIndex], it->symbolIndex);
- }
- else {
- throwf("malformed indirect table. size=%d, index=%d", indirectTableSize, it->indirectIndex);
- }
- }
-}
-
-
-
-template <typename A>
-uint64_t ModuleInfoLinkEditAtom<A>::getSize() const
-{
- return fWriter.fSymbolTableExportCount*sizeof(macho_dylib_table_of_contents<P>)
- + sizeof(macho_dylib_module<P>)
- + this->getReferencesCount()*sizeof(uint32_t);
-}
-
-template <typename A>
-uint32_t ModuleInfoLinkEditAtom<A>::getTableOfContentsFileOffset() const
-{
- return this->getFileOffset();
-}
-
-template <typename A>
-uint32_t ModuleInfoLinkEditAtom<A>::getModuleTableFileOffset() const
-{
- return this->getFileOffset() + fWriter.fSymbolTableExportCount*sizeof(macho_dylib_table_of_contents<P>);
-}
-
-template <typename A>
-uint32_t ModuleInfoLinkEditAtom<A>::getReferencesFileOffset() const
-{
- return this->getModuleTableFileOffset() + sizeof(macho_dylib_module<P>);
-}
-
-template <typename A>
-uint32_t ModuleInfoLinkEditAtom<A>::getReferencesCount() const
-{
- return fWriter.fSymbolTableExportCount + fWriter.fSymbolTableImportCount;
-}
-
-template <typename A>
-void ModuleInfoLinkEditAtom<A>::copyRawContent(uint8_t buffer[]) const
-{
- uint64_t size = this->getSize();
- bzero(buffer, size);
- // create toc. The symbols are already sorted, they are all in the smae module
- macho_dylib_table_of_contents<P>* p = (macho_dylib_table_of_contents<P>*)buffer;
- for(uint32_t i=0; i < fWriter.fSymbolTableExportCount; ++i, ++p) {
- p->set_symbol_index(fWriter.fSymbolTableExportStartIndex+i);
- p->set_module_index(0);
- }
- // create module table (one entry)
- uint16_t numInits = 0;
- uint16_t numTerms = 0;
- std::vector<SegmentInfo*>& segmentInfos = fWriter.fSegmentInfos;
- for (std::vector<SegmentInfo*>::iterator segit = segmentInfos.begin(); segit != segmentInfos.end(); ++segit) {
- if ( strcmp((*segit)->fName, "__DATA") == 0 ) {
- std::vector<SectionInfo*>& sectionInfos = (*segit)->fSections;
- for (std::vector<SectionInfo*>::iterator sectit = sectionInfos.begin(); sectit != sectionInfos.end(); ++sectit) {
- if ( strcmp((*sectit)->fSectionName, "__mod_init_func") == 0 )
- numInits = (*sectit)->fSize / sizeof(typename A::P::uint_t);
- else if ( strcmp((*sectit)->fSectionName, "__mod_term_func") == 0 )
- numTerms = (*sectit)->fSize / sizeof(typename A::P::uint_t);
- }
- }
- }
- macho_dylib_module<P>* module = (macho_dylib_module<P>*)&buffer[fWriter.fSymbolTableExportCount*sizeof(macho_dylib_table_of_contents<P>)];
- module->set_module_name(fModuleNameOffset);
- module->set_iextdefsym(fWriter.fSymbolTableExportStartIndex);
- module->set_nextdefsym(fWriter.fSymbolTableExportCount);
- module->set_irefsym(0);
- module->set_nrefsym(this->getReferencesCount());
- module->set_ilocalsym(fWriter.fSymbolTableStabsStartIndex);
- module->set_nlocalsym(fWriter.fSymbolTableStabsCount+fWriter.fSymbolTableLocalCount);
- module->set_iextrel(0);
- module->set_nextrel(fWriter.fExternalRelocs.size());
- module->set_iinit_iterm(0,0);
- module->set_ninit_nterm(numInits,numTerms);
- module->set_objc_module_info_addr(0); // Not used by ld_classic, and not used by objc runtime for many years
- module->set_objc_module_info_size(0); // Not used by ld_classic, and not used by objc runtime for many years
- // create reference table
- macho_dylib_reference<P>* ref = (macho_dylib_reference<P>*)((uint8_t*)module + sizeof(macho_dylib_module<P>));
- for(uint32_t i=0; i < fWriter.fSymbolTableExportCount; ++i, ++ref) {
- ref->set_isym(fWriter.fSymbolTableExportStartIndex+i);
- ref->set_flags(REFERENCE_FLAG_DEFINED);
- }
- for(uint32_t i=0; i < fWriter.fSymbolTableImportCount; ++i, ++ref) {
- ref->set_isym(fWriter.fSymbolTableImportStartIndex+i);
- std::map<const ObjectFile::Atom*,ObjectFile::Atom*>::iterator pos = fWriter.fStubsMap.find(fWriter.fImportedAtoms[i]);
- if ( pos != fWriter.fStubsMap.end() )
- ref->set_flags(REFERENCE_FLAG_UNDEFINED_LAZY);
- else
- ref->set_flags(REFERENCE_FLAG_UNDEFINED_NON_LAZY);
- }
-}
-
-
-
-template <typename A>
-StringsLinkEditAtom<A>::StringsLinkEditAtom(Writer<A>& writer)
- : LinkEditAtom<A>(writer), fCurrentBuffer(NULL), fCurrentBufferUsed(0)
-{
- fCurrentBuffer = new char[kBufferSize];
- // burn first byte of string pool (so zero is never a valid string offset)
- fCurrentBuffer[fCurrentBufferUsed++] = ' ';
- // make offset 1 always point to an empty string
- fCurrentBuffer[fCurrentBufferUsed++] = '\0';
-}
-
-template <typename A>
-uint64_t StringsLinkEditAtom<A>::getSize() const
-{
- // align size
- return (kBufferSize * fFullBuffers.size() + fCurrentBufferUsed + sizeof(typename A::P::uint_t) - 1) & (-sizeof(typename A::P::uint_t));
-}
-
-template <typename A>
-void StringsLinkEditAtom<A>::copyRawContent(uint8_t buffer[]) const
-{
- uint64_t offset = 0;
- for (unsigned int i=0; i < fFullBuffers.size(); ++i) {
- memcpy(&buffer[offset], fFullBuffers[i], kBufferSize);
- offset += kBufferSize;
- }
- memcpy(&buffer[offset], fCurrentBuffer, fCurrentBufferUsed);
- // zero fill end to align
- offset += fCurrentBufferUsed;
- while ( (offset % sizeof(typename A::P::uint_t)) != 0 )
- buffer[offset++] = 0;
-}
-
-template <typename A>
-int32_t StringsLinkEditAtom<A>::add(const char* name)
-{
- int32_t offset = kBufferSize * fFullBuffers.size() + fCurrentBufferUsed;
- int lenNeeded = strlcpy(&fCurrentBuffer[fCurrentBufferUsed], name, kBufferSize-fCurrentBufferUsed)+1;
- if ( (fCurrentBufferUsed+lenNeeded) < kBufferSize ) {
- fCurrentBufferUsed += lenNeeded;
- }
- else {
- int copied = kBufferSize-fCurrentBufferUsed-1;
- // change trailing '\0' that strlcpy added to real char
- fCurrentBuffer[kBufferSize-1] = name[copied];
- // alloc next buffer
- fFullBuffers.push_back(fCurrentBuffer);
- fCurrentBuffer = new char[kBufferSize];
- fCurrentBufferUsed = 0;
- // append rest of string
- this->add(&name[copied+1]);
- }
- return offset;
-}
-
-
-template <typename A>
-int32_t StringsLinkEditAtom<A>::addUnique(const char* name)
-{
- StringToOffset::iterator pos = fUniqueStrings.find(name);
- if ( pos != fUniqueStrings.end() ) {
- return pos->second;
- }
- else {
- int32_t offset = this->add(name);
- fUniqueStrings[name] = offset;
- return offset;
- }
-}
-
-
-template <typename A>
-const char* StringsLinkEditAtom<A>::stringForIndex(int32_t index) const
-{
- int32_t currentBufferStartIndex = kBufferSize * fFullBuffers.size();
- int32_t maxIndex = currentBufferStartIndex + fCurrentBufferUsed;
- // check for out of bounds
- if ( index > maxIndex )
- return "";
- // check for index in fCurrentBuffer
- if ( index > currentBufferStartIndex )
- return &fCurrentBuffer[index-currentBufferStartIndex];
- // otherwise index is in a full buffer
- uint32_t fullBufferIndex = index/kBufferSize;
- return &fFullBuffers[fullBufferIndex][index-(kBufferSize*fullBufferIndex)];
-}
-
-
-
-template <typename A>
-BranchIslandAtom<A>::BranchIslandAtom(Writer<A>& writer, const char* name, int islandRegion, ObjectFile::Atom& target, uint32_t targetOffset)
- : WriterAtom<A>(writer, Segment::fgTextSegment), fTarget(target), fTargetOffset(targetOffset)
-{
- char* buf = new char[strlen(name)+32];
- if ( targetOffset == 0 ) {
- if ( islandRegion == 0 )
- sprintf(buf, "%s$island", name);
- else
- sprintf(buf, "%s$island_%d", name, islandRegion);
- }
- else {
- sprintf(buf, "%s_plus_%d$island_%d", name, targetOffset, islandRegion);
- }
- fName = buf;
-}
-
-
-template <>
-void BranchIslandAtom<ppc>::copyRawContent(uint8_t buffer[]) const
-{
- int64_t displacement = fTarget.getAddress() + fTargetOffset - this->getAddress();
- int32_t branchInstruction = 0x48000000 | ((uint32_t)displacement & 0x03FFFFFC);
- OSWriteBigInt32(buffer, 0, branchInstruction);
-}
-
-template <>
-void BranchIslandAtom<ppc64>::copyRawContent(uint8_t buffer[]) const
-{
- int64_t displacement = fTarget.getAddress() + fTargetOffset - this->getAddress();
- int32_t branchInstruction = 0x48000000 | ((uint32_t)displacement & 0x03FFFFFC);
- OSWriteBigInt32(buffer, 0, branchInstruction);
-}
-
-template <>
-uint64_t BranchIslandAtom<ppc>::getSize() const
-{
- return 4;
-}
-
-template <>
-uint64_t BranchIslandAtom<ppc64>::getSize() const
-{
- return 4;
-}
-
-
-
-template <typename A>
-uint64_t SegmentSplitInfoLoadCommandsAtom<A>::getSize() const
-{
- if ( fWriter.fSplitCodeToDataContentAtom->canEncode() )
- return this->alignedSize(sizeof(macho_linkedit_data_command<P>));
- else
- return 0; // a zero size causes the load command to be suppressed
-}
-
-template <typename A>
-void SegmentSplitInfoLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
-{
- uint64_t size = this->getSize();
- bzero(buffer, size);
- macho_linkedit_data_command<P>* cmd = (macho_linkedit_data_command<P>*)buffer;
- cmd->set_cmd(LC_SEGMENT_SPLIT_INFO);
- cmd->set_cmdsize(size);
- cmd->set_dataoff(fWriter.fSplitCodeToDataContentAtom->getFileOffset());
- cmd->set_datasize(fWriter.fSplitCodeToDataContentAtom->getSize());
-}
-
-
-template <typename A>
-uint64_t SegmentSplitInfoContentAtom<A>::getSize() const
-{
- return fEncodedData.size();
-}
-
-template <typename A>
-void SegmentSplitInfoContentAtom<A>::copyRawContent(uint8_t buffer[]) const
-{
- memcpy(buffer, &fEncodedData[0], fEncodedData.size());
-}
-
-
-template <typename A>
-void SegmentSplitInfoContentAtom<A>::uleb128EncodeAddresses(const std::vector<SegmentSplitInfoContentAtom<A>::AtomAndOffset>& locations)
-{
- pint_t addr = fWriter.fOptions.baseAddress();
- for(typename std::vector<AtomAndOffset>::const_iterator it = locations.begin(); it != locations.end(); ++it) {
- pint_t nextAddr = it->atom->getAddress() + it->offset;
- //fprintf(stderr, "\t0x%0llX\n", (uint64_t)nextAddr);
- uint64_t delta = nextAddr - addr;
- if ( delta == 0 )
- throw "double split seg info for same address";
- // uleb128 encode
- uint8_t byte;
- do {
- byte = delta & 0x7F;
- delta &= ~0x7F;
- if ( delta != 0 )
- byte |= 0x80;
- fEncodedData.push_back(byte);
- delta = delta >> 7;
- }
- while( byte >= 0x80 );
- addr = nextAddr;
- }
-}
-
-template <typename A>
-void SegmentSplitInfoContentAtom<A>::encode()
-{
- if ( ! fCantEncode ) {
- fEncodedData.reserve(8192);
-
- if ( fKind1Locations.size() != 0 ) {
- fEncodedData.push_back(1);
- //fprintf(stderr, "type 1:\n");
- this->uleb128EncodeAddresses(fKind1Locations);
- fEncodedData.push_back(0);
- }
-
- if ( fKind2Locations.size() != 0 ) {
- fEncodedData.push_back(2);
- //fprintf(stderr, "type 2:\n");
- this->uleb128EncodeAddresses(fKind2Locations);
- fEncodedData.push_back(0);
- }
-
- if ( fKind3Locations.size() != 0 ) {
- fEncodedData.push_back(3);
- //fprintf(stderr, "type 3:\n");
- this->uleb128EncodeAddresses(fKind3Locations);
- fEncodedData.push_back(0);
- }
-
- if ( fKind4Locations.size() != 0 ) {
- fEncodedData.push_back(4);
- //fprintf(stderr, "type 4:\n");
- this->uleb128EncodeAddresses(fKind4Locations);
- fEncodedData.push_back(0);
- }
-
- // always add zero byte to mark end
- fEncodedData.push_back(0);
-
- // add zeros to end to align size
- while ( (fEncodedData.size() % sizeof(pint_t)) != 0 )
- fEncodedData.push_back(0);
- }
-}
-
-
-template <typename A>
-ObjCInfoAtom<A>::ObjCInfoAtom(Writer<A>& writer, ObjectFile::Reader::ObjcConstraint objcConstraint, bool objcReplacementClasses)
- : WriterAtom<A>(writer, getInfoSegment())
-{
- fContent[0] = 0;
- uint32_t value = 0;
- // struct objc_image_info {
- // uint32_t version; // initially 0
- // uint32_t flags;
- // };
- // #define OBJC_IMAGE_SUPPORTS_GC 2
- // #define OBJC_IMAGE_GC_ONLY 4
- //
- if ( objcReplacementClasses )
- value = 1;
- switch ( objcConstraint ) {
- case ObjectFile::Reader::kObjcNone:
- case ObjectFile::Reader::kObjcRetainRelease:
- break;
- case ObjectFile::Reader::kObjcRetainReleaseOrGC:
- value |= 2;
- break;
- case ObjectFile::Reader::kObjcGC:
- value |= 6;
- break;
- }
- A::P::E::set32(fContent[1], value);
-}
-
-template <typename A>
-void ObjCInfoAtom<A>::copyRawContent(uint8_t buffer[]) const
-{
- memcpy(buffer, &fContent[0], 8);
-}
-
-
-// objc info section is in a different segment and section for 32 vs 64 bit runtimes
-template <> const char* ObjCInfoAtom<ppc>::getSectionName() const { return "__image_info"; }
-template <> const char* ObjCInfoAtom<x86>::getSectionName() const { return "__image_info"; }
-template <> const char* ObjCInfoAtom<arm>::getSectionName() const { return "__objc_imageinfo"; }
-template <> const char* ObjCInfoAtom<ppc64>::getSectionName() const { return "__objc_imageinfo"; }
-template <> const char* ObjCInfoAtom<x86_64>::getSectionName() const { return "__objc_imageinfo"; }
-
-template <> Segment& ObjCInfoAtom<ppc>::getInfoSegment() const { return Segment::fgObjCSegment; }
-template <> Segment& ObjCInfoAtom<x86>::getInfoSegment() const { return Segment::fgObjCSegment; }
-template <> Segment& ObjCInfoAtom<ppc64>::getInfoSegment() const { return Segment::fgDataSegment; }
-template <> Segment& ObjCInfoAtom<x86_64>::getInfoSegment() const { return Segment::fgDataSegment; }
-template <> Segment& ObjCInfoAtom<arm>::getInfoSegment() const { return Segment::fgDataSegment; }
-
-
-}; // namespace executable
-}; // namespace mach_o
-
-
-#endif // __EXECUTABLE_MACH_O__
+++ /dev/null
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
- *
- * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <fcntl.h>
-#include <fcntl.h>
-
-#include "MachOReaderRelocatable.hpp"
-
-#define LTO_SUPPORT 1
-
-#if LTO_SUPPORT
- #include "LTOReader.hpp"
-#endif
-
-static bool sDumpContent= true;
-static bool sDumpStabs = false;
-static bool sSort = true;
-static bool sNMmode = false;
-static cpu_type_t sPreferredArch = CPU_TYPE_POWERPC64;
-static const char* sMatchName;
-static int sPrintRestrict;
-static int sPrintAlign;
-static int sPrintName;
-
-
- __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;
-}
-
-void warning(const char* format, ...)
-{
- va_list list;
- fprintf(stderr, "warning: ");
- va_start(list, format);
- vfprintf(stderr, format, list);
- va_end(list);
- fprintf(stderr, "\n");
-}
-
-static void dumpStabs(std::vector<ObjectFile::Reader::Stab>* stabs)
-{
- // debug info
- printf("stabs: (%lu)\n", stabs->size());
- for (std::vector<ObjectFile::Reader::Stab>::iterator it = stabs->begin(); it != stabs->end(); ++it ) {
- ObjectFile::Reader::Stab& stab = *it;
- const char* code = "?????";
- switch (stab.type) {
- case N_GSYM:
- code = " GSYM";
- break;
- case N_FNAME:
- code = "FNAME";
- break;
- case N_FUN:
- code = " FUN";
- break;
- case N_STSYM:
- code = "STSYM";
- break;
- case N_LCSYM:
- code = "LCSYM";
- break;
- case N_BNSYM:
- code = "BNSYM";
- break;
- case N_OPT:
- code = " OPT";
- break;
- case N_RSYM:
- code = " RSYM";
- break;
- case N_SLINE:
- code = "SLINE";
- break;
- case N_ENSYM:
- code = "ENSYM";
- break;
- case N_SSYM:
- code = " SSYM";
- break;
- case N_SO:
- code = " SO";
- break;
- case N_OSO:
- code = " OSO";
- break;
- case N_LSYM:
- code = " LSYM";
- break;
- case N_BINCL:
- code = "BINCL";
- break;
- case N_SOL:
- code = " SOL";
- break;
- case N_PARAMS:
- code = "PARMS";
- break;
- case N_VERSION:
- code = " VERS";
- break;
- case N_OLEVEL:
- code = "OLEVL";
- break;
- case N_PSYM:
- code = " PSYM";
- break;
- case N_EINCL:
- code = "EINCL";
- break;
- case N_ENTRY:
- code = "ENTRY";
- break;
- case N_LBRAC:
- code = "LBRAC";
- break;
- case N_EXCL:
- code = " EXCL";
- break;
- case N_RBRAC:
- code = "RBRAC";
- break;
- case N_BCOMM:
- code = "BCOMM";
- break;
- case N_ECOMM:
- code = "ECOMM";
- break;
- case N_LENG:
- code = "LENG";
- break;
- }
- printf(" [atom=%20s] %02X %04X %s %s\n", ((stab.atom != NULL) ? stab.atom->getDisplayName() : ""), stab.other, stab.desc, code, stab.string);
- }
-}
-
-
-static void dumpAtomLikeNM(ObjectFile::Atom* atom)
-{
- uint32_t size = atom->getSize();
-
- const char* visibility;
- switch ( atom->getScope() ) {
- case ObjectFile::Atom::scopeTranslationUnit:
- visibility = "internal";
- break;
- case ObjectFile::Atom::scopeLinkageUnit:
- visibility = "hidden ";
- break;
- case ObjectFile::Atom::scopeGlobal:
- visibility = "global ";
- break;
- default:
- visibility = " ";
- break;
- }
-
- const char* kind;
- switch ( atom->getDefinitionKind() ) {
- case ObjectFile::Atom::kRegularDefinition:
- kind = "regular ";
- break;
- case ObjectFile::Atom::kTentativeDefinition:
- kind = "tentative";
- break;
- case ObjectFile::Atom::kWeakDefinition:
- kind = "weak ";
- break;
- case ObjectFile::Atom::kAbsoluteSymbol:
- kind = "absolute ";
- break;
- default:
- kind = " ";
- break;
- }
-
- printf("0x%08X %s %s %s\n", size, visibility, kind, atom->getDisplayName());
-}
-
-
-static void dumpAtom(ObjectFile::Atom* atom)
-{
- if(sMatchName && strcmp(sMatchName, atom->getDisplayName()))
- return;
-
- //printf("atom: %p\n", atom);
-
- // name
- if(!sPrintRestrict || sPrintName)
- printf("name: %s\n", atom->getDisplayName());
-
- // scope
- if(!sPrintRestrict)
- switch ( atom->getScope() ) {
- case ObjectFile::Atom::scopeTranslationUnit:
- printf("scope: translation unit\n");
- break;
- case ObjectFile::Atom::scopeLinkageUnit:
- printf("scope: linkage unit\n");
- break;
- case ObjectFile::Atom::scopeGlobal:
- printf("scope: global\n");
- break;
- default:
- printf("scope: unknown\n");
- }
-
- // kind
- if(!sPrintRestrict)
- switch ( atom->getDefinitionKind() ) {
- case ObjectFile::Atom::kRegularDefinition:
- printf("kind: regular\n");
- break;
- case ObjectFile::Atom::kWeakDefinition:
- printf("kind: weak\n");
- break;
- case ObjectFile::Atom::kTentativeDefinition:
- printf("kind: tentative\n");
- break;
- case ObjectFile::Atom::kExternalDefinition:
- printf("kind: import\n");
- break;
- case ObjectFile::Atom::kExternalWeakDefinition:
- printf("kind: weak import\n");
- break;
- case ObjectFile::Atom::kAbsoluteSymbol:
- printf("kind: absolute symbol\n");
- break;
- default:
- printf("kind: unknown\n");
- }
-
- // segment and section
- if(!sPrintRestrict && (atom->getSectionName() != NULL) )
- printf("section: %s,%s\n", atom->getSegment().getName(), atom->getSectionName());
-
- // attributes
- if(!sPrintRestrict) {
- printf("attrs: ");
- if ( atom->dontDeadStrip() )
- printf("dont-dead-strip ");
- if ( atom->isZeroFill() )
- printf("zero-fill ");
- if ( atom->isThumb() )
- printf("thumb ");
- printf("\n");
- }
-
- // size
- if(!sPrintRestrict)
- printf("size: 0x%012llX\n", atom->getSize());
-
- // alignment
- if(!sPrintRestrict || sPrintAlign)
- printf("align: %u mod %u\n", atom->getAlignment().modulus, (1 << atom->getAlignment().powerOf2) );
-
- // content
- if (!sPrintRestrict && sDumpContent ) {
- uint64_t size = atom->getSize();
- if ( size < 4096 ) {
- uint8_t content[size];
- atom->copyRawContent(content);
- printf("content: ");
- if ( strcmp(atom->getSectionName(), "__cstring") == 0 ) {
- printf("\"");
- for (unsigned int i=0; i < size; ++i) {
- if(content[i]<'!' || content[i]>=127)
- printf("\\%o", content[i]);
- else
- printf("%c", content[i]);
- }
- printf("\"");
- }
- else {
- for (unsigned int i=0; i < size; ++i)
- printf("%02X ", content[i]);
- }
- }
- printf("\n");
- }
-
- // references
- if(!sPrintRestrict) {
- std::vector<ObjectFile::Reference*>& references = atom->getReferences();
- const int refCount = references.size();
- printf("references: (%u)\n", refCount);
- for (int i=0; i < refCount; ++i) {
- ObjectFile::Reference* ref = references[i];
- printf(" %s\n", ref->getDescription());
- }
- }
-
- // line info
- if(!sPrintRestrict) {
- std::vector<ObjectFile::LineInfo>* lineInfo = atom->getLineInfo();
- if ( (lineInfo != NULL) && (lineInfo->size() > 0) ) {
- printf("line info: (%lu)\n", lineInfo->size());
- for (std::vector<ObjectFile::LineInfo>::iterator it = lineInfo->begin(); it != lineInfo->end(); ++it) {
- printf(" offset 0x%04X, line %d, file %s\n", it->atomOffset, it->lineNumber, it->fileName);
- }
- }
- }
-
- if(!sPrintRestrict)
- printf("\n");
-}
-
-struct AtomSorter
-{
- bool operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right)
- {
- if ( left == right )
- return false;
- return (strcmp(left->getDisplayName(), right->getDisplayName()) < 0);
- }
-};
-
-
-static void dumpFile(ObjectFile::Reader* reader)
-{
- // stabs debug info
- if ( sDumpStabs && (reader->getDebugInfoKind() == ObjectFile::Reader::kDebugInfoStabs) ) {
- std::vector<ObjectFile::Reader::Stab>* stabs = reader->getStabs();
- if ( stabs != NULL )
- dumpStabs(stabs);
- }
-
- // get all atoms
- std::vector<ObjectFile::Atom*> atoms = reader->getAtoms();
-
- // make copy of vector and sort (so output is canonical)
- std::vector<ObjectFile::Atom*> sortedAtoms(atoms);
- if ( sSort )
- std::sort(sortedAtoms.begin(), sortedAtoms.end(), AtomSorter());
-
- for(std::vector<ObjectFile::Atom*>::iterator it=sortedAtoms.begin(); it != sortedAtoms.end(); ++it) {
- if ( sNMmode )
- dumpAtomLikeNM(*it);
- else
- dumpAtom(*it);
- }
-}
-
-
-static ObjectFile::Reader* createReader(const char* path, const ObjectFile::ReaderOptions& options)
-{
- struct stat stat_buf;
-
- int fd = ::open(path, O_RDONLY, 0);
- if ( fd == -1 )
- throwf("cannot open file: %s", path);
- ::fstat(fd, &stat_buf);
- uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | 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 < OSSwapBigToHostInt32(fh->nfat_arch); ++i) {
- if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)sPreferredArch ) {
- p = p + OSSwapBigToHostInt32(archs[i].offset);
- mh = (struct mach_header*)p;
- }
- }
- }
- if ( mach_o::relocatable::Reader<x86>::validFile(p) )
- return new mach_o::relocatable::Reader<x86>::Reader(p, path, 0, options, 0);
- else if ( mach_o::relocatable::Reader<ppc>::validFile(p) )
- return new mach_o::relocatable::Reader<ppc>::Reader(p, path, 0, options, 0);
- else if ( mach_o::relocatable::Reader<ppc64>::validFile(p) )
- return new mach_o::relocatable::Reader<ppc64>::Reader(p, path, 0, options, 0);
- else if ( mach_o::relocatable::Reader<x86_64>::validFile(p) )
- return new mach_o::relocatable::Reader<x86_64>::Reader(p, path, 0, options, 0);
- else if ( mach_o::relocatable::Reader<arm>::validFile(p) )
- return new mach_o::relocatable::Reader<arm>::Reader(p, path, 0, options, 0);
-#if LTO_SUPPORT
- if ( lto::Reader::validFile(p, stat_buf.st_size, 0) ) {
- return new lto::Reader(p, stat_buf.st_size, path, 0, options, 0);
- }
-#endif
-
- throwf("not a mach-o object file: %s", path);
-}
-
-static
-void
-usage()
-{
- fprintf(stderr, "ObjectDump options:\n"
- "\t-no_content\tdon't dump contents\n"
- "\t-stabs\t\tdump stabs\n"
- "\t-arch aaa\tonly dump info about arch aaa\n"
- "\t-only sym\tonly dump info about sym\n"
- "\t-align\t\tonly print alignment info\n"
- "\t-name\t\tonly print symbol names\n"
- );
-}
-
-int main(int argc, const char* argv[])
-{
- if(argc<2) {
- usage();
- return 0;
- }
-
- ObjectFile::ReaderOptions options;
- try {
- for(int i=1; i < argc; ++i) {
- const char* arg = argv[i];
- if ( arg[0] == '-' ) {
- if ( strcmp(arg, "-no_content") == 0 ) {
- sDumpContent = false;
- }
- else if ( strcmp(arg, "-nm") == 0 ) {
- sNMmode = true;
- }
- else if ( strcmp(arg, "-stabs") == 0 ) {
- sDumpStabs = true;
- }
- else if ( strcmp(arg, "-no_sort") == 0 ) {
- sSort = false;
- }
- else if ( strcmp(arg, "-arch") == 0 ) {
- const char* arch = ++i<argc? argv[i]: "";
- if ( strcmp(arch, "ppc64") == 0 )
- sPreferredArch = CPU_TYPE_POWERPC64;
- else if ( strcmp(arch, "ppc") == 0 )
- sPreferredArch = CPU_TYPE_POWERPC;
- else if ( strcmp(arch, "i386") == 0 )
- sPreferredArch = CPU_TYPE_I386;
- else if ( strcmp(arch, "x86_64") == 0 )
- sPreferredArch = CPU_TYPE_X86_64;
- else
- throwf("unknown architecture %s", arch);
- }
- else if ( strcmp(arg, "-only") == 0 ) {
- sMatchName = ++i<argc? argv[i]: NULL;
- }
- else if ( strcmp(arg, "-align") == 0 ) {
- sPrintRestrict = true;
- sPrintAlign = true;
- }
- else if ( strcmp(arg, "-name") == 0 ) {
- sPrintRestrict = true;
- sPrintName = true;
- }
- else {
- usage();
- throwf("unknown option: %s\n", arg);
- }
- }
- else {
- ObjectFile::Reader* reader = createReader(arg, options);
- dumpFile(reader);
- }
- }
- }
- catch (const char* msg) {
- fprintf(stderr, "ObjDump failed: %s\n", msg);
- return 1;
- }
-
- return 0;
-}
+++ /dev/null
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
- *
- * Copyright (c) 2005-2007 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-
-#ifndef __OBJECTFILE__
-#define __OBJECTFILE__
-
-#include <stdint.h>
-#include <vector>
-#include <map>
-
-
-
-//
-// These classes represent the abstract Atoms and References that are the basis of the linker.
-// An Atom and a Reference correspond to a Node and Edge in graph theory.
-//
-// A Reader is a class which parses an object file and presents it as Atoms and References.
-// All linking operations are done on Atoms and References. This makes the linker file
-// format independent.
-//
-// A Writer takes a vector of Atoms with all References resolved and produces an executable file.
-//
-//
-
-
-
-namespace ObjectFile {
-
-
-struct LineInfo
-{
- uint32_t atomOffset;
- const char* fileName;
- uint32_t lineNumber;
-};
-
-
-class ReaderOptions
-{
-public:
- ReaderOptions() : fFullyLoadArchives(false), fLoadAllObjcObjectsFromArchives(false), fFlatNamespace(false),
- fLinkingMainExecutable(false), fSlowx86Stubs(false),
- fForFinalLinkedImage(false), fForStatic(false), fForDyld(false), fMakeTentativeDefinitionsReal(false),
- fWhyLoad(false), fRootSafe(false), fSetuidSafe(false),fDebugInfoStripping(kDebugInfoFull),
- fImplicitlyLinkPublicDylibs(true), fLogObjectFiles(false), fLogAllFiles(false),
- fTraceDylibs(false), fTraceIndirectDylibs(false), fTraceArchives(false),
- fTraceOutputFile(NULL), fVersionMin(kMinUnset) {}
- enum DebugInfoStripping { kDebugInfoNone, kDebugInfoMinimal, kDebugInfoFull };
- enum VersionMin { kMinUnset, k10_1, k10_2, k10_3, k10_4, k10_5, k10_6 };
-
- struct AliasPair {
- const char* realName;
- const char* alias;
- };
-
- bool fFullyLoadArchives;
- bool fLoadAllObjcObjectsFromArchives;
- bool fFlatNamespace;
- bool fLinkingMainExecutable;
- bool fSlowx86Stubs;
- bool fForFinalLinkedImage;
- bool fForStatic;
- bool fForDyld;
- bool fMakeTentativeDefinitionsReal;
- bool fWhyLoad;
- bool fRootSafe;
- bool fSetuidSafe;
- DebugInfoStripping fDebugInfoStripping;
- bool fImplicitlyLinkPublicDylibs;
- bool fLogObjectFiles;
- bool fLogAllFiles;
- bool fTraceDylibs;
- bool fTraceIndirectDylibs;
- bool fTraceArchives;
- const char* fTraceOutputFile;
- VersionMin fVersionMin;
- std::vector<AliasPair> fAliases;
-};
-
-
-class Reader
-{
-public:
- enum DebugInfoKind { kDebugInfoNone=0, kDebugInfoStabs=1, kDebugInfoDwarf=2, kDebugInfoStabsUUID=3 };
- struct Stab
- {
- class Atom* atom;
- uint8_t type;
- uint8_t other;
- uint16_t desc;
- uint32_t value;
- const char* string;
- };
- enum ObjcConstraint { kObjcNone, kObjcRetainRelease, kObjcRetainReleaseOrGC, kObjcGC };
- enum CpuConstraint { kCpuAny = 0 };
-
- class DylibHander
- {
- public:
- virtual ~DylibHander() {}
- virtual Reader* findDylib(const char* installPath, const char* fromPath) = 0;
- };
-
-
- static Reader* createReader(const char* path, const ReaderOptions& options);
-
- virtual const char* getPath() = 0;
- virtual time_t getModificationTime() = 0;
- virtual DebugInfoKind getDebugInfoKind() = 0;
- virtual std::vector<class Atom*>& getAtoms() = 0;
- virtual std::vector<class Atom*>* getJustInTimeAtomsFor(const char* name) = 0;
- virtual std::vector<Stab>* getStabs() = 0;
- virtual ObjcConstraint getObjCConstraint() { return kObjcNone; }
- virtual uint32_t updateCpuConstraint(uint32_t current) { return current; }
- virtual bool objcReplacementClasses() { return false; }
-
- // For relocatable object files only
- virtual bool canScatterAtoms() { return true; }
- virtual void optimize(std::vector<ObjectFile::Atom*>&, std::vector<ObjectFile::Atom*>&,
- std::vector<const char*>&, uint32_t, ObjectFile::Reader* writer,
- bool allGlobalsAReDeadStripRoots, int okind,
- bool verbose, bool saveTemps, const char* outputFilePath,
- bool pie, bool allowTextRelocs) { }
- virtual bool hasLongBranchStubs() { return false; }
-
- // For Dynamic Libraries only
- virtual const char* getInstallPath() { return NULL; }
- virtual uint32_t getTimestamp() { return 0; }
- virtual uint32_t getCurrentVersion() { return 0; }
- virtual uint32_t getCompatibilityVersion() { return 0; }
- virtual void processIndirectLibraries(DylibHander* handler) { }
- virtual void setExplicitlyLinked() { }
- virtual bool explicitlyLinked() { return false; }
- virtual bool implicitlyLinked() { return false; }
- virtual bool providedExportAtom() { return false; }
- virtual const char* parentUmbrella() { return NULL; }
- virtual std::vector<const char*>* getAllowableClients() { return NULL; }
- virtual bool hasWeakExternals() { return false; }
- virtual bool isLazyLoadedDylib() { return false; }
-
-protected:
- Reader() {}
- virtual ~Reader() {}
-};
-
-class Segment
-{
-public:
- virtual const char* getName() const = 0;
- virtual bool isContentReadable() const = 0;
- virtual bool isContentWritable() const = 0;
- virtual bool isContentExecutable() const = 0;
-
- uint64_t getBaseAddress() const { return fBaseAddress; }
- void setBaseAddress(uint64_t addr) { fBaseAddress = addr; }
- virtual bool hasFixedAddress() const { return false; }
-
-protected:
- Segment() : fBaseAddress(0) {}
- virtual ~Segment() {}
- uint64_t fBaseAddress;
-};
-
-class Reference;
-
-class Section
-{
-public:
- unsigned int getIndex() { return fIndex; }
- uint64_t getBaseAddress() { return fBaseAddress; }
- void setBaseAddress(uint64_t addr) { fBaseAddress = addr; }
- void* fOther;
-
-protected:
- Section() : fOther(NULL), fBaseAddress(0), fIndex(0) {}
- uint64_t fBaseAddress;
- unsigned int fIndex;
-};
-
-
-struct Alignment
-{
- Alignment(int p2, int m=0) : powerOf2(p2), modulus(m) {}
- uint8_t trailingZeros() const { return (modulus==0) ? powerOf2 : __builtin_ctz(modulus); }
- uint16_t powerOf2;
- uint16_t modulus;
-};
-
-//
-// An atom is the fundamental unit of linking. A C function or global variable is an atom.
-// An atom has content and some attributes. The content of a function atom is the instructions
-// that implement the function. The content of a global variable atom is its initial bits.
-//
-// Name:
-// The name of an atom is the label name generated by the compiler. A C compiler names foo()
-// as _foo. A C++ compiler names foo() as __Z3foov.
-// The name refers to the first byte of the content. An atom cannot have multiple entry points.
-// Such code is modeled as multiple atoms, each having a "follow on" reference to the next.
-// A "follow on" reference is a contraint to the linker to the atoms must be laid out contiguously.
-//
-// Scope:
-// An atom is in one of three scopes: translation-unit, linkage-unit, or global. These correspond
-// to the C visibility of static, hidden, default.
-//
-// DefinitionKind:
-// An atom is one of five defintion kinds:
-// regular Most atoms.
-// weak C++ compiler makes some functions weak if there might be multiple copies
-// that the linker needs to coalesce.
-// tentative A straggler from ancient C when the extern did not exist. "int foo;" is ambiguous.
-// It could be a prototype or it could be a definition.
-// external This is a "proxy" atom produced by a dylib reader. It has no content. It exists
-// so that all References can be resolved.
-// external-weak Same as external, but the definition in the dylib is weak.
-//
-// SymbolTableInclusion:
-// An atom may or may not be in the symbol table in an object file.
-// in Most atoms for functions or global data
-// not-in Anonymous atoms such literal c-strings, or other compiler generated data
-// in-never-strip Atom whose name the strip tool should never remove (e.g. REFERENCED_DYNAMICALLY in mach-o)
-//
-// Ordinal:
-// When a reader is created it is given a base ordinal number. All atoms created by the reader
-// should return a contiguous range of ordinal values that start at the base ordinal. The ordinal
-// values are used by the linker to sort the atom graph when producing the output file.
-//
-class Atom
-{
-public:
- enum Scope { scopeTranslationUnit, scopeLinkageUnit, scopeGlobal };
- enum DefinitionKind { kRegularDefinition, kWeakDefinition, kTentativeDefinition, kExternalDefinition, kExternalWeakDefinition, kAbsoluteSymbol };
- enum SymbolTableInclusion { kSymbolTableNotIn, kSymbolTableIn, kSymbolTableInAndNeverStrip, kSymbolTableInAsAbsolute };
-
- virtual Reader* getFile() const = 0;
- virtual bool getTranslationUnitSource(const char** dir, const char** name) const = 0;
- virtual const char* getName() const = 0;
- virtual const char* getDisplayName() const = 0;
- virtual Scope getScope() const = 0;
- virtual DefinitionKind getDefinitionKind() const = 0;
- virtual SymbolTableInclusion getSymbolTableInclusion() const = 0;
- virtual bool dontDeadStrip() const = 0;
- virtual bool isZeroFill() const = 0;
- virtual bool isThumb() const = 0;
- virtual uint64_t getSize() const = 0;
- virtual std::vector<ObjectFile::Reference*>& getReferences() const = 0;
- virtual bool mustRemainInSection() const = 0;
- virtual const char* getSectionName() const = 0;
- virtual Segment& getSegment() const = 0;
- virtual Atom& getFollowOnAtom() const = 0;
- virtual uint32_t getOrdinal() const = 0;
- virtual std::vector<LineInfo>* getLineInfo() const = 0;
- virtual Alignment getAlignment() const = 0;
- virtual void copyRawContent(uint8_t buffer[]) const = 0;
- virtual void setScope(Scope) = 0;
-
-
- uint64_t getSectionOffset() const { return fSectionOffset; }
- uint64_t getAddress() const { return fSection->getBaseAddress() + fSectionOffset; }
- class Section* getSection() const { return fSection; }
-
- virtual void setSectionOffset(uint64_t offset) { fSectionOffset = offset; }
- virtual void setSection(class Section* sect) { fSection = sect; }
-
-protected:
- Atom() : fSectionOffset(0), fSection(NULL) {}
- virtual ~Atom() {}
-
- uint64_t fSectionOffset;
- class Section* fSection;
-};
-
-
-//
-// A Reference is a directed edge to another Atom. When an instruction in
-// the content of an Atom refers to another Atom, that is represented by a
-// Reference.
-//
-// There are two kinds of references: direct and by-name. With a direct Reference,
-// the target is bound by the Reader that created it. For instance a reference to a
-// static would produce a direct reference. A by-name reference requires the linker
-// to find the target Atom with the required name in order to be bound.
-//
-// For a link to succeed all References must be bound.
-//
-// A Reference has an optional "from" target. This is used when the content to fix-up
-// is the difference of two Atom address. For instance, if a pointer sized data Atom
-// is to contain A - B, then the Atom would have on Reference with a target of "A" and
-// a from-target of "B".
-//
-// A Reference also has a fix-up-offset. This is the offset into the content of the
-// Atom holding the reference where the fix-up (relocation) will be applied.
-//
-//
-//
-class Reference
-{
-public:
- enum TargetBinding { kUnboundByName, kBoundDirectly, kBoundByName, kDontBind };
-
- virtual TargetBinding getTargetBinding() const = 0;
- virtual TargetBinding getFromTargetBinding() const = 0;
- virtual uint8_t getKind() const = 0;
- virtual uint64_t getFixUpOffset() const = 0;
- virtual const char* getTargetName() const = 0;
- virtual Atom& getTarget() const = 0;
- virtual uint64_t getTargetOffset() const = 0;
- virtual Atom& getFromTarget() const = 0;
- virtual const char* getFromTargetName() const = 0;
- virtual uint64_t getFromTargetOffset() const = 0;
-
- virtual void setTarget(Atom&, uint64_t offset) = 0;
- virtual void setFromTarget(Atom&) = 0;
- virtual const char* getDescription() const = 0;
-
-protected:
- Reference() {}
- virtual ~Reference() {}
-};
-
-
-}; // namespace ObjectFile
-
-
-#endif // __OBJECTFILE__
+++ /dev/null
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
- *
- * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#ifndef __OPAQUE_SECTION__
-#define __OPAQUE_SECTION__
-
-
-#include <vector>
-
-#include "ObjectFile.h"
-
-namespace opaque_section {
-
-
-class Segment : public ObjectFile::Segment
-{
-public:
- Segment(const char* name) { fName = name; }
- virtual const char* getName() const { return fName; }
- virtual bool isContentReadable() const { return true; }
- virtual bool isContentWritable() const { return false; }
- virtual bool isContentExecutable() const { return (strcmp(fName, "__TEXT") == 0); }
-private:
- const char* fName;
-};
-
-
-class Reader : public ObjectFile::Reader
-{
-public:
- Reader(const char* segmentName, const char* sectionName, const char* path,
- const uint8_t fileContent[], uint64_t fileLength, uint32_t ordinal, const char* symbolName=NULL);
- virtual ~Reader();
-
- void addSectionReference(uint8_t kind, uint64_t offsetInSection, const ObjectFile::Atom* targetAtom,
- uint64_t offsetInTarget, const ObjectFile::Atom* fromTargetAtom=NULL, uint64_t offsetInFromTarget=0);
-
- virtual const char* getPath() { return fPath; }
- virtual time_t getModificationTime() { return 0; }
- virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; }
- virtual std::vector<class ObjectFile::Atom*>& getAtoms() { return fAtoms; }
- virtual std::vector<class ObjectFile::Atom*>* getJustInTimeAtomsFor(const char* name) { return NULL; }
- virtual std::vector<Stab>* getStabs() { return NULL; }
-
-private:
- const char* fPath;
- std::vector<class ObjectFile::Atom*> fAtoms;
-};
-
-class Reference : public ObjectFile::Reference
-{
-public:
- Reference(uint8_t kind, uint64_t fixupOffset, const ObjectFile::Atom* target, uint64_t targetOffset,
- const ObjectFile::Atom* fromTarget=NULL, uint64_t fromTargetOffset=0)
- : fFixUpOffset(fixupOffset), fTarget(target), fTargetOffset(targetOffset), fKind(kind),
- fFromTarget(fromTarget), fFromTargetOffset(fromTargetOffset) {}
- virtual ~Reference() {}
-
-
- virtual ObjectFile::Reference::TargetBinding getTargetBinding() const { return ObjectFile::Reference::kBoundDirectly; }
- virtual ObjectFile::Reference::TargetBinding getFromTargetBinding() const{ return ObjectFile::Reference::kDontBind; }
- virtual uint8_t getKind() const { return fKind; }
- virtual uint64_t getFixUpOffset() const { return fFixUpOffset; }
- virtual const char* getTargetName() const { return fTarget->getName(); }
- virtual ObjectFile::Atom& getTarget() const { return *((ObjectFile::Atom*)fTarget); }
- virtual uint64_t getTargetOffset() const { return fTargetOffset; }
- virtual ObjectFile::Atom& getFromTarget() const { return *((ObjectFile::Atom*)fFromTarget); }
- virtual const char* getFromTargetName() const { return fFromTarget->getName(); }
- virtual uint64_t getFromTargetOffset() const { return fFromTargetOffset; }
- virtual void setTarget(ObjectFile::Atom&, uint64_t offset) { throw "can't set target"; }
- virtual void setFromTarget(ObjectFile::Atom&) { throw "can't set from target"; }
- virtual const char* getDescription() const { return "opaque section reference"; }
-
-private:
- uint64_t fFixUpOffset;
- const ObjectFile::Atom* fTarget;
- uint64_t fTargetOffset;
- uint8_t fKind;
- const ObjectFile::Atom* fFromTarget;
- uint64_t fFromTargetOffset;
-};
-
-
-class Atom : public ObjectFile::Atom {
-public:
- virtual ObjectFile::Reader* getFile() const { return &fOwner; }
- virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; }
- virtual const char* getName() const { return fName; }
- virtual const char* getDisplayName() const;
- virtual Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; }
- 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 bool isThumb() const { return false; }
- virtual uint64_t getSize() const { return fFileLength; }
- virtual std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(fReferences); }
- virtual bool mustRemainInSection() const { return false; }
- virtual const char* getSectionName() const { return fSectionName; }
- virtual Segment& getSegment() const { return fSegment; }
- virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); }
- virtual uint32_t getOrdinal() const { return fOrdinal; }
- virtual std::vector<ObjectFile::LineInfo>* getLineInfo() const { return NULL; }
- virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(4); }
- virtual void copyRawContent(uint8_t buffer[]) const;
-
- virtual void setScope(Scope) { }
-
-protected:
- friend class Reader;
-
- Atom(Reader& owner, Segment& segment, const char* sectionName,
- const uint8_t fileContent[], uint64_t fileLength, uint32_t ordinal, const char* symbolName);
- virtual ~Atom() {}
-
- Reader& fOwner;
- Segment& fSegment;
- const char* fName;
- const char* fSectionName;
- const uint8_t* fFileContent;
- uint32_t fOrdinal;
- uint64_t fFileLength;
- std::vector<ObjectFile::Reference*> fReferences;
-};
-
-
-
-Atom::Atom(Reader& owner, Segment& segment, const char* sectionName, const uint8_t fileContent[], uint64_t fileLength, uint32_t ordinal, const char* symbolName)
- : fOwner(owner), fSegment(segment), fSectionName(sectionName), fFileContent(fileContent), fOrdinal(ordinal), fFileLength(fileLength)
-{
- if ( symbolName != NULL )
- fName = strdup(symbolName);
- else
- asprintf((char**)&fName, "__section$%s%s", segment.getName(), sectionName);
-}
-
-
-Reader::Reader(const char* segmentName, const char* sectionName, const char* path, const uint8_t fileContent[],
- uint64_t fileLength, uint32_t ordinal, const char* symbolName)
- : fPath(path)
-{
- fAtoms.push_back(new Atom(*this, *(new Segment(segmentName)), strdup(sectionName), fileContent, fileLength, ordinal, symbolName));
-}
-
-Reader::~Reader()
-{
-}
-
-void Reader::addSectionReference(uint8_t kind, uint64_t offsetInSection, const ObjectFile::Atom* targetAtom,
- uint64_t offsetInTarget, const ObjectFile::Atom* fromTargetAtom, uint64_t offsetInFromTarget)
-{
- fAtoms[0]->getReferences().push_back(new Reference(kind, offsetInSection, targetAtom, offsetInTarget, fromTargetAtom, offsetInFromTarget));
-}
-
-
-const char* Atom::getDisplayName() const
-{
- static char name[64];
- sprintf(name, "opaque section %s %s", fSegment.getName(), fSectionName);
- return name;
-}
-
-
-void Atom::copyRawContent(uint8_t buffer[]) const
-{
- memcpy(buffer, fFileContent, fFileLength);
-}
-
-
-
-};
-
-
-
-#endif // __OPAQUE_SECTION__
-
-
-
+++ /dev/null
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
- *
- * Copyright (c) 2005-2008 Apple 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 <mach/vm_prot.h>
-#include <fcntl.h>
-#include <vector>
-
-#include "configure.h"
-#include "Options.h"
-#include "Architectures.hpp"
-#include "MachOFileAbstraction.hpp"
-
-extern void printLTOVersion(Options &opts);
-
-
-static bool sEmitWarnings = true;
-static const char* sWarningsSideFilePath = NULL;
-static FILE* sWarningsSideFile = NULL;
-
-void warning(const char* format, ...)
-{
- if ( sEmitWarnings ) {
- va_list list;
- if ( sWarningsSideFilePath != NULL ) {
- if ( sWarningsSideFile == NULL )
- sWarningsSideFile = fopen(sWarningsSideFilePath, "a");
- }
- va_start(list, format);
- fprintf(stderr, "ld warning: ");
- vfprintf(stderr, format, list);
- fprintf(stderr, "\n");
- if ( sWarningsSideFile != NULL ) {
- fprintf(sWarningsSideFile, "ld warning: ");
- vfprintf(sWarningsSideFile, format, list);
- fprintf(sWarningsSideFile, "\n");
- fflush(sWarningsSideFile);
- }
- va_end(list);
- }
-}
-
-void throwf(const char* format, ...)
-{
- va_list list;
- char* p;
- va_start(list, format);
- vasprintf(&p, format, list);
- va_end(list);
-
- const char* t = p;
- throw t;
-}
-
-Options::Options(int argc, const char* argv[])
- : fOutputFile("a.out"), fArchitecture(0), fSubArchitecture(0), fOutputKind(kDynamicExecutable),
- fHasPreferredSubType(false), fPrebind(false), fBindAtLoad(false), fKeepPrivateExterns(false),
- fNeedsModuleTable(false), fIgnoreOtherArchFiles(false), fForceSubtypeAll(false),
- fInterposeMode(kInterposeNone), fDeadStrip(kDeadStripOff), fNameSpace(kTwoLevelNameSpace),
- fDylibCompatVersion(0), fDylibCurrentVersion(0), fDylibInstallName(NULL), fFinalName(NULL), fEntryName("start"), fBaseAddress(0),
- fBaseWritableAddress(0), fSplitSegs(false),
- fExportMode(kExportDefault), fLibrarySearchMode(kSearchAllDirsForDylibsThenAllDirsForArchives),
- fUndefinedTreatment(kUndefinedError), fMessagesPrefixedWithArchitecture(false),
- fWeakReferenceMismatchTreatment(kWeakReferenceMismatchNonWeak),
- fClientName(NULL),
- fUmbrellaName(NULL), fInitFunctionName(NULL), fDotOutputFile(NULL), fExecutablePath(NULL),
- fBundleLoader(NULL), fDtraceScriptName(NULL), fSegAddrTablePath(NULL), fMapPath(NULL),
- fZeroPageSize(ULLONG_MAX), fStackSize(0), fStackAddr(0), fExecutableStack(false), fMinimumHeaderPad(32),
- fCommonsMode(kCommonsIgnoreDylibs), fUUIDMode(kUUIDContent), fLocalSymbolHandling(kLocalSymbolsAll), fWarnCommons(false),
- fVerbose(false), fKeepRelocations(false), fWarnStabs(false),
- fTraceDylibSearching(false), fPause(false), fStatistics(false), fPrintOptions(false),
- fSharedRegionEligible(false), fPrintOrderFileStatistics(false),
- fReadOnlyx86Stubs(false), fPositionIndependentExecutable(false), fMaxMinimumHeaderPad(false),
- fDeadStripDylibs(false), fAllowTextRelocs(false), fWarnTextRelocs(false),
- fUsingLazyDylibLinking(false), fEncryptable(true), fSaveTempFiles(false)
-{
- this->checkForClassic(argc, argv);
- this->parsePreCommandLineEnvironmentSettings();
- this->parse(argc, argv);
- this->parsePostCommandLineEnvironmentSettings();
- this->reconfigureDefaults();
- this->checkIllegalOptionCombinations();
-}
-
-Options::~Options()
-{
-}
-
-const ObjectFile::ReaderOptions& Options::readerOptions()
-{
- return fReaderOptions;
-}
-
-
-const char* Options::getOutputFilePath()
-{
- return fOutputFile;
-}
-
-std::vector<Options::FileInfo>& Options::getInputFiles()
-{
- return fInputFiles;
-}
-
-Options::OutputKind Options::outputKind()
-{
- return fOutputKind;
-}
-
-bool Options::bindAtLoad()
-{
- return fBindAtLoad;
-}
-
-bool Options::prebind()
-{
- return fPrebind;
-}
-
-bool Options::fullyLoadArchives()
-{
- return fReaderOptions.fFullyLoadArchives;
-}
-
-Options::NameSpace Options::nameSpace()
-{
- return fNameSpace;
-}
-
-const char* Options::installPath()
-{
- if ( fDylibInstallName != NULL )
- return fDylibInstallName;
- else if ( fFinalName != NULL )
- return fFinalName;
- else
- return fOutputFile;
-}
-
-uint32_t Options::currentVersion()
-{
- return fDylibCurrentVersion;
-}
-
-uint32_t Options::compatibilityVersion()
-{
- return fDylibCompatVersion;
-}
-
-const char* Options::entryName()
-{
- return fEntryName;
-}
-
-uint64_t Options::baseAddress()
-{
- return fBaseAddress;
-}
-
-bool Options::keepPrivateExterns()
-{
- return fKeepPrivateExterns;
-}
-
-bool Options::interposable(const char* name)
-{
- switch ( fInterposeMode ) {
- case kInterposeNone:
- return false;
- case kInterposeAllExternal:
- return true;
- case kInterposeSome:
- return fInterposeList.contains(name);
- }
- throw "internal error";
-}
-
-bool Options::needsModuleTable()
-{
- return fNeedsModuleTable;
-}
-
-bool Options::ignoreOtherArchInputFiles()
-{
- return fIgnoreOtherArchFiles;
-}
-
-bool Options::forceCpuSubtypeAll()
-{
- return fForceSubtypeAll;
-}
-
-bool Options::traceDylibs()
-{
- return fReaderOptions.fTraceDylibs;
-}
-
-bool Options::traceArchives()
-{
- return fReaderOptions.fTraceArchives;
-}
-
-Options::UndefinedTreatment Options::undefinedTreatment()
-{
- return fUndefinedTreatment;
-}
-
-ObjectFile::ReaderOptions::VersionMin Options::macosxVersionMin()
-{
- return fReaderOptions.fVersionMin;
-}
-
-Options::WeakReferenceMismatchTreatment Options::weakReferenceMismatchTreatment()
-{
- return fWeakReferenceMismatchTreatment;
-}
-
-const char* Options::umbrellaName()
-{
- return fUmbrellaName;
-}
-
-std::vector<const char*>& Options::allowableClients()
-{
- return fAllowableClients;
-}
-
-const char* Options::clientName()
-{
- return fClientName;
-}
-
-uint64_t Options::zeroPageSize()
-{
- return fZeroPageSize;
-}
-
-bool Options::hasCustomStack()
-{
- return (fStackSize != 0);
-}
-
-uint64_t Options::customStackSize()
-{
- return fStackSize;
-}
-
-uint64_t Options::customStackAddr()
-{
- return fStackAddr;
-}
-
-bool Options::hasExecutableStack()
-{
- return fExecutableStack;
-}
-
-std::vector<const char*>& Options::initialUndefines()
-{
- return fInitialUndefines;
-}
-
-bool Options::printWhyLive(const char* symbolName)
-{
- return ( fWhyLive.find(symbolName) != fWhyLive.end() );
-}
-
-
-const char* Options::initFunctionName()
-{
- return fInitFunctionName;
-}
-
-const char* Options::dotOutputFile()
-{
- return fDotOutputFile;
-}
-
-bool Options::hasExportRestrictList()
-{
- return (fExportMode != kExportDefault);
-}
-
-bool Options::hasExportMaskList()
-{
- return (fExportMode == kExportSome);
-}
-
-
-bool Options::hasWildCardExportRestrictList()
-{
- // has -exported_symbols_list which contains some wildcards
- return ((fExportMode == kExportSome) && fExportSymbols.hasWildCards());
-}
-
-
-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;
-}
-
-std::vector<Options::ExtraSection>& Options::extraSections()
-{
- return fExtraSections;
-}
-
-std::vector<Options::SectionAlignment>& Options::sectionAlignments()
-{
- return fSectionAlignments;
-}
-
-Options::CommonsMode Options::commonsMode()
-{
- return fCommonsMode;
-}
-
-bool Options::warnCommons()
-{
- return fWarnCommons;
-}
-
-bool Options::keepRelocations()
-{
- return fKeepRelocations;
-}
-
-bool Options::warnStabs()
-{
- return fWarnStabs;
-}
-
-const char* Options::executablePath()
-{
- return fExecutablePath;
-}
-
-Options::DeadStripMode Options::deadStrip()
-{
- return fDeadStrip;
-}
-
-bool Options::shouldExport(const char* symbolName)
-{
- switch (fExportMode) {
- case kExportSome:
- return fExportSymbols.contains(symbolName);
- case kDontExportSome:
- return ! fDontExportSymbols.contains(symbolName);
- case kExportDefault:
- return true;
- }
- throw "internal error";
-}
-
-bool Options::keepLocalSymbol(const char* symbolName)
-{
- switch (fLocalSymbolHandling) {
- case kLocalSymbolsAll:
- return true;
- case kLocalSymbolsNone:
- return false;
- case kLocalSymbolsSelectiveInclude:
- return fLocalSymbolsIncluded.contains(symbolName);
- case kLocalSymbolsSelectiveExclude:
- return ! fLocalSymbolsExcluded.contains(symbolName);
- }
- throw "internal error";
-}
-
-void Options::parseArch(const char* architecture)
-{
- if ( architecture == NULL )
- throw "-arch must be followed by an architecture string";
- if ( strcmp(architecture, "ppc") == 0 ) {
- fArchitecture = CPU_TYPE_POWERPC;
- fSubArchitecture = CPU_SUBTYPE_POWERPC_ALL;
- }
- else if ( strcmp(architecture, "ppc64") == 0 ) {
- fArchitecture = CPU_TYPE_POWERPC64;
- fSubArchitecture = CPU_SUBTYPE_POWERPC_ALL;
- }
- else if ( strcmp(architecture, "i386") == 0 ) {
- fArchitecture = CPU_TYPE_I386;
- fSubArchitecture = CPU_SUBTYPE_I386_ALL;
- }
- else if ( strcmp(architecture, "x86_64") == 0 ) {
- fArchitecture = CPU_TYPE_X86_64;
- fSubArchitecture = CPU_SUBTYPE_X86_64_ALL;
- }
- else if ( strcmp(architecture, "arm") == 0 ) {
- fArchitecture = CPU_TYPE_ARM;
- fSubArchitecture = CPU_SUBTYPE_ARM_ALL;
- }
- // compatibility support for cpu-sub-types
- else if ( strcmp(architecture, "ppc750") == 0 ) {
- fArchitecture = CPU_TYPE_POWERPC;
- fSubArchitecture = CPU_SUBTYPE_POWERPC_750;
- fHasPreferredSubType = true;
- }
- else if ( strcmp(architecture, "ppc7400") == 0 ) {
- fArchitecture = CPU_TYPE_POWERPC;
- fSubArchitecture = CPU_SUBTYPE_POWERPC_7400;
- fHasPreferredSubType = true;
- }
- else if ( strcmp(architecture, "ppc7450") == 0 ) {
- fArchitecture = CPU_TYPE_POWERPC;
- fSubArchitecture = CPU_SUBTYPE_POWERPC_7450;
- fHasPreferredSubType = true;
- }
- else if ( strcmp(architecture, "ppc970") == 0 ) {
- fArchitecture = CPU_TYPE_POWERPC;
- fSubArchitecture = CPU_SUBTYPE_POWERPC_970;
- fHasPreferredSubType = true;
- }
- else if ( strcmp(architecture, "armv6") == 0 ) {
- fArchitecture = CPU_TYPE_ARM;
- fSubArchitecture = CPU_SUBTYPE_ARM_V6;
- fHasPreferredSubType = true;
- }
- else if ( strcmp(architecture, "armv5") == 0 ) {
- fArchitecture = CPU_TYPE_ARM;
- fSubArchitecture = CPU_SUBTYPE_ARM_V5TEJ;
- fHasPreferredSubType = true;
- }
- else if ( strcmp(architecture, "armv4t") == 0 ) {
- fArchitecture = CPU_TYPE_ARM;
- fSubArchitecture = CPU_SUBTYPE_ARM_V4T;
- fHasPreferredSubType = true;
- }
- else if ( strcmp(architecture, "xscale") == 0 ) {
- fArchitecture = CPU_TYPE_ARM;
- fSubArchitecture = CPU_SUBTYPE_ARM_XSCALE;
- fHasPreferredSubType = true;
- }
- else if ( strcmp(architecture, "armv7") == 0 ) {
- fArchitecture = CPU_TYPE_ARM;
- fSubArchitecture = CPU_SUBTYPE_ARM_V7;
- fHasPreferredSubType = true;
- }
- else
- throwf("unknown/unsupported architecture name for: -arch %s", architecture);
-}
-
-bool Options::checkForFile(const char* format, const char* dir, const char* rootName, FileInfo& result)
-{
- struct stat statBuffer;
- char possiblePath[strlen(dir)+strlen(rootName)+strlen(format)+8];
- sprintf(possiblePath, format, dir, rootName);
- bool found = (stat(possiblePath, &statBuffer) == 0);
- if ( fTraceDylibSearching )
- printf("[Logging for XBS]%sfound library: '%s'\n", (found ? " " : " not "), possiblePath);
- if ( found ) {
- result.path = strdup(possiblePath);
- result.fileLen = statBuffer.st_size;
- result.modTime = statBuffer.st_mtime;
- return true;
- }
- return false;
-}
-
-
-Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly)
-{
- FileInfo result;
- const int rootNameLen = strlen(rootName);
- // if rootName ends in .o there is no .a vs .dylib choice
- if ( (rootNameLen > 3) && (strcmp(&rootName[rootNameLen-2], ".o") == 0) ) {
- for (std::vector<const char*>::iterator it = fLibrarySearchPaths.begin();
- it != fLibrarySearchPaths.end();
- it++) {
- const char* dir = *it;
- if ( checkForFile("%s/%s", dir, rootName, result) )
- return result;
- }
- }
- else {
- bool lookForDylibs = ( fOutputKind != Options::kDyld);
- switch ( fLibrarySearchMode ) {
- case kSearchAllDirsForDylibsThenAllDirsForArchives:
- // first look in all directories for just for dylibs
- if ( lookForDylibs ) {
- for (std::vector<const char*>::iterator it = fLibrarySearchPaths.begin();
- it != fLibrarySearchPaths.end();
- it++) {
- const char* dir = *it;
- if ( checkForFile("%s/lib%s.dylib", dir, rootName, result) )
- return result;
- }
- for (std::vector<const char*>::iterator it = fLibrarySearchPaths.begin();
- it != fLibrarySearchPaths.end();
- it++) {
- const char* dir = *it;
- if ( checkForFile("%s/lib%s.so", dir, rootName, result) )
- return result;
- }
- }
- // next look in all directories for just for archives
- if ( !dylibsOnly ) {
- for (std::vector<const char*>::iterator it = fLibrarySearchPaths.begin();
- it != fLibrarySearchPaths.end();
- it++) {
- const char* dir = *it;
- if ( checkForFile("%s/lib%s.a", dir, rootName, result) )
- return result;
- }
- }
- break;
-
- case kSearchDylibAndArchiveInEachDir:
- // look in each directory for just for a dylib then for an archive
- for (std::vector<const char*>::iterator it = fLibrarySearchPaths.begin();
- it != fLibrarySearchPaths.end();
- it++) {
- const char* dir = *it;
- if ( lookForDylibs && checkForFile("%s/lib%s.dylib", dir, rootName, result) )
- return result;
- if ( lookForDylibs && checkForFile("%s/lib%s.so", dir, rootName, result) )
- return result;
- if ( !dylibsOnly && checkForFile("%s/lib%s.a", dir, rootName, result) )
- return result;
- }
- break;
- }
- }
- throwf("library not found for -l%s", rootName);
-}
-
-Options::FileInfo Options::findFramework(const char* frameworkName)
-{
- if ( frameworkName == NULL )
- throw "-framework missing next argument";
- char temp[strlen(frameworkName)+1];
- strcpy(temp, frameworkName);
- const char* name = temp;
- const char* suffix = NULL;
- char* comma = strchr(temp, ',');
- if ( comma != NULL ) {
- *comma = '\0';
- suffix = &comma[1];
- }
- return findFramework(name, suffix);
-}
-
-Options::FileInfo Options::findFramework(const char* rootName, const char* suffix)
-{
- struct stat statBuffer;
- for (std::vector<const char*>::iterator it = fFrameworkSearchPaths.begin();
- it != fFrameworkSearchPaths.end();
- it++) {
- // ??? Shouldn't we be using String here and just initializing it?
- // ??? Use str.c_str () to pull out the string for the stat call.
- const char* dir = *it;
- char possiblePath[PATH_MAX];
- strcpy(possiblePath, dir);
- strcat(possiblePath, "/");
- strcat(possiblePath, rootName);
- strcat(possiblePath, ".framework/");
- strcat(possiblePath, rootName);
- if ( suffix != NULL ) {
- char realPath[PATH_MAX];
- // no symlink in framework to suffix variants, so follow main symlink
- if ( realpath(possiblePath, realPath) != NULL ) {
- strcpy(possiblePath, realPath);
- strcat(possiblePath, suffix);
- }
- }
- bool found = (stat(possiblePath, &statBuffer) == 0);
- if ( fTraceDylibSearching )
- printf("[Logging for XBS]%sfound framework: '%s'\n",
- (found ? " " : " not "), possiblePath);
- if ( found ) {
- FileInfo result;
- result.path = strdup(possiblePath);
- result.fileLen = statBuffer.st_size;
- result.modTime = statBuffer.st_mtime;
- return result;
- }
- }
- // try without suffix
- if ( suffix != NULL )
- return findFramework(rootName, NULL);
- else
- throwf("framework not found %s", rootName);
-}
-
-Options::FileInfo Options::findFile(const char* path)
-{
- FileInfo result;
- struct stat statBuffer;
-
- // if absolute path and not a .o file, the use SDK prefix
- if ( (path[0] == '/') && (strcmp(&path[strlen(path)-2], ".o") != 0) ) {
- const int pathLen = strlen(path);
- for (std::vector<const char*>::iterator it = fSDKPaths.begin(); it != fSDKPaths.end(); it++) {
- // ??? Shouldn't we be using String here?
- const char* sdkPathDir = *it;
- const int sdkPathDirLen = strlen(sdkPathDir);
- char possiblePath[sdkPathDirLen+pathLen+4];
- strcpy(possiblePath, sdkPathDir);
- if ( possiblePath[sdkPathDirLen-1] == '/' )
- possiblePath[sdkPathDirLen-1] = '\0';
- strcat(possiblePath, path);
- if ( stat(possiblePath, &statBuffer) == 0 ) {
- result.path = strdup(possiblePath);
- result.fileLen = statBuffer.st_size;
- result.modTime = statBuffer.st_mtime;
- return result;
- }
- }
- }
- // try raw path
- if ( stat(path, &statBuffer) == 0 ) {
- result.path = strdup(path);
- result.fileLen = statBuffer.st_size;
- result.modTime = statBuffer.st_mtime;
- return result;
- }
-
- // try @executable_path substitution
- if ( (strncmp(path, "@executable_path/", 17) == 0) && (fExecutablePath != NULL) ) {
- char newPath[strlen(fExecutablePath) + strlen(path)];
- strcpy(newPath, fExecutablePath);
- char* addPoint = strrchr(newPath,'/');
- if ( addPoint != NULL )
- strcpy(&addPoint[1], &path[17]);
- else
- strcpy(newPath, &path[17]);
- if ( stat(newPath, &statBuffer) == 0 ) {
- result.path = strdup(newPath);
- result.fileLen = statBuffer.st_size;
- result.modTime = statBuffer.st_mtime;
- return result;
- }
- }
-
- // not found
- throwf("file not found: %s", path);
-}
-
-Options::FileInfo Options::findFileUsingPaths(const char* path)
-{
- FileInfo result;
-
- const char* lastSlash = strrchr(path, '/');
- const char* leafName = (lastSlash == NULL) ? path : &lastSlash[1];
-
- // Is this in a framework?
- // /path/Foo.framework/Foo ==> true (Foo)
- // /path/Foo.framework/Frameworks/Bar.framework/Bar ==> true (Bar)
- // /path/Foo.framework/Resources/Bar ==> false
- bool isFramework = false;
- if ( lastSlash != NULL ) {
- char frameworkDir[strlen(leafName) + 20];
- strcpy(frameworkDir, "/");
- strcat(frameworkDir, leafName);
- strcat(frameworkDir, ".framework/");
- if ( strstr(path, frameworkDir) != NULL )
- isFramework = true;
- }
-
- // These are abbreviated versions of the routines findFramework and findLibrary above
- // because we already know the final name of the file that we're looking for and so
- // don't need to try variations, just paths. We do need to add the additional bits
- // onto the framework path though.
- if ( isFramework ) {
- for (std::vector<const char*>::iterator it = fFrameworkSearchPaths.begin();
- it != fFrameworkSearchPaths.end();
- it++) {
- const char* dir = *it;
- char possiblePath[PATH_MAX];
- strcpy(possiblePath, dir);
- strcat(possiblePath, "/");
- strcat(possiblePath, leafName);
- strcat(possiblePath, ".framework");
-
- //fprintf(stderr,"Finding Framework: %s/%s, leafName=%s\n", possiblePath, leafName, leafName);
- if ( checkForFile("%s/%s", possiblePath, leafName, result) )
- return result;
- }
- }
- else {
- // if this is a .dylib inside a framework, do not search -L paths
- // <rdar://problem/5427952> ld64's re-export cycle detection logic prevents use of X11 libGL on Leopard
- int leafLen = strlen(leafName);
- bool embeddedDylib = ( (leafLen > 6)
- && (strcmp(&leafName[leafLen-6], ".dylib") == 0)
- && (strstr(path, ".framework/") != NULL) );
- if ( !embeddedDylib ) {
- for (std::vector<const char*>::iterator it = fLibrarySearchPaths.begin();
- it != fLibrarySearchPaths.end();
- it++) {
- const char* dir = *it;
- //fprintf(stderr,"Finding Library: %s/%s\n", dir, leafName);
- if ( checkForFile("%s/%s", dir, leafName, result) )
- return result;
- }
- }
- }
-
- // If we didn't find it fall back to findFile.
- return findFile(path);
-}
-
-
-void Options::parseSegAddrTable(const char* segAddrPath, const char* installPath)
-{
- FILE* file = fopen(segAddrPath, "r");
- if ( file == NULL ) {
- warning("-seg_addr_table file cannot be read: %s", segAddrPath);
- return;
- }
-
- char path[PATH_MAX];
- uint64_t firstColumAddress = 0;
- uint64_t secondColumAddress = 0;
- bool hasSecondColumn = false;
- while ( fgets(path, PATH_MAX, file) != NULL ) {
- path[PATH_MAX-1] = '\0';
- char* eol = strchr(path, '\n');
- if ( eol != NULL )
- *eol = '\0';
- // ignore lines not starting with 0x number
- if ( (path[0] == '0') && (path[1] == 'x') ) {
- char* p;
- firstColumAddress = strtoull(path, &p, 16);
- while ( isspace(*p) )
- ++p;
- // see if second column is a number
- if ( (p[0] == '0') && (p[1] == 'x') ) {
- secondColumAddress = strtoull(p, &p, 16);
- hasSecondColumn = true;
- while ( isspace(*p) )
- ++p;
- }
- while ( isspace(*p) )
- ++p;
- if ( p[0] == '/' ) {
- // remove any trailing whitespace
- for(char* end = eol-1; (end > p) && isspace(*end); --end)
- *end = '\0';
- // see if this line is for the dylib being linked
- if ( strcmp(p, installPath) == 0 ) {
- fBaseAddress = firstColumAddress;
- if ( hasSecondColumn ) {
- fBaseWritableAddress = secondColumAddress;
- fSplitSegs = true;
- }
- break; // out of while loop
- }
- }
- }
- }
-
- fclose(file);
-}
-
-void Options::loadFileList(const char* 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[PATH_MAX];
- while ( fgets(path, PATH_MAX, file) != NULL ) {
- path[PATH_MAX-1] = '\0';
- char* eol = strchr(path, '\n');
- if ( eol != NULL )
- *eol = '\0';
- 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);
-}
-
-bool Options::SetWithWildcards::hasWildCards(const char* symbol)
-{
- // an exported symbol name containing *, ?, or [ requires wildcard matching
- return ( strpbrk(symbol, "*?[") != NULL );
-}
-
-void Options::SetWithWildcards::insert(const char* symbol)
-{
- if ( hasWildCards(symbol) )
- fWildCard.push_back(symbol);
- else
- fRegular.insert(symbol);
-}
-
-bool Options::SetWithWildcards::contains(const char* symbol)
-{
- // first look at hash table on non-wildcard symbols
- if ( fRegular.find(symbol) != fRegular.end() )
- return true;
- // next walk list of wild card symbols looking for a match
- for(std::vector<const char*>::iterator it = fWildCard.begin(); it != fWildCard.end(); ++it) {
- if ( wildCardMatch(*it, symbol) )
- return true;
- }
- return false;
-}
-
-
-bool Options::SetWithWildcards::inCharRange(const char*& p, unsigned char c)
-{
- ++p; // find end
- const char* b = p;
- while ( *p != '\0' ) {
- if ( *p == ']') {
- const char* e = p;
- // found beginining [ and ending ]
- unsigned char last = '\0';
- for ( const char* s = b; s < e; ++s ) {
- if ( *s == '-' ) {
- unsigned char next = *(++s);
- if ( (last <= c) && (c <= next) )
- return true;
- ++s;
- }
- else {
- if ( *s == c )
- return true;
- last = *s;
- }
- }
- return false;
- }
- ++p;
- }
- return false;
-}
-
-bool Options::SetWithWildcards::wildCardMatch(const char* pattern, const char* symbol)
-{
- const char* s = symbol;
- for (const char* p = pattern; *p != '\0'; ++p) {
- switch ( *p ) {
- case '*':
- if ( p[1] == '\0' )
- return true;
- for (const char* t = s; *t != '\0'; ++t) {
- if ( wildCardMatch(&p[1], t) )
- return true;
- }
- return false;
- case '?':
- if ( *s == '\0' )
- return false;
- ++s;
- break;
- case '[':
- if ( ! inCharRange(p, *s) )
- return false;
- ++s;
- break;
- default:
- if ( *s != *p )
- return false;
- ++s;
- }
- }
- return (*s == '\0');
-}
-
-
-void Options::loadExportFile(const char* fileOfExports, const char* option, SetWithWildcards& set)
-{
- // read in whole file
- int fd = ::open(fileOfExports, O_RDONLY, 0);
- if ( fd == -1 )
- throwf("can't open %s file: %s", option, fileOfExports);
- struct stat stat_buf;
- ::fstat(fd, &stat_buf);
- char* p = (char*)malloc(stat_buf.st_size);
- if ( p == NULL )
- throwf("can't process %s file: %s", option, fileOfExports);
-
- if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size )
- throwf("can't read %s file: %s", option, fileOfExports);
-
- ::close(fd);
-
- // parse into symbols and add to hash_set
- char * const end = &p[stat_buf.st_size];
- enum { lineStart, inSymbol, inComment } state = lineStart;
- char* symbolStart = NULL;
- for (char* s = p; s < end; ++s ) {
- switch ( state ) {
- case lineStart:
- if ( *s =='#' ) {
- state = inComment;
- }
- else if ( !isspace(*s) ) {
- state = inSymbol;
- symbolStart = s;
- }
- break;
- case inSymbol:
- if ( (*s == '\n') || (*s == '\r') ) {
- *s = '\0';
- // removing any trailing spaces
- char* last = s-1;
- while ( isspace(*last) ) {
- *last = '\0';
- --last;
- }
- set.insert(symbolStart);
- symbolStart = NULL;
- state = lineStart;
- }
- break;
- case inComment:
- if ( (*s == '\n') || (*s == '\r') )
- state = lineStart;
- break;
- }
- }
- if ( state == inSymbol ) {
- warning("missing line-end at end of file \"%s\"", fileOfExports);
- int len = end-symbolStart+1;
- char* temp = new char[len];
- strlcpy(temp, symbolStart, len);
-
- // remove any trailing spaces
- char* last = &temp[len-2];
- while ( isspace(*last) ) {
- *last = '\0';
- --last;
- }
- set.insert(temp);
- }
-
- // Note: we do not free() the malloc buffer, because the strings are used by the export-set hash table
-}
-
-void Options::parseAliasFile(const char* fileOfAliases)
-{
- // read in whole file
- int fd = ::open(fileOfAliases, O_RDONLY, 0);
- if ( fd == -1 )
- throwf("can't open alias file: %s", fileOfAliases);
- struct stat stat_buf;
- ::fstat(fd, &stat_buf);
- char* p = (char*)malloc(stat_buf.st_size+1);
- if ( p == NULL )
- throwf("can't process alias file: %s", fileOfAliases);
-
- if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size )
- throwf("can't read alias file: %s", fileOfAliases);
- p[stat_buf.st_size] = '\n';
- ::close(fd);
-
- // parse into symbols and add to fAliases
- ObjectFile::ReaderOptions::AliasPair pair;
- char * const end = &p[stat_buf.st_size+1];
- enum { lineStart, inRealName, inBetween, inAliasName, inComment } state = lineStart;
- int lineNumber = 1;
- for (char* s = p; s < end; ++s ) {
- switch ( state ) {
- case lineStart:
- if ( *s =='#' ) {
- state = inComment;
- }
- else if ( !isspace(*s) ) {
- state = inRealName;
- pair.realName = s;
- }
- break;
- case inRealName:
- if ( *s == '\n' ) {
- warning("line needs two symbols but has only one at line #%d in \"%s\"", lineNumber, fileOfAliases);
- ++lineNumber;
- state = lineStart;
- }
- else if ( isspace(*s) ) {
- *s = '\0';
- state = inBetween;
- }
- break;
- case inBetween:
- if ( *s == '\n' ) {
- warning("line needs two symbols but has only one at line #%d in \"%s\"", lineNumber, fileOfAliases);
- ++lineNumber;
- state = lineStart;
- }
- else if ( ! isspace(*s) ) {
- state = inAliasName;
- pair.alias = s;
- }
- break;
- case inAliasName:
- if ( *s =='#' ) {
- *s = '\0';
- // removing any trailing spaces
- char* last = s-1;
- while ( isspace(*last) ) {
- *last = '\0';
- --last;
- }
- fReaderOptions.fAliases.push_back(pair);
- state = inComment;
- }
- else if ( *s == '\n' ) {
- *s = '\0';
- // removing any trailing spaces
- char* last = s-1;
- while ( isspace(*last) ) {
- *last = '\0';
- --last;
- }
- fReaderOptions.fAliases.push_back(pair);
- state = lineStart;
- }
- break;
- case inComment:
- if ( *s == '\n' )
- state = lineStart;
- break;
- }
- }
-
- // Note: we do not free() the malloc buffer, because the strings therein are used by fAliases
-}
-
-
-
-void Options::setUndefinedTreatment(const char* treatment)
-{
- if ( treatment == NULL )
- throw "-undefined missing [ warning | error | suppress | dynamic_lookup ]";
-
- if ( strcmp(treatment, "warning") == 0 )
- fUndefinedTreatment = kUndefinedWarning;
- else if ( strcmp(treatment, "error") == 0 )
- fUndefinedTreatment = kUndefinedError;
- else if ( strcmp(treatment, "suppress") == 0 )
- fUndefinedTreatment = kUndefinedSuppress;
- else if ( strcmp(treatment, "dynamic_lookup") == 0 )
- fUndefinedTreatment = kUndefinedDynamicLookup;
- else
- throw "invalid option to -undefined [ warning | error | suppress | dynamic_lookup ]";
-}
-
-Options::Treatment Options::parseTreatment(const char* treatment)
-{
- if ( treatment == NULL )
- return kNULL;
-
- if ( strcmp(treatment, "warning") == 0 )
- return kWarning;
- else if ( strcmp(treatment, "error") == 0 )
- return kError;
- else if ( strcmp(treatment, "suppress") == 0 )
- return kSuppress;
- else
- return kInvalid;
-}
-
-void Options::setMacOSXVersionMin(const char* version)
-{
- if ( version == NULL )
- throw "-macosx_version_min argument missing";
-
- if ( (strncmp(version, "10.", 3) == 0) && isdigit(version[3]) ) {
- int num = version[3] - '0';
- switch ( num ) {
- case 0:
- case 1:
- fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_1;
- break;
- case 2:
- fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_2;
- break;
- case 3:
- fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_3;
- break;
- case 4:
- fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_4;
- break;
- case 5:
- fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_5;
- break;
- case 6:
- fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_6;
- break;
- default:
- fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_6;
- break;
- }
- }
- else {
- warning("unknown option to -macosx_version_min, not 10.x");
- }
-}
-
-void Options::setIPhoneVersionMin(const char* version)
-{
- if ( version == NULL )
- throw "-iphoneos_version_min argument missing";
-
- if ( ((strncmp(version, "1.", 2) == 0) || (strncmp(version, "2.", 2) == 0)) && isdigit(version[2]) ) {
- int num = version[2] - '0';
- switch ( num ) {
- case 2:
- // TODO: store deployment version
- break;
- default:
- break;
- }
- }
- else {
- warning("unknown option to -iphoneos_version_min, not 1.x or 2.x");
- }
-}
-
-void Options::setWeakReferenceMismatchTreatment(const char* treatment)
-{
- if ( treatment == NULL )
- throw "-weak_reference_mismatches missing [ error | weak | non-weak ]";
-
- if ( strcmp(treatment, "error") == 0 )
- fWeakReferenceMismatchTreatment = kWeakReferenceMismatchError;
- else if ( strcmp(treatment, "weak") == 0 )
- fWeakReferenceMismatchTreatment = kWeakReferenceMismatchWeak;
- else if ( strcmp(treatment, "non-weak") == 0 )
- fWeakReferenceMismatchTreatment = kWeakReferenceMismatchNonWeak;
- else
- throw "invalid option to -weak_reference_mismatches [ error | weak | non-weak ]";
-}
-
-Options::CommonsMode Options::parseCommonsTreatment(const char* mode)
-{
- if ( mode == NULL )
- throw "-commons missing [ ignore_dylibs | use_dylibs | error ]";
-
- if ( strcmp(mode, "ignore_dylibs") == 0 )
- return kCommonsIgnoreDylibs;
- else if ( strcmp(mode, "use_dylibs") == 0 )
- return kCommonsOverriddenByDylibs;
- else if ( strcmp(mode, "error") == 0 )
- return kCommonsConflictsDylibsError;
- else
- throw "invalid option to -commons [ ignore_dylibs | use_dylibs | error ]";
-}
-
-void Options::addDylibOverride(const char* paths)
-{
- if ( paths == NULL )
- throw "-dylib_file must followed by two colon separated paths";
- const char* colon = strchr(paths, ':');
- if ( colon == NULL )
- throw "-dylib_file must followed by two colon separated paths";
- int len = colon-paths;
- char* target = new char[len+2];
- strncpy(target, paths, len);
- target[len] = '\0';
- DylibOverride entry;
- entry.installName = target;
- entry.useInstead = &colon[1];
- fDylibOverrides.push_back(entry);
-}
-
-uint64_t Options::parseAddress(const char* addr)
-{
- char* endptr;
- uint64_t result = strtoull(addr, &endptr, 16);
- return result;
-}
-
-uint32_t Options::parseProtection(const char* prot)
-{
- uint32_t result = 0;
- for(const char* p = prot; *p != '\0'; ++p) {
- switch(tolower(*p)) {
- case 'r':
- result |= VM_PROT_READ;
- break;
- case 'w':
- result |= VM_PROT_WRITE;
- break;
- case 'x':
- result |= VM_PROT_EXECUTE;
- break;
- case '-':
- break;
- default:
- throwf("unknown -segprot lettter in %s", prot);
- }
- }
- return result;
-}
-
-
-
-//
-// Parses number of form X[.Y[.Z]] into a uint32_t where the nibbles are xxxx.yy.zz
-//
-//
-uint32_t Options::parseVersionNumber(const char* versionString)
-{
- unsigned long x = 0;
- unsigned long y = 0;
- unsigned long z = 0;
- char* end;
- x = strtoul(versionString, &end, 10);
- if ( *end == '.' ) {
- y = strtoul(&end[1], &end, 10);
- if ( *end == '.' ) {
- z = strtoul(&end[1], &end, 10);
- }
- }
- if ( (*end != '\0') || (x > 0xffff) || (y > 0xff) || (z > 0xff) )
- throwf("malformed version number: %s", versionString);
-
- return (x << 16) | ( y << 8 ) | z;
-}
-
-static const char* cstringSymbolName(const char* orderFileString)
-{
- char* result;
- asprintf(&result, "cstring=%s", orderFileString);
- // convert escaped characters
- char* d = result;
- for(const char* s=result; *s != '\0'; ++s, ++d) {
- if ( *s == '\\' ) {
- ++s;
- switch ( *s ) {
- case 'n':
- *d = '\n';
- break;
- case 't':
- *d = '\t';
- break;
- case 'v':
- *d = '\v';
- break;
- case 'b':
- *d = '\b';
- break;
- case 'r':
- *d = '\r';
- break;
- case 'f':
- *d = '\f';
- break;
- case 'a':
- *d = '\a';
- break;
- case '\\':
- *d = '\\';
- break;
- case '?':
- *d = '\?';
- break;
- case '\'':
- *d = '\r';
- break;
- case '\"':
- *d = '\"';
- break;
- case 'x':
- // hexadecimal value of char
- {
- ++s;
- char value = 0;
- while ( isxdigit(*s) ) {
- value *= 16;
- if ( isdigit(*s) )
- value += (*s-'0');
- else
- value += ((toupper(*s)-'A') + 10);
- ++s;
- }
- *d = value;
- }
- break;
- default:
- if ( isdigit(*s) ) {
- // octal value of char
- char value = 0;
- while ( isdigit(*s) ) {
- value = (value << 3) + (*s-'0');
- ++s;
- }
- *d = value;
- }
- }
- }
- else {
- *d = *s;
- }
- }
- *d = '\0';
- return result;
-}
-
-void Options::parseOrderFile(const char* path, bool cstring)
-{
- // read in whole file
- int fd = ::open(path, O_RDONLY, 0);
- if ( fd == -1 )
- throwf("can't open order file: %s", path);
- struct stat stat_buf;
- ::fstat(fd, &stat_buf);
- char* p = (char*)malloc(stat_buf.st_size+1);
- if ( p == NULL )
- throwf("can't process order file: %s", path);
- if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size )
- throwf("can't read order file: %s", path);
- ::close(fd);
- p[stat_buf.st_size] = '\n';
-
- // parse into vector of pairs
- char * const end = &p[stat_buf.st_size+1];
- enum { lineStart, inSymbol, inComment } state = lineStart;
- char* symbolStart = NULL;
- for (char* s = p; s < end; ++s ) {
- switch ( state ) {
- case lineStart:
- if ( *s =='#' ) {
- state = inComment;
- }
- else if ( !isspace(*s) || cstring ) {
- state = inSymbol;
- symbolStart = s;
- }
- break;
- case inSymbol:
- if ( (*s == '\n') || (!cstring && (*s == '#')) ) {
- bool wasComment = (*s == '#');
- *s = '\0';
- // removing any trailing spaces
- char* last = s-1;
- while ( isspace(*last) ) {
- *last = '\0';
- --last;
- }
- if ( strncmp(symbolStart, "ppc:", 4) == 0 ) {
- if ( fArchitecture == CPU_TYPE_POWERPC )
- symbolStart = &symbolStart[4];
- else
- symbolStart = NULL;
- }
- // if there is an architecture prefix, only use this symbol it if matches current arch
- else if ( strncmp(symbolStart, "ppc64:", 6) == 0 ) {
- if ( fArchitecture == CPU_TYPE_POWERPC64 )
- symbolStart = &symbolStart[6];
- else
- symbolStart = NULL;
- }
- else if ( strncmp(symbolStart, "i386:", 5) == 0 ) {
- if ( fArchitecture == CPU_TYPE_I386 )
- symbolStart = &symbolStart[5];
- else
- symbolStart = NULL;
- }
- else if ( strncmp(symbolStart, "x86_64:", 7) == 0 ) {
- if ( fArchitecture == CPU_TYPE_X86_64 )
- symbolStart = &symbolStart[7];
- else
- symbolStart = NULL;
- }
- else if ( strncmp(symbolStart, "arm:", 4) == 0 ) {
- if ( fArchitecture == CPU_TYPE_ARM )
- symbolStart = &symbolStart[4];
- else
- symbolStart = NULL;
- }
- if ( symbolStart != NULL ) {
- char* objFileName = NULL;
- char* colon = strstr(symbolStart, ".o:");
- if ( colon != NULL ) {
- colon[2] = '\0';
- objFileName = symbolStart;
- symbolStart = &colon[3];
- }
- // trim leading spaces
- while ( isspace(*symbolStart) )
- ++symbolStart;
- Options::OrderedSymbol pair;
- if ( cstring )
- pair.symbolName = cstringSymbolName(symbolStart);
- else
- pair.symbolName = symbolStart;
- pair.objectFileName = objFileName;
- fOrderedSymbols.push_back(pair);
- }
- symbolStart = NULL;
- if ( wasComment )
- state = inComment;
- else
- state = lineStart;
- }
- break;
- case inComment:
- if ( *s == '\n' )
- state = lineStart;
- break;
- }
- }
- // Note: we do not free() the malloc buffer, because the strings are used by the fOrderedSymbols
-}
-
-void Options::parseSectionOrderFile(const char* segment, const char* section, const char* path)
-{
- if ( (strcmp(section, "__cstring") == 0) && (strcmp(segment, "__TEXT") == 0) ) {
- parseOrderFile(path, true);
- }
- else if ( (strncmp(section, "__literal",9) == 0) && (strcmp(segment, "__TEXT") == 0) ) {
- warning("sorting of __literal[4,8,16] sections not supported");
- }
- else {
- // ignore section information and append all symbol names to global order file
- parseOrderFile(path, false);
- }
-}
-
-void Options::addSection(const char* segment, const char* section, const char* path)
-{
- if ( strlen(segment) > 16 )
- throw "-seccreate segment name max 16 chars";
- if ( strlen(section) > 16 ) {
- char* tmp = strdup(section);
- tmp[16] = '\0';
- warning("-seccreate section name (%s) truncated to 16 chars (%s)\n", section, tmp);
- section = tmp;
- }
-
- // read in whole file
- int fd = ::open(path, O_RDONLY, 0);
- if ( fd == -1 )
- throwf("can't open -sectcreate file: %s", path);
- struct stat stat_buf;
- ::fstat(fd, &stat_buf);
- char* p = (char*)malloc(stat_buf.st_size);
- if ( p == NULL )
- throwf("can't process -sectcreate file: %s", path);
- if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size )
- throwf("can't read -sectcreate file: %s", path);
- ::close(fd);
-
- // record section to create
- ExtraSection info = { segment, section, path, (uint8_t*)p, stat_buf.st_size };
- fExtraSections.push_back(info);
-}
-
-void Options::addSectionAlignment(const char* segment, const char* section, const char* alignmentStr)
-{
- if ( strlen(segment) > 16 )
- throw "-sectalign segment name max 16 chars";
- if ( strlen(section) > 16 )
- throw "-sectalign section name max 16 chars";
-
- // argument to -sectalign is a hexadecimal number
- char* endptr;
- unsigned long value = strtoul(alignmentStr, &endptr, 16);
- if ( *endptr != '\0')
- throw "argument for -sectalign is not a hexadecimal number";
- if ( value > 0x8000 )
- throw "argument for -sectalign must be less than or equal to 0x8000";
- if ( value == 0 ) {
- warning("zero is not a valid -sectalign");
- value = 1;
- }
-
- // alignment is power of 2 (e.g. page alignment = 12)
- uint8_t alignment = (uint8_t)__builtin_ctz(value);
- if ( (unsigned long)(1 << alignment) != value ) {
- warning("alignment for -sectalign %s %s is not a power of two, using 0x%X",
- segment, section, 1 << alignment);
- }
-
- SectionAlignment info = { segment, section, alignment };
- fSectionAlignments.push_back(info);
-}
-
-void Options::addLibrary(const FileInfo& info)
-{
- // if this library has already been added, don't add again (archives are automatically repeatedly searched)
- for (std::vector<Options::FileInfo>::iterator fit = fInputFiles.begin(); fit != fInputFiles.end(); fit++) {
- if ( strcmp(info.path, fit->path) == 0 ) {
- // if dylib is specified again but weak, record that it should be weak
- if ( info.options.fWeakImport )
- fit->options.fWeakImport = true;
- return;
- }
- }
- // add to list
- fInputFiles.push_back(info);
-}
-
-void Options::warnObsolete(const char* arg)
-{
- warning("option %s is obsolete and being ignored", arg);
-}
-
-
-
-
-//
-// Process all command line arguments.
-//
-// The only error checking done here is that each option is valid and if it has arguments
-// that they too are valid.
-//
-// The general rule is "last option wins", i.e. if both -bundle and -dylib are specified,
-// whichever was last on the command line is used.
-//
-// Error check for invalid combinations of options is done in checkIllegalOptionCombinations()
-//
-void Options::parse(int argc, const char* argv[])
-{
- // pass one builds search list from -L and -F options
- this->buildSearchPaths(argc, argv);
-
- // reduce re-allocations
- fInputFiles.reserve(32);
-
- // pass two parse all other options
- for(int i=1; i < argc; ++i) {
- const char* arg = argv[i];
-
- if ( arg[0] == '-' ) {
-
- // Since we don't care about the files passed, just the option names, we do this here.
- if (fPrintOptions)
- fprintf (stderr, "[Logging ld64 options]\t%s\n", arg);
-
- if ( (arg[1] == 'L') || (arg[1] == 'F') ) {
- // previously handled by buildSearchPaths()
- }
- // The one gnu style option we have to keep compatibility
- // with gcc. Might as well have the single hyphen one as well.
- else if ( (strcmp(arg, "--help") == 0)
- || (strcmp(arg, "-help") == 0)) {
- fprintf (stdout, "ld64: For information on command line options please use 'man ld'.\n");
- exit (0);
- }
- else if ( strcmp(arg, "-arch") == 0 ) {
- parseArch(argv[++i]);
- }
- else if ( strcmp(arg, "-dynamic") == 0 ) {
- // default
- }
- else if ( strcmp(arg, "-static") == 0 ) {
- if ( fOutputKind != kObjectFile )
- fOutputKind = kStaticExecutable;
- fReaderOptions.fForStatic = true;
- }
- else if ( strcmp(arg, "-dylib") == 0 ) {
- fOutputKind = kDynamicLibrary;
- }
- else if ( strcmp(arg, "-bundle") == 0 ) {
- fOutputKind = kDynamicBundle;
- }
- else if ( strcmp(arg, "-dylinker") == 0 ) {
- fOutputKind = kDyld;
- }
- else if ( strcmp(arg, "-execute") == 0 ) {
- if ( fOutputKind != kStaticExecutable )
- fOutputKind = kDynamicExecutable;
- }
- else if ( strcmp(arg, "-r") == 0 ) {
- fOutputKind = kObjectFile;
- }
- else if ( strcmp(arg, "-o") == 0 ) {
- fOutputFile = argv[++i];
- }
- else if ( (arg[1] == 'l') && (strncmp(arg,"-lazy_",6) !=0) ) {
- addLibrary(findLibrary(&arg[2]));
- }
- // This causes a dylib to be weakly bound at
- // link time. This corresponds to weak_import.
- else if ( strncmp(arg, "-weak-l", 7) == 0 ) {
- FileInfo info = findLibrary(&arg[7]);
- info.options.fWeakImport = true;
- addLibrary(info);
- }
- else if ( strncmp(arg, "-lazy-l", 7) == 0 ) {
- FileInfo info = findLibrary(&arg[7], true);
- info.options.fLazyLoad = true;
- addLibrary(info);
- fUsingLazyDylibLinking = true;
- }
- // Avoid lazy binding.
- // ??? Deprecate.
- else if ( strcmp(arg, "-bind_at_load") == 0 ) {
- fBindAtLoad = true;
- }
- else if ( strcmp(arg, "-twolevel_namespace") == 0 ) {
- fNameSpace = kTwoLevelNameSpace;
- }
- else if ( strcmp(arg, "-flat_namespace") == 0 ) {
- fNameSpace = kFlatNameSpace;
- }
- // Also sets a bit to ensure dyld causes everything
- // in the namespace to be flat.
- // ??? Deprecate
- else if ( strcmp(arg, "-force_flat_namespace") == 0 ) {
- fNameSpace = kForceFlatNameSpace;
- }
- // Similar to --whole-archive.
- else if ( strcmp(arg, "-all_load") == 0 ) {
- fReaderOptions.fFullyLoadArchives = true;
- }
- else if ( strcmp(arg, "-noall_load") == 0) {
- warnObsolete(arg);
- }
- // Similar to -all_load
- else if ( strcmp(arg, "-ObjC") == 0 ) {
- fReaderOptions.fLoadAllObjcObjectsFromArchives = true;
- }
- // Library versioning.
- else if ( (strcmp(arg, "-dylib_compatibility_version") == 0)
- || (strcmp(arg, "-compatibility_version") == 0)) {
- const char* vers = argv[++i];
- if ( vers == NULL )
- throw "-dylib_compatibility_version missing <version>";
- fDylibCompatVersion = parseVersionNumber(vers);
- }
- else if ( (strcmp(arg, "-dylib_current_version") == 0)
- || (strcmp(arg, "-current_version") == 0)) {
- const char* vers = argv[++i];
- if ( vers == NULL )
- throw "-dylib_current_version missing <version>";
- fDylibCurrentVersion = parseVersionNumber(vers);
- }
- else if ( strcmp(arg, "-sectorder") == 0 ) {
- if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) )
- throw "-sectorder missing <segment> <section> <file-path>";
- parseSectionOrderFile(argv[i+1], argv[i+2], argv[i+3]);
- i += 3;
- }
- else if ( strcmp(arg, "-order_file") == 0 ) {
- parseOrderFile(argv[++i], false);
- }
- else if ( strcmp(arg, "-order_file_statistics") == 0 ) {
- fPrintOrderFileStatistics = true;
- }
- // ??? Deprecate segcreate.
- // -sectcreate puts whole files into a section in the output.
- else if ( (strcmp(arg, "-sectcreate") == 0) || (strcmp(arg, "-segcreate") == 0) ) {
- if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) )
- throw "-sectcreate missing <segment> <section> <file-path>";
- addSection(argv[i+1], argv[i+2], argv[i+3]);
- i += 3;
- }
- // Since we have a full path in binary/library names we need to be able to override it.
- else if ( (strcmp(arg, "-dylib_install_name") == 0)
- || (strcmp(arg, "-dylinker_install_name") == 0)
- || (strcmp(arg, "-install_name") == 0)) {
- fDylibInstallName = argv[++i];
- if ( fDylibInstallName == NULL )
- throw "-install_name missing <path>";
- }
- // Sets the base address of the output.
- else if ( (strcmp(arg, "-seg1addr") == 0) || (strcmp(arg, "-image_base") == 0) ) {
- const char* address = argv[++i];
- if ( address == NULL )
- throwf("%s missing <address>", arg);
- fBaseAddress = parseAddress(address);
- uint64_t temp = (fBaseAddress+4095) & (-4096); // page align
- if ( fBaseAddress != temp ) {
- warning("-seg1addr not page aligned, rounding up");
- fBaseAddress = temp;
- }
- }
- else if ( strcmp(arg, "-e") == 0 ) {
- fEntryName = argv[++i];
- }
- // Same as -@ from the FSF linker.
- else if ( strcmp(arg, "-filelist") == 0 ) {
- const char* path = argv[++i];
- if ( (path == NULL) || (path[0] == '-') )
- throw "-filelist missing <path>";
- loadFileList(path);
- }
- else if ( strcmp(arg, "-keep_private_externs") == 0 ) {
- fKeepPrivateExterns = true;
- }
- else if ( strcmp(arg, "-final_output") == 0 ) {
- fFinalName = argv[++i];
- }
- // Ensure that all calls to exported symbols go through lazy pointers. Multi-module
- // just ensures that this happens for cross object file boundaries.
- else if ( (strcmp(arg, "-interposable") == 0) || (strcmp(arg, "-multi_module") == 0)) {
- switch ( fInterposeMode ) {
- case kInterposeNone:
- case kInterposeAllExternal:
- fInterposeMode = kInterposeAllExternal;
- break;
- case kInterposeSome:
- // do nothing, -interposable_list overrides -interposable"
- break;
- }
- }
- else if ( strcmp(arg, "-interposable_list") == 0 ) {
- fInterposeMode = kInterposeSome;
- loadExportFile(argv[++i], "-interposable_list", fInterposeList);
- }
- // Default for -interposable/-multi_module/-single_module.
- else if ( strcmp(arg, "-single_module") == 0 ) {
- fInterposeMode = kInterposeNone;
- }
- else if ( strcmp(arg, "-exported_symbols_list") == 0 ) {
- if ( fExportMode == kDontExportSome )
- throw "can't use -exported_symbols_list and -unexported_symbols_list";
- fExportMode = kExportSome;
- loadExportFile(argv[++i], "-exported_symbols_list", fExportSymbols);
- }
- else if ( strcmp(arg, "-unexported_symbols_list") == 0 ) {
- if ( fExportMode == kExportSome )
- throw "can't use -unexported_symbols_list and -exported_symbols_list";
- fExportMode = kDontExportSome;
- loadExportFile(argv[++i], "-unexported_symbols_list", fDontExportSymbols);
- }
- else if ( strcmp(arg, "-exported_symbol") == 0 ) {
- if ( fExportMode == kDontExportSome )
- throw "can't use -exported_symbol and -unexported_symbols";
- fExportMode = kExportSome;
- fExportSymbols.insert(argv[++i]);
- }
- else if ( strcmp(arg, "-unexported_symbol") == 0 ) {
- if ( fExportMode == kExportSome )
- throw "can't use -unexported_symbol and -exported_symbol";
- fExportMode = kDontExportSome;
- fDontExportSymbols.insert(argv[++i]);
- }
- else if ( strcmp(arg, "-non_global_symbols_no_strip_list") == 0 ) {
- if ( fLocalSymbolHandling == kLocalSymbolsSelectiveExclude )
- throw "can't use -non_global_symbols_no_strip_list and -non_global_symbols_strip_list";
- fLocalSymbolHandling = kLocalSymbolsSelectiveInclude;
- loadExportFile(argv[++i], "-non_global_symbols_no_strip_list", fLocalSymbolsIncluded);
- }
- else if ( strcmp(arg, "-non_global_symbols_strip_list") == 0 ) {
- if ( fLocalSymbolHandling == kLocalSymbolsSelectiveInclude )
- throw "can't use -non_global_symbols_no_strip_list and -non_global_symbols_strip_list";
- fLocalSymbolHandling = kLocalSymbolsSelectiveExclude;
- loadExportFile(argv[++i], "-non_global_symbols_strip_list", fLocalSymbolsExcluded);
- }
- // ??? Deprecate
- else if ( strcmp(arg, "-no_arch_warnings") == 0 ) {
- fIgnoreOtherArchFiles = true;
- }
- else if ( strcmp(arg, "-force_cpusubtype_ALL") == 0 ) {
- fForceSubtypeAll = true;
- }
- // Similar to -weak-l but uses the absolute path name to the library.
- else if ( strcmp(arg, "-weak_library") == 0 ) {
- FileInfo info = findFile(argv[++i]);
- info.options.fWeakImport = true;
- addLibrary(info);
- }
- else if ( strcmp(arg, "-lazy_library") == 0 ) {
- FileInfo info = findFile(argv[++i]);
- info.options.fLazyLoad = true;
- addLibrary(info);
- fUsingLazyDylibLinking = true;
- }
- else if ( strcmp(arg, "-framework") == 0 ) {
- addLibrary(findFramework(argv[++i]));
- }
- else if ( strcmp(arg, "-weak_framework") == 0 ) {
- FileInfo info = findFramework(argv[++i]);
- info.options.fWeakImport = true;
- addLibrary(info);
- }
- else if ( strcmp(arg, "-lazy_framework") == 0 ) {
- FileInfo info = findFramework(argv[++i]);
- info.options.fLazyLoad = true;
- addLibrary(info);
- fUsingLazyDylibLinking = true;
- }
- else if ( strcmp(arg, "-search_paths_first") == 0 ) {
- // previously handled by buildSearchPaths()
- }
- else if ( strcmp(arg, "-undefined") == 0 ) {
- setUndefinedTreatment(argv[++i]);
- }
- // Debugging output flag.
- else if ( strcmp(arg, "-arch_multiple") == 0 ) {
- fMessagesPrefixedWithArchitecture = true;
- }
- // Specify what to do with relocations in read only
- // sections like .text. Could be errors, warnings,
- // or suppressed. Currently we do nothing with the
- // flag.
- else if ( strcmp(arg, "-read_only_relocs") == 0 ) {
- switch ( parseTreatment(argv[++i]) ) {
- case kNULL:
- case kInvalid:
- throw "-read_only_relocs missing [ warning | error | suppress ]";
- case kWarning:
- fWarnTextRelocs = true;
- fAllowTextRelocs = true;
- break;
- case kSuppress:
- fWarnTextRelocs = false;
- fAllowTextRelocs = true;
- break;
- case kError:
- fWarnTextRelocs = false;
- fAllowTextRelocs = false;
- break;
- }
- }
- else if ( strcmp(arg, "-sect_diff_relocs") == 0 ) {
- warnObsolete(arg);
- ++i;
- }
- // Warn, error or make strong a mismatch between weak
- // and non-weak references.
- else if ( strcmp(arg, "-weak_reference_mismatches") == 0 ) {
- setWeakReferenceMismatchTreatment(argv[++i]);
- }
- // For a deployment target of 10.3 and earlier ld64 will
- // prebind an executable with 0s in all addresses that
- // are prebound. This can then be fixed up by update_prebinding
- // later. Prebinding is less useful on 10.4 and greater.
- else if ( strcmp(arg, "-prebind") == 0 ) {
- fPrebind = true;
- }
- else if ( strcmp(arg, "-noprebind") == 0 ) {
- warnObsolete(arg);
- fPrebind = false;
- }
- else if ( strcmp(arg, "-prebind_allow_overlap") == 0 ) {
- warnObsolete(arg);
- }
- else if ( strcmp(arg, "-prebind_all_twolevel_modules") == 0 ) {
- warnObsolete(arg);
- }
- else if ( strcmp(arg, "-noprebind_all_twolevel_modules") == 0 ) {
- warnObsolete(arg);
- }
- else if ( strcmp(arg, "-nofixprebinding") == 0 ) {
- warnObsolete(arg);
- }
- // This should probably be deprecated when we respect -L and -F
- // when searching for libraries.
- else if ( strcmp(arg, "-dylib_file") == 0 ) {
- addDylibOverride(argv[++i]);
- }
- // What to expand @executable_path to if found in dependent dylibs
- else if ( strcmp(arg, "-executable_path") == 0 ) {
- fExecutablePath = argv[++i];
- if ( (fExecutablePath == NULL) || (fExecutablePath[0] == '-') )
- throw "-executable_path missing <path>";
- // if a directory was passed, add / to end
- // <rdar://problem/5171880> ld64 can't find @executable _path relative dylibs from our umbrella frameworks
- struct stat statBuffer;
- if ( stat(fExecutablePath, &statBuffer) == 0 ) {
- if ( (statBuffer.st_mode & S_IFMT) == S_IFDIR ) {
- char* pathWithSlash = new char[strlen(fExecutablePath)+2];
- strcpy(pathWithSlash, fExecutablePath);
- strcat(pathWithSlash, "/");
- fExecutablePath = pathWithSlash;
- }
- }
- }
- // Aligns all segments to the power of 2 boundary specified.
- else if ( strcmp(arg, "-segalign") == 0 ) {
- warnObsolete(arg);
- ++i;
- }
- // Puts a specified segment at a particular address that must
- // be a multiple of the segment alignment.
- else if ( strcmp(arg, "-segaddr") == 0 ) {
- SegmentStart seg;
- seg.name = argv[++i];
- if ( (seg.name == NULL) || (argv[i+1] == NULL) )
- throw "-segaddr missing segName Adddress";
- seg.address = parseAddress(argv[++i]);
- uint64_t temp = seg.address & (-4096); // page align
- if ( (seg.address != temp) )
- warning("-segaddr %s not page aligned, rounding down", seg.name);
- fCustomSegmentAddresses.push_back(seg);
- }
- // ??? Deprecate when we deprecate split-seg.
- else if ( strcmp(arg, "-segs_read_only_addr") == 0 ) {
- fBaseAddress = parseAddress(argv[++i]);
- }
- // ??? Deprecate when we deprecate split-seg.
- else if ( strcmp(arg, "-segs_read_write_addr") == 0 ) {
- fBaseWritableAddress = parseAddress(argv[++i]);
- fSplitSegs = true;
- }
- // ??? Deprecate when we get rid of basing at build time.
- else if ( strcmp(arg, "-seg_addr_table") == 0 ) {
- const char* name = argv[++i];
- if ( name == NULL )
- throw "-seg_addr_table missing argument";
- fSegAddrTablePath = name;
- }
- else if ( strcmp(arg, "-seg_addr_table_filename") == 0 ) {
- warnObsolete(arg);
- ++i;
- }
- else if ( strcmp(arg, "-segprot") == 0 ) {
- SegmentProtect seg;
- seg.name = argv[++i];
- if ( (seg.name == NULL) || (argv[i+1] == NULL) || (argv[i+2] == NULL) )
- throw "-segprot missing segName max-prot init-prot";
- seg.max = parseProtection(argv[++i]);
- seg.init = parseProtection(argv[++i]);
- fCustomSegmentProtections.push_back(seg);
- }
- else if ( strcmp(arg, "-pagezero_size") == 0 ) {
- const char* size = argv[++i];
- if ( size == NULL )
- throw "-pagezero_size missing <size>";
- fZeroPageSize = parseAddress(size);
- uint64_t temp = fZeroPageSize & (-4096); // page align
- if ( (fZeroPageSize != temp) )
- warning("-pagezero_size not page aligned, rounding down");
- fZeroPageSize = temp;
- }
- else if ( strcmp(arg, "-stack_addr") == 0 ) {
- const char* address = argv[++i];
- if ( address == NULL )
- throw "-stack_addr missing <address>";
- fStackAddr = parseAddress(address);
- }
- else if ( strcmp(arg, "-stack_size") == 0 ) {
- const char* size = argv[++i];
- if ( size == NULL )
- throw "-stack_size missing <address>";
- fStackSize = parseAddress(size);
- uint64_t temp = fStackSize & (-4096); // page align
- if ( (fStackSize != temp) )
- warning("-stack_size not page aligned, rounding down");
- }
- else if ( strcmp(arg, "-allow_stack_execute") == 0 ) {
- fExecutableStack = true;
- }
- else if ( strcmp(arg, "-sectalign") == 0 ) {
- if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) )
- throw "-sectalign missing <segment> <section> <file-path>";
- addSectionAlignment(argv[i+1], argv[i+2], argv[i+3]);
- i += 3;
- }
- else if ( strcmp(arg, "-sectorder_detail") == 0 ) {
- warnObsolete(arg);
- }
- else if ( strcmp(arg, "-sectobjectsymbols") == 0 ) {
- warnObsolete(arg);
- i += 2;
- }
- else if ( strcmp(arg, "-bundle_loader") == 0 ) {
- 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 ) {
- warnObsolete(arg);
- }
- else if ( strcmp(arg, "-twolevel_namespace_hints") == 0 ) {
- // FIX FIX
- }
- // Use this flag to set default behavior for deployement targets.
- else if ( strcmp(arg, "-macosx_version_min") == 0 ) {
- setMacOSXVersionMin(argv[++i]);
- }
- else if ( (strcmp(arg, "-aspen_version_min") == 0) || (strcmp(arg, "-iphone_version_min") == 0) || (strcmp(arg, "-iphoneos_version_min") == 0) ) {
- setIPhoneVersionMin(argv[++i]);
- }
- else if ( strcmp(arg, "-multiply_defined") == 0 ) {
- //warnObsolete(arg);
- ++i;
- }
- else if ( strcmp(arg, "-multiply_defined_unused") == 0 ) {
- warnObsolete(arg);
- ++i;
- }
- else if ( strcmp(arg, "-nomultidefs") == 0 ) {
- warnObsolete(arg);
- }
- // Display each file in which the argument symbol appears and whether
- // the file defines or references it. This option takes an argument
- // as -y<symbol> note that there is no space.
- else if ( strncmp(arg, "-y", 2) == 0 ) {
- warnObsolete("-y");
- }
- // Same output as -y, but output <arg> number of undefined symbols only.
- else if ( strcmp(arg, "-Y") == 0 ) {
- //warnObsolete(arg);
- ++i;
- }
- // This option affects all objects linked into the final result.
- else if ( strcmp(arg, "-m") == 0 ) {
- warnObsolete(arg);
- }
- 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];
- if ( name == NULL )
- throw "-u missing argument";
- fInitialUndefines.push_back(name);
- }
- else if ( strcmp(arg, "-U") == 0 ) {
- const char* name = argv[++i];
- if ( name == NULL )
- throw "-U missing argument";
- fAllowedUndefined.insert(name);
- }
- else if ( strcmp(arg, "-s") == 0 ) {
- warnObsolete(arg);
- fLocalSymbolHandling = kLocalSymbolsNone;
- fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoNone;
- }
- else if ( strcmp(arg, "-x") == 0 ) {
- fLocalSymbolHandling = kLocalSymbolsNone;
- }
- else if ( strcmp(arg, "-S") == 0 ) {
- fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoNone;
- }
- else if ( strcmp(arg, "-X") == 0 ) {
- warnObsolete(arg);
- }
- else if ( strcmp(arg, "-Si") == 0 ) {
- warnObsolete(arg);
- fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoFull;
- }
- else if ( strcmp(arg, "-b") == 0 ) {
- warnObsolete(arg);
- }
- else if ( strcmp(arg, "-Sn") == 0 ) {
- warnObsolete(arg);
- fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoFull;
- }
- else if ( strcmp(arg, "-Sp") == 0 ) {
- warnObsolete(arg);
- }
- else if ( strcmp(arg, "-dead_strip") == 0 ) {
- fDeadStrip = kDeadStripOnPlusUnusedInits;
- }
- else if ( strcmp(arg, "-no_dead_strip_inits_and_terms") == 0 ) {
- fDeadStrip = kDeadStripOn;
- }
- else if ( strcmp(arg, "-w") == 0 ) {
- // previously handled by buildSearchPaths()
- }
- else if ( strcmp(arg, "-arch_errors_fatal") == 0 ) {
- // FIX FIX
- }
- else if ( strcmp(arg, "-M") == 0 ) {
- // FIX FIX
- }
- else if ( strcmp(arg, "-headerpad") == 0 ) {
- const char* size = argv[++i];
- if ( size == NULL )
- throw "-headerpad missing argument";
- fMinimumHeaderPad = parseAddress(size);
- }
- else if ( strcmp(arg, "-headerpad_max_install_names") == 0 ) {
- fMaxMinimumHeaderPad = true;
- }
- else if ( strcmp(arg, "-t") == 0 ) {
- fReaderOptions.fLogAllFiles = true;
- }
- else if ( strcmp(arg, "-whatsloaded") == 0 ) {
- fReaderOptions.fLogObjectFiles = true;
- }
- else if ( strcmp(arg, "-A") == 0 ) {
- warnObsolete(arg);
- ++i;
- }
- else if ( strcmp(arg, "-umbrella") == 0 ) {
- const char* name = argv[++i];
- if ( name == NULL )
- throw "-umbrella missing argument";
- fUmbrellaName = name;
- }
- else if ( strcmp(arg, "-allowable_client") == 0 ) {
- const char* name = argv[++i];
-
- if ( name == NULL )
- throw "-allowable_client missing argument";
-
- fAllowableClients.push_back(name);
- }
- else if ( strcmp(arg, "-client_name") == 0 ) {
- const char* name = argv[++i];
-
- if ( name == NULL )
- throw "-client_name missing argument";
-
- fClientName = name;
- }
- else if ( strcmp(arg, "-sub_umbrella") == 0 ) {
- const char* name = argv[++i];
- if ( name == NULL )
- throw "-sub_umbrella missing argument";
- fSubUmbellas.push_back(name);
- }
- else if ( strcmp(arg, "-sub_library") == 0 ) {
- const char* name = argv[++i];
- if ( name == NULL )
- throw "-sub_library missing argument";
- fSubLibraries.push_back(name);
- }
- else if ( strcmp(arg, "-init") == 0 ) {
- const char* name = argv[++i];
- if ( name == NULL )
- throw "-init missing argument";
- fInitFunctionName = name;
- }
- else if ( strcmp(arg, "-dot") == 0 ) {
- const char* name = argv[++i];
- if ( name == NULL )
- throw "-dot missing argument";
- fDotOutputFile = name;
- }
- else if ( strcmp(arg, "-warn_commons") == 0 ) {
- fWarnCommons = true;
- }
- else if ( strcmp(arg, "-commons") == 0 ) {
- fCommonsMode = parseCommonsTreatment(argv[++i]);
- }
- else if ( strcmp(arg, "-keep_relocs") == 0 ) {
- fKeepRelocations = true;
- }
- else if ( strcmp(arg, "-warn_stabs") == 0 ) {
- fWarnStabs = true;
- }
- else if ( strcmp(arg, "-pause") == 0 ) {
- fPause = true;
- }
- else if ( strcmp(arg, "-print_statistics") == 0 ) {
- fStatistics = true;
- }
- else if ( strcmp(arg, "-d") == 0 ) {
- fReaderOptions.fMakeTentativeDefinitionsReal = true;
- }
- else if ( strcmp(arg, "-v") == 0 ) {
- // previously handled by buildSearchPaths()
- }
- else if ( strcmp(arg, "-Z") == 0 ) {
- // previously handled by buildSearchPaths()
- }
- else if ( strcmp(arg, "-syslibroot") == 0 ) {
- ++i;
- // previously handled by buildSearchPaths()
- }
- else if ( strcmp(arg, "-no_uuid") == 0 ) {
- fUUIDMode = kUUIDNone;
- }
- else if ( strcmp(arg, "-random_uuid") == 0 ) {
- fUUIDMode = kUUIDRandom;
- }
- else if ( strcmp(arg, "-dtrace") == 0 ) {
- const char* name = argv[++i];
- if ( name == NULL )
- throw "-dtrace missing argument";
- fDtraceScriptName = name;
- }
- else if ( strcmp(arg, "-root_safe") == 0 ) {
- fReaderOptions.fRootSafe = true;
- }
- else if ( strcmp(arg, "-setuid_safe") == 0 ) {
- fReaderOptions.fSetuidSafe = true;
- }
- else if ( strcmp(arg, "-alias") == 0 ) {
- ObjectFile::ReaderOptions::AliasPair pair;
- pair.realName = argv[++i];
- if ( pair.realName == NULL )
- throw "missing argument to -alias";
- pair.alias = argv[++i];
- if ( pair.alias == NULL )
- throw "missing argument to -alias";
- fReaderOptions.fAliases.push_back(pair);
- }
- else if ( strcmp(arg, "-alias_list") == 0 ) {
- parseAliasFile(argv[++i]);
- }
- // put this last so that it does not interfer with other options starting with 'i'
- else if ( strncmp(arg, "-i", 2) == 0 ) {
- const char* colon = strchr(arg, ':');
- if ( colon == NULL )
- throwf("unknown option: %s", arg);
- ObjectFile::ReaderOptions::AliasPair pair;
- char* temp = new char[colon-arg];
- strlcpy(temp, &arg[2], colon-arg-1);
- pair.realName = &colon[1];
- pair.alias = temp;
- fReaderOptions.fAliases.push_back(pair);
- }
- else if ( strcmp(arg, "-save-temps") == 0 ) {
- fSaveTempFiles = true;
- }
- else if ( strcmp(arg, "-rpath") == 0 ) {
- const char* path = argv[++i];
- if ( path == NULL )
- throw "missing argument to -rpath";
- fRPaths.push_back(path);
- }
- else if ( strcmp(arg, "-read_only_stubs") == 0 ) {
- fReadOnlyx86Stubs = true;
- }
- else if ( strcmp(arg, "-slow_stubs") == 0 ) {
- fReaderOptions.fSlowx86Stubs = true;
- }
- else if ( strcmp(arg, "-map") == 0 ) {
- fMapPath = argv[++i];
- if ( fMapPath == NULL )
- throw "missing argument to -map";
- }
- else if ( strcmp(arg, "-pie") == 0 ) {
- fPositionIndependentExecutable = true;
- }
- else if ( strncmp(arg, "-reexport-l", 11) == 0 ) {
- FileInfo info = findLibrary(&arg[11], true);
- info.options.fReExport = true;
- addLibrary(info);
- }
- else if ( strcmp(arg, "-reexport_library") == 0 ) {
- FileInfo info = findFile(argv[++i]);
- info.options.fReExport = true;
- addLibrary(info);
- }
- else if ( strcmp(arg, "-reexport_framework") == 0 ) {
- FileInfo info = findFramework(argv[++i]);
- info.options.fReExport = true;
- addLibrary(info);
- }
- else if ( strcmp(arg, "-dead_strip_dylibs") == 0 ) {
- fDeadStripDylibs = true;
- }
- else if ( strcmp(arg, "-no_implicit_dylibs") == 0 ) {
- fReaderOptions.fImplicitlyLinkPublicDylibs = false;
- }
- else if ( strcmp(arg, "-new_linker") == 0 ) {
- // ignore
- }
- else if ( strcmp(arg, "-no_encryption") == 0 ) {
- fEncryptable = false;
- }
- else {
- throwf("unknown option: %s", arg);
- }
- }
- else {
- FileInfo info = findFile(arg);
- if ( strcmp(&info.path[strlen(info.path)-2], ".a") == 0 )
- addLibrary(info);
- else
- fInputFiles.push_back(info);
- }
- }
-
- // if a -lazy option was used, implicitly link in lazydylib1.o
- if ( fUsingLazyDylibLinking ) {
- addLibrary(findLibrary("lazydylib1.o"));
- }
-}
-
-
-
-//
-// -syslibroot <path> is used for SDK support.
-// The rule is that all search paths (both explicit and default) are
-// checked to see if they exist in the SDK. If so, that path is
-// replaced with the sdk prefixed path. If not, that search path
-// is used as is. If multiple -syslibroot options are specified
-// their directory structures are logically overlayed and files
-// from sdks specified earlier on the command line used before later ones.
-
-void Options::buildSearchPaths(int argc, const char* argv[])
-{
- bool addStandardLibraryDirectories = true;
- std::vector<const char*> libraryPaths;
- std::vector<const char*> frameworkPaths;
- libraryPaths.reserve(10);
- frameworkPaths.reserve(10);
- // scan through argv looking for -L, -F, -Z, and -syslibroot options
- for(int i=0; i < argc; ++i) {
- if ( (argv[i][0] == '-') && (argv[i][1] == 'L') )
- libraryPaths.push_back(&argv[i][2]);
- else if ( (argv[i][0] == '-') && (argv[i][1] == 'F') )
- frameworkPaths.push_back(&argv[i][2]);
- else if ( strcmp(argv[i], "-Z") == 0 )
- addStandardLibraryDirectories = false;
- else if ( strcmp(argv[i], "-v") == 0 ) {
- fVerbose = true;
- extern const char ldVersionString[];
- fprintf(stderr, "%s", ldVersionString);
- // if only -v specified, exit cleanly
- if ( argc == 2 ) {
-#if LTO_SUPPORT
- printLTOVersion(*this);
-#endif
- exit(0);
- }
- }
- else if ( strcmp(argv[i], "-syslibroot") == 0 ) {
- const char* path = argv[++i];
- if ( path == NULL )
- throw "-syslibroot missing argument";
- fSDKPaths.push_back(path);
- }
- else if ( strcmp(argv[i], "-search_paths_first") == 0 ) {
- // ??? Deprecate when we get -Bstatic/-Bdynamic.
- fLibrarySearchMode = kSearchDylibAndArchiveInEachDir;
- }
- else if ( strcmp(argv[i], "-w") == 0 ) {
- sEmitWarnings = false;
- }
- }
- if ( addStandardLibraryDirectories ) {
- libraryPaths.push_back("/usr/lib");
- libraryPaths.push_back("/usr/local/lib");
-
- frameworkPaths.push_back("/Library/Frameworks/");
- frameworkPaths.push_back("/System/Library/Frameworks/");
- // <rdar://problem/5433882> remove /Network from default search path
- //frameworkPaths.push_back("/Network/Library/Frameworks/");
- }
-
- // <rdar://problem/5829579> Support for configure based hacks
- // if last -syslibroot is /, then ignore all syslibroots
- if ( fSDKPaths.size() > 0 ) {
- if ( strcmp(fSDKPaths.back(), "/") == 0 ) {
- fSDKPaths.clear();
- }
- }
-
- // now merge sdk and library paths to make real search paths
- fLibrarySearchPaths.reserve(libraryPaths.size()*(fSDKPaths.size()+1));
- for (std::vector<const char*>::iterator it = libraryPaths.begin(); it != libraryPaths.end(); it++) {
- const char* libDir = *it;
- bool sdkOverride = false;
- if ( libDir[0] == '/' ) {
- char betterLibDir[PATH_MAX];
- if ( strstr(libDir, "/..") != NULL ) {
- if ( realpath(libDir, betterLibDir) != NULL )
- libDir = strdup(betterLibDir);
- }
- const int libDirLen = strlen(libDir);
- for (std::vector<const char*>::iterator sdkit = fSDKPaths.begin(); sdkit != fSDKPaths.end(); sdkit++) {
- // ??? Should be using string here.
- const char* sdkDir = *sdkit;
- const int sdkDirLen = strlen(sdkDir);
- char newPath[libDirLen + sdkDirLen+4];
- strcpy(newPath, sdkDir);
- if ( newPath[sdkDirLen-1] == '/' )
- newPath[sdkDirLen-1] = '\0';
- strcat(newPath, libDir);
- struct stat statBuffer;
- if ( stat(newPath, &statBuffer) == 0 ) {
- fLibrarySearchPaths.push_back(strdup(newPath));
- sdkOverride = true;
- }
- }
- }
- if ( !sdkOverride )
- fLibrarySearchPaths.push_back(libDir);
- }
-
- // now merge sdk and framework paths to make real search paths
- fFrameworkSearchPaths.reserve(frameworkPaths.size()*(fSDKPaths.size()+1));
- for (std::vector<const char*>::iterator it = frameworkPaths.begin(); it != frameworkPaths.end(); it++) {
- const char* frameworkDir = *it;
- bool sdkOverride = false;
- if ( frameworkDir[0] == '/' ) {
- char betterFrameworkDir[PATH_MAX];
- if ( strstr(frameworkDir, "/..") != NULL ) {
- if ( realpath(frameworkDir, betterFrameworkDir) != NULL )
- frameworkDir = strdup(betterFrameworkDir);
- }
- const int frameworkDirLen = strlen(frameworkDir);
- for (std::vector<const char*>::iterator sdkit = fSDKPaths.begin(); sdkit != fSDKPaths.end(); sdkit++) {
- // ??? Should be using string here
- const char* sdkDir = *sdkit;
- const int sdkDirLen = strlen(sdkDir);
- char newPath[frameworkDirLen + sdkDirLen+4];
- strcpy(newPath, sdkDir);
- if ( newPath[sdkDirLen-1] == '/' )
- newPath[sdkDirLen-1] = '\0';
- strcat(newPath, frameworkDir);
- struct stat statBuffer;
- if ( stat(newPath, &statBuffer) == 0 ) {
- fFrameworkSearchPaths.push_back(strdup(newPath));
- sdkOverride = true;
- }
- }
- }
- if ( !sdkOverride )
- fFrameworkSearchPaths.push_back(frameworkDir);
- }
-
- if ( fVerbose ) {
- fprintf(stderr,"Library search paths:\n");
- for (std::vector<const char*>::iterator it = fLibrarySearchPaths.begin();
- it != fLibrarySearchPaths.end();
- it++)
- fprintf(stderr,"\t%s\n", *it);
- fprintf(stderr,"Framework search paths:\n");
- for (std::vector<const char*>::iterator it = fFrameworkSearchPaths.begin();
- it != fFrameworkSearchPaths.end();
- it++)
- fprintf(stderr,"\t%s\n", *it);
- }
-}
-
-// this is run before the command line is parsed
-void Options::parsePreCommandLineEnvironmentSettings()
-{
- if ((getenv("LD_TRACE_ARCHIVES") != NULL)
- || (getenv("RC_TRACE_ARCHIVES") != NULL))
- fReaderOptions.fTraceArchives = true;
-
- if ((getenv("LD_TRACE_DYLIBS") != NULL)
- || (getenv("RC_TRACE_DYLIBS") != NULL)) {
- fReaderOptions.fTraceDylibs = true;
- fReaderOptions.fTraceIndirectDylibs = true;
- }
-
- if (getenv("RC_TRACE_DYLIB_SEARCHING") != NULL) {
- fTraceDylibSearching = true;
- }
-
- if (getenv("LD_PRINT_OPTIONS") != NULL)
- fPrintOptions = true;
-
- if (fReaderOptions.fTraceDylibs || fReaderOptions.fTraceArchives)
- fReaderOptions.fTraceOutputFile = getenv("LD_TRACE_FILE");
-
- if (getenv("LD_PRINT_ORDER_FILE_STATISTICS") != NULL)
- fPrintOrderFileStatistics = true;
-
- if (getenv("LD_SPLITSEGS_NEW_LIBRARIES") != NULL)
- fSplitSegs = true;
-
- if (getenv("LD_NO_ENCRYPT") != NULL)
- fEncryptable = false;
-
- sWarningsSideFilePath = getenv("LD_WARN_FILE");
-}
-
-
-// this is run after the command line is parsed
-void Options::parsePostCommandLineEnvironmentSettings()
-{
- // when building a dynamic main executable, default any use of @executable_path to output path
- if ( fExecutablePath == NULL && (fOutputKind == kDynamicExecutable) ) {
- fExecutablePath = fOutputFile;
- }
-
- // allow build system to set default seg_addr_table
- if ( fSegAddrTablePath == NULL )
- fSegAddrTablePath = getenv("LD_SEG_ADDR_TABLE");
-
- // allow build system to turn on prebinding
- if ( !fPrebind ) {
- fPrebind = ( getenv("LD_PREBIND") != NULL );
- }
-
- // allow build system to force on dead-code-stripping
- if ( fDeadStrip == kDeadStripOff ) {
- if ( getenv("LD_DEAD_STRIP") != NULL ) {
- switch (fOutputKind) {
- case Options::kDynamicLibrary:
- case Options::kDynamicExecutable:
- case Options::kDynamicBundle:
- fDeadStrip = kDeadStripOn;
- break;
- case Options::kObjectFile:
- case Options::kDyld:
- case Options::kStaticExecutable:
- break;
- }
- }
- }
-
- // allow build system to force on -warn_commons
- if ( getenv("LD_WARN_COMMONS") != NULL )
- fWarnCommons = true;
-}
-
-void Options::reconfigureDefaults()
-{
- // sync reader options
- switch ( fOutputKind ) {
- case Options::kObjectFile:
- fReaderOptions.fForFinalLinkedImage = false;
- break;
- case Options::kDyld:
- fReaderOptions.fForDyld = true;
- fReaderOptions.fForFinalLinkedImage = true;
- break;
- case Options::kDynamicLibrary:
- case Options::kDynamicBundle:
- fReaderOptions.fForFinalLinkedImage = true;
- break;
- case Options::kDynamicExecutable:
- case Options::kStaticExecutable:
- fReaderOptions.fLinkingMainExecutable = true;
- fReaderOptions.fForFinalLinkedImage = true;
- break;
- }
-
- // set default min OS version
- if ( fReaderOptions.fVersionMin == ObjectFile::ReaderOptions::kMinUnset ) {
- // if -macosx_version_min not used, try environment variable
- const char* envVers = getenv("MACOSX_DEPLOYMENT_TARGET");
- if ( envVers != NULL )
- setMacOSXVersionMin(envVers);
- // if -macosx_version_min and environment variable not used assume current OS version
- if ( fReaderOptions.fVersionMin == ObjectFile::ReaderOptions::kMinUnset )
- fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_5; // FIX FIX, this really should be a check of the OS version the linker is running on
- }
-
- // adjust min based on architecture
- switch ( fArchitecture ) {
- case CPU_TYPE_I386:
- if ( fReaderOptions.fVersionMin < ObjectFile::ReaderOptions::k10_4 ) {
- //warning("-macosx_version_min should be 10.4 or later for i386");
- fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_4;
- }
- break;
- case CPU_TYPE_POWERPC64:
- if ( fReaderOptions.fVersionMin < ObjectFile::ReaderOptions::k10_4 ) {
- //warning("-macosx_version_min should be 10.4 or later for ppc64");
- fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_4;
- }
- break;
- case CPU_TYPE_X86_64:
- if ( fReaderOptions.fVersionMin < ObjectFile::ReaderOptions::k10_4 ) {
- //warning("-macosx_version_min should be 10.4 or later for x86_64");
- fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_4;
- }
- break;
- }
-
- // disable implicit dylibs when targetting 10.3
- // <rdar://problem/5451987> add option to disable implicit load commands for indirectly used public dylibs
- if ( fReaderOptions.fVersionMin <= ObjectFile::ReaderOptions::k10_3 )
- fReaderOptions.fImplicitlyLinkPublicDylibs = false;
-
-
- // determine if info for shared region should be added
- if ( fOutputKind == Options::kDynamicLibrary ) {
- if ( fReaderOptions.fVersionMin >= ObjectFile::ReaderOptions::k10_5 )
- if ( fArchitecture != CPU_TYPE_ARM )
- fSharedRegionEligible = true;
- }
-
- // allow build system to force linker to ignore seg_addr_table
- if ( getenv("LD_FORCE_NO_SEG_ADDR_TABLE") != NULL )
- fSegAddrTablePath = NULL;
-
- // check for base address specified externally
- if ( (fSegAddrTablePath != NULL) && (fOutputKind == Options::kDynamicLibrary) ) {
- parseSegAddrTable(fSegAddrTablePath, this->installPath());
- // HACK to support seg_addr_table entries that are physical paths instead of install paths
- if ( fBaseAddress == 0 ) {
- if ( strcmp(this->installPath(), "/usr/lib/libstdc++.6.dylib") == 0 )
- parseSegAddrTable(fSegAddrTablePath, "/usr/lib/libstdc++.6.0.4.dylib");
-
- else if ( strcmp(this->installPath(), "/usr/lib/libz.1.dylib") == 0 )
- parseSegAddrTable(fSegAddrTablePath, "/usr/lib/libz.1.2.3.dylib");
-
- else if ( strcmp(this->installPath(), "/usr/lib/libutil.dylib") == 0 )
- parseSegAddrTable(fSegAddrTablePath, "/usr/lib/libutil1.0.dylib");
- }
- }
-
- // split segs only allowed for dylibs
- if ( fSplitSegs ) {
- // split seg only supported for ppc, i386, and arm.
- switch ( fArchitecture ) {
- case CPU_TYPE_POWERPC:
- case CPU_TYPE_I386:
- if ( fOutputKind != Options::kDynamicLibrary )
- fSplitSegs = false;
- // make sure read and write segments are proper distance apart
- if ( fSplitSegs && (fBaseWritableAddress-fBaseAddress != 0x10000000) )
- fBaseWritableAddress = fBaseAddress + 0x10000000;
- break;
- case CPU_TYPE_ARM:
- if ( fOutputKind != Options::kDynamicLibrary ) {
- fSplitSegs = false;
- }
- else {
- // make sure read and write segments are proper distance apart
- if ( fSplitSegs && (fBaseWritableAddress-fBaseAddress != 0x08000000) )
- fBaseWritableAddress = fBaseAddress + 0x08000000;
- }
- break;
- default:
- fSplitSegs = false;
- fBaseAddress = 0;
- fBaseWritableAddress = 0;
- }
- }
-
- // disable prebinding depending on arch and min OS version
- if ( fPrebind ) {
- switch ( fArchitecture ) {
- case CPU_TYPE_POWERPC:
- case CPU_TYPE_I386:
- if ( fReaderOptions.fVersionMin == ObjectFile::ReaderOptions::k10_4 ) {
- // in 10.4 only split seg dylibs are prebound
- if ( (fOutputKind != Options::kDynamicLibrary) || ! fSplitSegs )
- fPrebind = false;
- }
- else if ( fReaderOptions.fVersionMin >= ObjectFile::ReaderOptions::k10_5 ) {
- // in 10.5 nothing is prebound
- fPrebind = false;
- }
- else {
- // in 10.3 and earlier only dylibs and main executables could be prebound
- switch ( fOutputKind ) {
- case Options::kDynamicExecutable:
- case Options::kDynamicLibrary:
- // only main executables and dylibs can be prebound
- break;
- case Options::kStaticExecutable:
- case Options::kDynamicBundle:
- case Options::kObjectFile:
- case Options::kDyld:
- // disable prebinding for everything else
- fPrebind = false;
- break;
- }
- }
- break;
- case CPU_TYPE_POWERPC64:
- case CPU_TYPE_X86_64:
- fPrebind = false;
- break;
- case CPU_TYPE_ARM:
- switch ( fOutputKind ) {
- case Options::kDynamicExecutable:
- case Options::kDynamicLibrary:
- // only main executables and dylibs can be prebound
- break;
- case Options::kStaticExecutable:
- case Options::kDynamicBundle:
- case Options::kObjectFile:
- case Options::kDyld:
- // disable prebinding for everything else
- fPrebind = false;
- break;
- }
- break;
- }
- }
-
- // only prebound images can be split-seg
- if ( fSplitSegs && !fPrebind )
- fSplitSegs = false;
-
- // figure out if module table is needed for compatibility with old ld/dyld
- if ( fOutputKind == Options::kDynamicLibrary ) {
- switch ( fArchitecture ) {
- case CPU_TYPE_POWERPC: // 10.3 and earlier dyld requires a module table
- case CPU_TYPE_I386: // ld_classic for 10.4.x requires a module table
- if ( fReaderOptions.fVersionMin <= ObjectFile::ReaderOptions::k10_5 )
- fNeedsModuleTable = true;
- break;
- case CPU_TYPE_ARM:
- fNeedsModuleTable = true; // redo_prebinding requires a module table
- break;
- }
- }
-
- // <rdar://problem/5366363> -r -x implies -S
- if ( (fOutputKind == Options::kObjectFile) && (fLocalSymbolHandling == kLocalSymbolsNone) )
- fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoNone;
-
- // only ARM main executables can be encrypted
- if ( fOutputKind != Options::kDynamicExecutable )
- fEncryptable = false;
- if ( fArchitecture != CPU_TYPE_ARM )
- fEncryptable = false;
-}
-
-void Options::checkIllegalOptionCombinations()
-{
- // check -undefined setting
- switch ( fUndefinedTreatment ) {
- case kUndefinedError:
- case kUndefinedDynamicLookup:
- // always legal
- break;
- case kUndefinedWarning:
- case kUndefinedSuppress:
- // requires flat namespace
- if ( fNameSpace == kTwoLevelNameSpace )
- throw "can't use -undefined warning or suppress with -twolevel_namespace";
- break;
- }
-
- // unify -sub_umbrella with dylibs
- for (std::vector<const char*>::iterator it = fSubUmbellas.begin(); it != fSubUmbellas.end(); it++) {
- const char* subUmbrella = *it;
- bool found = false;
- for (std::vector<Options::FileInfo>::iterator fit = fInputFiles.begin(); fit != fInputFiles.end(); fit++) {
- Options::FileInfo& info = *fit;
- const char* lastSlash = strrchr(info.path, '/');
- if ( lastSlash == NULL )
- lastSlash = info.path - 1;
- if ( strcmp(&lastSlash[1], subUmbrella) == 0 ) {
- info.options.fReExport = true;
- found = true;
- break;
- }
- }
- if ( ! found )
- warning("-sub_umbrella %s does not match a supplied dylib", subUmbrella);
- }
-
- // unify -sub_library with dylibs
- for (std::vector<const char*>::iterator it = fSubLibraries.begin(); it != fSubLibraries.end(); it++) {
- const char* subLibrary = *it;
- bool found = false;
- for (std::vector<Options::FileInfo>::iterator fit = fInputFiles.begin(); fit != fInputFiles.end(); fit++) {
- Options::FileInfo& info = *fit;
- const char* lastSlash = strrchr(info.path, '/');
- if ( lastSlash == NULL )
- lastSlash = info.path - 1;
- const char* dot = strchr(&lastSlash[1], '.');
- if ( dot == NULL )
- dot = &lastSlash[strlen(lastSlash)];
- if ( strncmp(&lastSlash[1], subLibrary, dot-lastSlash-1) == 0 ) {
- info.options.fReExport = true;
- found = true;
- break;
- }
- }
- if ( ! found )
- warning("-sub_library %s does not match a supplied dylib", subLibrary);
- }
-
- // sync reader options
- if ( fNameSpace != kTwoLevelNameSpace )
- fReaderOptions.fFlatNamespace = true;
-
- // check -stack_addr
- if ( fStackAddr != 0 ) {
- switch (fArchitecture) {
- case CPU_TYPE_I386:
- case CPU_TYPE_POWERPC:
- case CPU_TYPE_ARM:
- if ( fStackAddr > 0xFFFFFFFF )
- 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 )
- throw "-stack_addr must be multiples of 4K";
- if ( fStackSize == 0 )
- throw "-stack_addr must be used with -stack_size";
- }
-
- // check -stack_size
- if ( fStackSize != 0 ) {
- switch (fArchitecture) {
- case CPU_TYPE_I386:
- case CPU_TYPE_POWERPC:
- if ( fStackSize > 0xFFFFFFFF )
- throw "-stack_size must be < 4G for 32-bit processes";
- if ( fStackAddr == 0 ) {
- fStackAddr = 0xC0000000;
- }
- if ( (fStackAddr > 0xB0000000) && ((fStackAddr-fStackSize) < 0xB0000000) )
- warning("custom stack placement overlaps and will disable shared region");
- break;
- case CPU_TYPE_ARM:
- if ( fStackSize > 0xFFFFFFFF )
- throw "-stack_size must be < 4G for 32-bit processes";
- if ( fStackAddr == 0 )
- fStackAddr = 0x30000000;
- if ( fStackAddr > 0x40000000)
- throw "-stack_addr must be < 1G for arm";
- case CPU_TYPE_POWERPC64:
- case CPU_TYPE_X86_64:
- if ( fStackAddr == 0 ) {
- fStackAddr = 0x00007FFF5C000000LL;
- }
- break;
- }
- if ( (fStackSize & -4096) != fStackSize )
- throw "-stack_size must be multiples of 4K";
- switch ( fOutputKind ) {
- case Options::kDynamicExecutable:
- case Options::kStaticExecutable:
- // custom stack size only legal when building main executable
- break;
- case Options::kDynamicLibrary:
- case Options::kDynamicBundle:
- case Options::kObjectFile:
- case Options::kDyld:
- throw "-stack_size option can only be used when linking a main executable";
- }
- }
-
- // check that -allow_stack_execute is only used with main executables
- if ( fExecutableStack ) {
- switch ( fOutputKind ) {
- case Options::kDynamicExecutable:
- case Options::kStaticExecutable:
- // -allow_stack_execute size only legal when building main executable
- break;
- case Options::kDynamicLibrary:
- case Options::kDynamicBundle:
- case Options::kObjectFile:
- case Options::kDyld:
- throw "-allow_stack_execute option can only be used when linking a main executable";
- }
- }
-
- // check -client_name is only used when making a bundle or main executable
- if ( fClientName != NULL ) {
- switch ( fOutputKind ) {
- case Options::kDynamicExecutable:
- case Options::kDynamicBundle:
- break;
- case Options::kStaticExecutable:
- case Options::kDynamicLibrary:
- case Options::kObjectFile:
- case Options::kDyld:
- throw "-client_name can only be used with -bundle";
- }
- }
-
- // check -init is only used when building a dylib
- if ( (fInitFunctionName != NULL) && (fOutputKind != Options::kDynamicLibrary) )
- throw "-init can only be used with -dynamiclib";
-
- // check -bundle_loader only used with -bundle
- if ( (fBundleLoader != NULL) && (fOutputKind != Options::kDynamicBundle) )
- throw "-bundle_loader can only be used with -bundle";
-
- // check -dtrace not used with -r
- if ( (fDtraceScriptName != NULL) && (fOutputKind == Options::kObjectFile) )
- throw "-dtrace can only be used when creating final linked images";
-
- // check -d can only be used with -r
- if ( fReaderOptions.fMakeTentativeDefinitionsReal && (fOutputKind != Options::kObjectFile) )
- throw "-d can only be used with -r";
-
- // check that -root_safe is not used with -r
- if ( fReaderOptions.fRootSafe && (fOutputKind == Options::kObjectFile) )
- throw "-root_safe cannot be used with -r";
-
- // check that -setuid_safe is not used with -r
- if ( fReaderOptions.fSetuidSafe && (fOutputKind == Options::kObjectFile) )
- throw "-setuid_safe cannot be used with -r";
-
- // make sure all required exported symbols exist
- std::vector<const char*> impliedExports;
- for (NameSet::iterator it=fExportSymbols.regularBegin(); it != fExportSymbols.regularEnd(); it++) {
- const char* name = *it;
- // never export .eh symbols
- const int len = strlen(name);
- if ( (strcmp(&name[len-3], ".eh") == 0) || (strncmp(name, ".objc_category_name_", 20) == 0) )
- warning("ignoring %s in export list", name);
- else
- fInitialUndefines.push_back(name);
- if ( strncmp(name, ".objc_class_name_", 17) == 0 ) {
- // rdar://problem/4718189 map ObjC class names to new runtime names
- switch (fArchitecture) {
- case CPU_TYPE_POWERPC64:
- case CPU_TYPE_X86_64:
- case CPU_TYPE_ARM:
- char* temp;
- asprintf(&temp, "_OBJC_CLASS_$_%s", &name[17]);
- impliedExports.push_back(temp);
- asprintf(&temp, "_OBJC_METACLASS_$_%s", &name[17]);
- impliedExports.push_back(temp);
- break;
- }
- }
- }
- for (std::vector<const char*>::iterator it=impliedExports.begin(); it != impliedExports.end(); it++) {
- const char* name = *it;
- fExportSymbols.insert(name);
- fInitialUndefines.push_back(name);
- }
-
- // make sure that -init symbol exist
- if ( fInitFunctionName != NULL )
- fInitialUndefines.push_back(fInitFunctionName);
-
- // check custom segments
- if ( fCustomSegmentAddresses.size() != 0 ) {
- // verify no segment is in zero page
- if ( fZeroPageSize != ULLONG_MAX ) {
- for (std::vector<SegmentStart>::iterator it = fCustomSegmentAddresses.begin(); it != fCustomSegmentAddresses.end(); ++it) {
- if ( (it->address >= 0) && (it->address < fZeroPageSize) )
- throwf("-segaddr %s 0x%X conflicts with -pagezero_size", it->name, it->address);
- }
- }
- // verify no duplicates
- for (std::vector<SegmentStart>::iterator it = fCustomSegmentAddresses.begin(); it != fCustomSegmentAddresses.end(); ++it) {
- for (std::vector<SegmentStart>::iterator it2 = fCustomSegmentAddresses.begin(); it2 != fCustomSegmentAddresses.end(); ++it2) {
- if ( (it->address == it2->address) && (it != it2) )
- throwf("duplicate -segaddr addresses for %s and %s", it->name, it2->name);
- }
- // a custom segment address of zero will disable the use of a zero page
- if ( it->address == 0 )
- fZeroPageSize = 0;
- }
- }
-
- if ( fZeroPageSize == ULLONG_MAX ) {
- // zero page size not specified on command line, set default
- switch (fArchitecture) {
- case CPU_TYPE_I386:
- case CPU_TYPE_POWERPC:
- case CPU_TYPE_ARM:
- // first 4KB for 32-bit architectures
- fZeroPageSize = 0x1000;
- break;
- case CPU_TYPE_POWERPC64:
- // first 4GB for ppc64 on 10.5
- if ( fReaderOptions.fVersionMin >= ObjectFile::ReaderOptions::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:
- if ( fZeroPageSize != 0 )
- 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";
-
- // can't use -rpath unless targeting 10.5 or later
- if ( fRPaths.size() > 0 ) {
- if ( fReaderOptions.fVersionMin < ObjectFile::ReaderOptions::k10_5 )
- throw "-rpath can only be used when targeting Mac OS X 10.5 or later";
- switch ( fOutputKind ) {
- case Options::kDynamicExecutable:
- case Options::kDynamicLibrary:
- case Options::kDynamicBundle:
- break;
- case Options::kStaticExecutable:
- case Options::kObjectFile:
- case Options::kDyld:
- throw "-rpath can only be used when creating a dynamic final linked image";
- }
- }
-
- // check -pie is only used when building a dynamic main executable for 10.5
- if ( fPositionIndependentExecutable ) {
- if ( fOutputKind != Options::kDynamicExecutable )
- throw "-pie can only be used when linking a main executable";
- if ( fReaderOptions.fVersionMin < ObjectFile::ReaderOptions::k10_5 )
- throw "-pie can only be used when targeting Mac OS X 10.5 or later";
- }
-}
-
-
-
-void Options::checkForClassic(int argc, const char* argv[])
-{
- // scan options
- bool archFound = false;
- bool staticFound = false;
- bool dtraceFound = false;
- bool rFound = false;
- bool creatingMachKernel = false;
- bool newLinker = false;
-
- for(int i=0; i < argc; ++i) {
- const char* arg = argv[i];
- if ( arg[0] == '-' ) {
- if ( strcmp(arg, "-arch") == 0 ) {
- parseArch(argv[++i]);
- archFound = true;
- }
- else if ( strcmp(arg, "-static") == 0 ) {
- staticFound = true;
- }
- else if ( strcmp(arg, "-dtrace") == 0 ) {
- dtraceFound = true;
- }
- else if ( strcmp(arg, "-r") == 0 ) {
- rFound = true;
- }
- else if ( strcmp(arg, "-new_linker") == 0 ) {
- newLinker = true;
- }
- else if ( strcmp(arg, "-classic_linker") == 0 ) {
- // ld_classic does not understand this option, so remove it
- for(int j=i; j < argc; ++j)
- argv[j] = argv[j+1];
- this->gotoClassicLinker(argc-1, argv);
- }
- else if ( strcmp(arg, "-o") == 0 ) {
- const char* outfile = argv[++i];
- if ( (outfile != NULL) && (strstr(outfile, "/mach_kernel") != NULL) )
- creatingMachKernel = true;
- }
- }
- }
-
- // -dtrace only supported by new linker
- if( dtraceFound )
- return;
-
- if( archFound ) {
- switch ( fArchitecture ) {
- case CPU_TYPE_POWERPC:
- case CPU_TYPE_I386:
- case CPU_TYPE_ARM:
-// if ( staticFound && (rFound || !creatingMachKernel) ) {
- if ( staticFound && !newLinker ) {
- // this environment variable will disable use of ld_classic for -static links
- if ( getenv("LD_NO_CLASSIC_LINKER_STATIC") == NULL ) {
- // ld_classic does not support -aspen_version_min, so change
- for(int j=0; j < argc; ++j) {
- if ( (strcmp(argv[j], "-aspen_version_min") == 0)
- || (strcmp(argv[j], "-iphone_version_min") == 0)
- || (strcmp(argv[j], "-iphoneos_version_min") == 0) ) {
- argv[j] = "-macosx_version_min";
- if ( j < argc-1 )
- argv[j+1] = "10.5";
- break;
- }
- }
- this->gotoClassicLinker(argc, argv);
- }
- }
- break;
- }
- }
- else {
- // work around for VSPTool
- if ( staticFound )
- this->gotoClassicLinker(argc, argv);
- }
-
-}
-
-void Options::gotoClassicLinker(int argc, const char* argv[])
-{
- argv[0] = "ld_classic";
- execvp(argv[0], (char**)argv);
- fprintf(stderr, "can't exec ld_classic\n");
- exit(1);
-}
+++ /dev/null
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
- *
- * Copyright (c) 2005-2007 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#ifndef __OPTIONS__
-#define __OPTIONS__
-
-
-#include <stdint.h>
-#include <mach/machine.h>
-
-#include <vector>
-#include <ext/hash_set>
-
-#include "ObjectFile.h"
-
-extern void throwf (const char* format, ...) __attribute__ ((noreturn));
-extern void warning(const char* format, ...);
-
-class DynamicLibraryOptions
-{
-public:
- DynamicLibraryOptions() : fWeakImport(false), fReExport(false), fBundleLoader(false), fLazyLoad(false) {}
-
- bool fWeakImport;
- bool fReExport;
- bool fBundleLoader;
- bool fLazyLoad;
-};
-
-//
-// The public interface to the Options class is the abstract representation of what work the linker
-// should do.
-//
-// This abstraction layer will make it easier to support a future where the linker is a shared library
-// invoked directly from Xcode. The target settings in Xcode would be used to directly construct an Options
-// object (without building a command line which is then parsed).
-//
-//
-class Options
-{
-public:
- Options(int argc, const char* argv[]);
- ~Options();
-
- enum OutputKind { kDynamicExecutable, kStaticExecutable, kDynamicLibrary, kDynamicBundle, kObjectFile, kDyld };
- enum NameSpace { kTwoLevelNameSpace, kFlatNameSpace, kForceFlatNameSpace };
- // Standard treatment for many options.
- enum Treatment { kError, kWarning, kSuppress, kNULL, kInvalid };
- enum UndefinedTreatment { kUndefinedError, kUndefinedWarning, kUndefinedSuppress, kUndefinedDynamicLookup };
- enum WeakReferenceMismatchTreatment { kWeakReferenceMismatchError, kWeakReferenceMismatchWeak,
- kWeakReferenceMismatchNonWeak };
- enum CommonsMode { kCommonsIgnoreDylibs, kCommonsOverriddenByDylibs, kCommonsConflictsDylibsError };
- enum DeadStripMode { kDeadStripOff, kDeadStripOn, kDeadStripOnPlusUnusedInits };
- enum UUIDMode { kUUIDNone, kUUIDRandom, kUUIDContent };
- enum LocalSymbolHandling { kLocalSymbolsAll, kLocalSymbolsNone, kLocalSymbolsSelectiveInclude, kLocalSymbolsSelectiveExclude };
-
- struct FileInfo {
- const char* path;
- uint64_t fileLen;
- time_t modTime;
- DynamicLibraryOptions options;
- };
-
- struct ExtraSection {
- const char* segmentName;
- const char* sectionName;
- const char* path;
- const uint8_t* data;
- uint64_t dataLen;
- };
-
- struct SectionAlignment {
- const char* segmentName;
- const char* sectionName;
- uint8_t alignment;
- };
-
- struct OrderedSymbol {
- const char* symbolName;
- const char* objectFileName;
- };
-
- struct SegmentStart {
- const char* name;
- uint64_t address;
- };
-
- struct SegmentProtect {
- const char* name;
- uint32_t max;
- uint32_t init;
- };
-
- struct DylibOverride {
- const char* installName;
- const char* useInstead;
- };
-
-
- const ObjectFile::ReaderOptions& readerOptions();
- const char* getOutputFilePath();
- std::vector<FileInfo>& getInputFiles();
-
- cpu_type_t architecture() { return fArchitecture; }
- bool preferSubArchitecture() { return fHasPreferredSubType; }
- cpu_subtype_t subArchitecture() { return fSubArchitecture; }
- OutputKind outputKind();
- bool prebind();
- bool bindAtLoad();
- bool fullyLoadArchives();
- NameSpace nameSpace();
- const char* installPath(); // only for kDynamicLibrary
- uint32_t currentVersion(); // only for kDynamicLibrary
- uint32_t compatibilityVersion(); // only for kDynamicLibrary
- const char* entryName(); // only for kDynamicExecutable or kStaticExecutable
- const char* executablePath();
- uint64_t baseAddress();
- bool keepPrivateExterns(); // only for kObjectFile
- bool needsModuleTable(); // only for kDynamicLibrary
- bool interposable(const char* name);
- bool hasExportRestrictList();
- bool hasExportMaskList();
- bool hasWildCardExportRestrictList();
- bool allGlobalsAreDeadStripRoots();
- bool shouldExport(const char*);
- bool ignoreOtherArchInputFiles();
- bool forceCpuSubtypeAll();
- bool traceDylibs();
- bool traceArchives();
- DeadStripMode deadStrip();
- UndefinedTreatment undefinedTreatment();
- ObjectFile::ReaderOptions::VersionMin macosxVersionMin();
- bool messagesPrefixedWithArchitecture();
- Treatment picTreatment();
- WeakReferenceMismatchTreatment weakReferenceMismatchTreatment();
- const char* umbrellaName();
- std::vector<const char*>& allowableClients();
- const char* clientName();
- const char* initFunctionName(); // only for kDynamicLibrary
- const char* dotOutputFile();
- uint64_t zeroPageSize();
- bool hasCustomStack();
- uint64_t customStackSize();
- uint64_t customStackAddr();
- bool hasExecutableStack();
- std::vector<const char*>& initialUndefines();
- bool printWhyLive(const char* name);
- uint32_t minimumHeaderPad();
- bool maxMminimumHeaderPad() { return fMaxMinimumHeaderPad; }
- std::vector<ExtraSection>& extraSections();
- std::vector<SectionAlignment>& sectionAlignments();
- CommonsMode commonsMode();
- bool warnCommons();
- bool keepRelocations();
- FileInfo findFile(const char* path);
- UUIDMode getUUIDMode() { return fUUIDMode; }
- bool warnStabs();
- bool pauseAtEnd() { return fPause; }
- bool printStatistics() { return fStatistics; }
- bool printArchPrefix() { return fMessagesPrefixedWithArchitecture; }
- void gotoClassicLinker(int argc, const char* argv[]);
- bool sharedRegionEligible() { return fSharedRegionEligible; }
- bool printOrderFileStatistics() { return fPrintOrderFileStatistics; }
- const char* dTraceScriptName() { return fDtraceScriptName; }
- bool dTrace() { return (fDtraceScriptName != NULL); }
- std::vector<OrderedSymbol>& orderedSymbols() { return fOrderedSymbols; }
- bool splitSeg() { return fSplitSegs; }
- uint64_t baseWritableAddress() { return fBaseWritableAddress; }
- std::vector<SegmentStart>& customSegmentAddresses() { return fCustomSegmentAddresses; }
- std::vector<SegmentProtect>& customSegmentProtections() { return fCustomSegmentProtections; }
- bool saveTempFiles() { return fSaveTempFiles; }
- const std::vector<const char*>& rpaths() { return fRPaths; }
- bool readOnlyx86Stubs() { return fReadOnlyx86Stubs; }
- bool slowx86Stubs() { return fReaderOptions.fSlowx86Stubs; }
- std::vector<DylibOverride>& dylibOverrides() { return fDylibOverrides; }
- const char* generatedMapPath() { return fMapPath; }
- bool positionIndependentExecutable() { return fPositionIndependentExecutable; }
- Options::FileInfo findFileUsingPaths(const char* path);
- bool deadStripDylibs() { return fDeadStripDylibs; }
- bool allowedUndefined(const char* name) { return ( fAllowedUndefined.find(name) != fAllowedUndefined.end() ); }
- bool someAllowedUndefines() { return (fAllowedUndefined.size() != 0); }
- LocalSymbolHandling localSymbolHandling() { return fLocalSymbolHandling; }
- bool keepLocalSymbol(const char* symbolName);
- bool allowTextRelocs() { return fAllowTextRelocs; }
- bool warnAboutTextRelocs() { return fWarnTextRelocs; }
- bool usingLazyDylibLinking() { return fUsingLazyDylibLinking; }
- bool verbose() { return fVerbose; }
- bool makeEncryptable() { return fEncryptable; }
-
-private:
- class CStringEquals
- {
- public:
- bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
- };
-
- typedef __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> NameSet;
- enum ExportMode { kExportDefault, kExportSome, kDontExportSome };
- enum LibrarySearchMode { kSearchDylibAndArchiveInEachDir, kSearchAllDirsForDylibsThenAllDirsForArchives };
- enum InterposeMode { kInterposeNone, kInterposeAllExternal, kInterposeSome };
-
- class SetWithWildcards {
- public:
- void insert(const char*);
- bool contains(const char*);
- bool hasWildCards() { return !fWildCard.empty(); }
- NameSet::iterator regularBegin() { return fRegular.begin(); }
- NameSet::iterator regularEnd() { return fRegular.end(); }
- private:
- static bool hasWildCards(const char*);
- bool wildCardMatch(const char* pattern, const char* candidate);
- bool inCharRange(const char*& range, unsigned char c);
-
- NameSet fRegular;
- std::vector<const char*> fWildCard;
- };
-
-
- void parse(int argc, const char* argv[]);
- void checkIllegalOptionCombinations();
- void buildSearchPaths(int argc, const char* argv[]);
- void parseArch(const char* architecture);
- FileInfo findLibrary(const char* rootName, bool dylibsOnly=false);
- FileInfo findFramework(const char* frameworkName);
- FileInfo findFramework(const char* rootName, const char* suffix);
- bool checkForFile(const char* format, const char* dir, const char* rootName,
- FileInfo& result);
- uint32_t parseVersionNumber(const char*);
- void parseSectionOrderFile(const char* segment, const char* section, const char* path);
- void parseOrderFile(const char* path, bool cstring);
- void addSection(const char* segment, const char* section, const char* path);
- void addSubLibrary(const char* name);
- void loadFileList(const char* fileOfPaths);
- uint64_t parseAddress(const char* addr);
- void loadExportFile(const char* fileOfExports, const char* option, SetWithWildcards& set);
- void parseAliasFile(const char* fileOfAliases);
- void parsePreCommandLineEnvironmentSettings();
- void parsePostCommandLineEnvironmentSettings();
- void setUndefinedTreatment(const char* treatment);
- void setMacOSXVersionMin(const char* version);
- void setIPhoneVersionMin(const char* version);
- void setWeakReferenceMismatchTreatment(const char* treatment);
- void addDylibOverride(const char* paths);
- void addSectionAlignment(const char* segment, const char* section, const char* alignment);
- CommonsMode parseCommonsTreatment(const char* mode);
- Treatment parseTreatment(const char* treatment);
- void reconfigureDefaults();
- void checkForClassic(int argc, const char* argv[]);
- void parseSegAddrTable(const char* segAddrPath, const char* installPath);
- void addLibrary(const FileInfo& info);
- void warnObsolete(const char* arg);
- uint32_t parseProtection(const char* prot);
-
-
- ObjectFile::ReaderOptions fReaderOptions;
- const char* fOutputFile;
- std::vector<Options::FileInfo> fInputFiles;
- cpu_type_t fArchitecture;
- cpu_subtype_t fSubArchitecture;
- OutputKind fOutputKind;
- bool fHasPreferredSubType;
- bool fPrebind;
- bool fBindAtLoad;
- bool fKeepPrivateExterns;
- bool fNeedsModuleTable;
- bool fIgnoreOtherArchFiles;
- bool fForceSubtypeAll;
- InterposeMode fInterposeMode;
- DeadStripMode fDeadStrip;
- NameSpace fNameSpace;
- uint32_t fDylibCompatVersion;
- uint32_t fDylibCurrentVersion;
- const char* fDylibInstallName;
- const char* fFinalName;
- const char* fEntryName;
- uint64_t fBaseAddress;
- uint64_t fBaseWritableAddress;
- bool fSplitSegs;
- SetWithWildcards fExportSymbols;
- SetWithWildcards fDontExportSymbols;
- SetWithWildcards fInterposeList;
- ExportMode fExportMode;
- LibrarySearchMode fLibrarySearchMode;
- UndefinedTreatment fUndefinedTreatment;
- bool fMessagesPrefixedWithArchitecture;
- WeakReferenceMismatchTreatment fWeakReferenceMismatchTreatment;
- std::vector<const char*> fSubUmbellas;
- std::vector<const char*> fSubLibraries;
- std::vector<const char*> fAllowableClients;
- std::vector<const char*> fRPaths;
- const char* fClientName;
- const char* fUmbrellaName;
- const char* fInitFunctionName;
- const char* fDotOutputFile;
- const char* fExecutablePath;
- const char* fBundleLoader;
- const char* fDtraceScriptName;
- const char* fSegAddrTablePath;
- const char* fMapPath;
- uint64_t fZeroPageSize;
- uint64_t fStackSize;
- uint64_t fStackAddr;
- bool fExecutableStack;
- uint32_t fMinimumHeaderPad;
- CommonsMode fCommonsMode;
- UUIDMode fUUIDMode;
- SetWithWildcards fLocalSymbolsIncluded;
- SetWithWildcards fLocalSymbolsExcluded;
- LocalSymbolHandling fLocalSymbolHandling;
- bool fWarnCommons;
- bool fVerbose;
- bool fKeepRelocations;
- bool fWarnStabs;
- bool fTraceDylibSearching;
- bool fPause;
- bool fStatistics;
- bool fPrintOptions;
- bool fSharedRegionEligible;
- bool fPrintOrderFileStatistics;
- bool fReadOnlyx86Stubs;
- bool fPositionIndependentExecutable;
- bool fMaxMinimumHeaderPad;
- bool fDeadStripDylibs;
- bool fAllowTextRelocs;
- bool fWarnTextRelocs;
- bool fUsingLazyDylibLinking;
- bool fEncryptable;
- std::vector<const char*> fInitialUndefines;
- NameSet fAllowedUndefined;
- NameSet fWhyLive;
- std::vector<ExtraSection> fExtraSections;
- std::vector<SectionAlignment> fSectionAlignments;
- std::vector<OrderedSymbol> fOrderedSymbols;
- std::vector<SegmentStart> fCustomSegmentAddresses;
- std::vector<SegmentProtect> fCustomSegmentProtections;
- std::vector<DylibOverride> fDylibOverrides;
-
- std::vector<const char*> fLibrarySearchPaths;
- std::vector<const char*> fFrameworkSearchPaths;
- std::vector<const char*> fSDKPaths;
- bool fSaveTempFiles;
-};
-
-
-
-#endif // __OPTIONS__
+++ /dev/null
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
- *
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-
-#ifndef __SECTCREATE__
-#define __SECTCREATE__
-
-
-#include "ObjectFile.h"
-
-namespace SectCreate {
-
- extern ObjectFile::Reader* MakeReader(const char* segmentName, const char* sectionName, const char* path, const uint8_t fileContent[], uint64_t fileLength);
-
-};
-
-
-#endif
-
-
-
-
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#ifndef __FILE_ABSTRACTION__
+#define __FILE_ABSTRACTION__
+
+
+#include <stdint.h>
+#include <string.h>
+#include <libkern/OSByteOrder.h>
+
+#ifdef __OPTIMIZE__
+#define INLINE __attribute__((always_inline))
+#else
+#define INLINE
+#endif
+
+//
+// This abstraction layer is for use with file formats that have 64-bit/32-bit and Big-Endian/Little-Endian variants
+//
+// For example: to make a utility that handles 32-bit little enidan files use: Pointer32<LittleEndian>
+//
+//
+// get16() read a 16-bit number from an E endian struct
+// set16() write a 16-bit number to an E endian struct
+// get32() read a 32-bit number from an E endian struct
+// set32() write a 32-bit number to an E endian struct
+// get64() read a 64-bit number from an E endian struct
+// set64() write a 64-bit number to an E endian struct
+//
+// getBits() read a bit field from an E endian struct (bitCount=number of bits in field, firstBit=bit index of field)
+// setBits() write a bit field to an E endian struct (bitCount=number of bits in field, firstBit=bit index of field)
+//
+// getBitsRaw() read a bit field from a struct with native endianness
+// setBitsRaw() write a bit field from a struct with native endianness
+//
+
+class BigEndian
+{
+public:
+ static uint16_t get16(const uint16_t& from) INLINE { return OSReadBigInt16(&from, 0); }
+ static void set16(uint16_t& into, uint16_t value) INLINE { OSWriteBigInt16(&into, 0, value); }
+
+ static uint32_t get32(const uint32_t& from) INLINE { return OSReadBigInt32(&from, 0); }
+ static void set32(uint32_t& into, uint32_t value) INLINE { OSWriteBigInt32(&into, 0, value); }
+
+ static uint64_t get64(const uint64_t& from) INLINE { return OSReadBigInt64(&from, 0); }
+ static void set64(uint64_t& into, uint64_t value) INLINE { OSWriteBigInt64(&into, 0, value); }
+
+ static uint32_t getBits(const uint32_t& from,
+ uint8_t firstBit, uint8_t bitCount) INLINE { return getBitsRaw(get32(from), firstBit, bitCount); }
+ static void setBits(uint32_t& into, uint32_t value,
+ uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = get32(into); setBitsRaw(temp, value, firstBit, bitCount); set32(into, temp); }
+
+ static uint32_t getBitsRaw(const uint32_t& from,
+ uint8_t firstBit, uint8_t bitCount) INLINE { return ((from >> (32-firstBit-bitCount)) & ((1<<bitCount)-1)); }
+ static void setBitsRaw(uint32_t& into, uint32_t value,
+ uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = into;
+ const uint32_t mask = ((1<<bitCount)-1);
+ temp &= ~(mask << (32-firstBit-bitCount));
+ temp |= ((value & mask) << (32-firstBit-bitCount));
+ into = temp; }
+ enum { little_endian = 0 };
+};
+
+
+class LittleEndian
+{
+public:
+ static uint16_t get16(const uint16_t& from) INLINE { return OSReadLittleInt16(&from, 0); }
+ static void set16(uint16_t& into, uint16_t value) INLINE { OSWriteLittleInt16(&into, 0, value); }
+
+ static uint32_t get32(const uint32_t& from) INLINE { return OSReadLittleInt32(&from, 0); }
+ static void set32(uint32_t& into, uint32_t value) INLINE { OSWriteLittleInt32(&into, 0, value); }
+
+ static uint64_t get64(const uint64_t& from) INLINE { return OSReadLittleInt64(&from, 0); }
+ static void set64(uint64_t& into, uint64_t value) INLINE { OSWriteLittleInt64(&into, 0, value); }
+
+ static uint32_t getBits(const uint32_t& from,
+ uint8_t firstBit, uint8_t bitCount) INLINE { return getBitsRaw(get32(from), firstBit, bitCount); }
+ static void setBits(uint32_t& into, uint32_t value,
+ uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = get32(into); setBitsRaw(temp, value, firstBit, bitCount); set32(into, temp); }
+
+ static uint32_t getBitsRaw(const uint32_t& from,
+ uint8_t firstBit, uint8_t bitCount) INLINE { return ((from >> firstBit) & ((1<<bitCount)-1)); }
+ static void setBitsRaw(uint32_t& into, uint32_t value,
+ uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = into;
+ const uint32_t mask = ((1<<bitCount)-1);
+ temp &= ~(mask << firstBit);
+ temp |= ((value & mask) << firstBit);
+ into = temp; }
+ enum { little_endian = 1 };
+};
+
+
+template <typename _E>
+class Pointer32
+{
+public:
+ typedef uint32_t uint_t;
+ typedef _E E;
+
+ static uint64_t getP(const uint_t& from) INLINE { return _E::get32(from); }
+ static void setP(uint_t& into, uint64_t value) INLINE { _E::set32(into, value); }
+};
+
+
+template <typename _E>
+class Pointer64
+{
+public:
+ typedef uint64_t uint_t;
+ typedef _E E;
+
+ static uint64_t getP(const uint_t& from) INLINE { return _E::get64(from); }
+ static void setP(uint_t& into, uint64_t value) INLINE { _E::set64(into, value); }
+};
+
+
+
+
+
+#endif // __FILE_ABSTRACTION__
+
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2005-2008 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+*/
+#ifndef __MACH_O_FILE_ABSTRACTION__
+#define __MACH_O_FILE_ABSTRACTION__
+
+#include <mach-o/loader.h>
+#include <mach-o/nlist.h>
+#include <mach-o/reloc.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 <mach-o/arm/reloc.h>
+#include <mach-o/compact_unwind_encoding.h>
+#include <mach/machine.h>
+
+#include "FileAbstraction.hpp"
+
+
+// stuff that will eventually go away once newer cctools headers are widespread
+#ifndef CPU_SUBTYPE_ARM_V5TEJ
+ #define CPU_SUBTYPE_ARM_V5TEJ ((cpu_subtype_t) 7)
+#endif
+#ifndef CPU_SUBTYPE_ARM_XSCALE
+ #define CPU_SUBTYPE_ARM_XSCALE ((cpu_subtype_t) 8)
+#endif
+#ifndef CPU_SUBTYPE_ARM_V7
+ #define CPU_SUBTYPE_ARM_V7 ((cpu_subtype_t) 9)
+#endif
+#ifndef ARM_THUMB_32BIT_BRANCH
+ #define ARM_THUMB_32BIT_BRANCH 7
+#endif
+#ifndef N_ARM_THUMB_DEF
+ #define N_ARM_THUMB_DEF 0x0008
+#endif
+#ifndef MH_DEAD_STRIPPABLE_DYLIB
+ #define MH_DEAD_STRIPPABLE_DYLIB 0x400000
+#endif
+#ifndef MH_KEXT_BUNDLE
+ #define MH_KEXT_BUNDLE 11
+#endif
+#ifndef LC_DYLD_INFO
+ #define LC_DYLD_INFO 0x22 /* compressed dyld information */
+ #define LC_DYLD_INFO_ONLY (0x22|LC_REQ_DYLD) /* compressed dyld information only */
+
+ struct dyld_info_command {
+ uint32_t cmd; /* LC_DYLD_INFO or LC_DYLD_INFO_ONLY */
+ uint32_t cmdsize; /* sizeof(struct dyld_info_command) */
+ uint32_t rebase_off; /* file offset to rebase info */
+ uint32_t rebase_size; /* size of rebase info */
+ uint32_t bind_off; /* file offset to binding info */
+ uint32_t bind_size; /* size of binding info */
+ uint32_t weak_bind_off; /* file offset to weak binding info */
+ uint32_t weak_bind_size; /* size of weak binding info */
+ uint32_t lazy_bind_off; /* file offset to lazy binding info */
+ uint32_t lazy_bind_size; /* size of lazy binding infs */
+ uint32_t export_off; /* file offset to lazy binding info */
+ uint32_t export_size; /* size of lazy binding infs */
+ };
+
+ #define REBASE_TYPE_POINTER 1
+ #define REBASE_TYPE_TEXT_ABSOLUTE32 2
+ #define REBASE_TYPE_TEXT_PCREL32 3
+
+ #define REBASE_OPCODE_MASK 0xF0
+ #define REBASE_IMMEDIATE_MASK 0x0F
+ #define REBASE_OPCODE_DONE 0x00
+ #define REBASE_OPCODE_SET_TYPE_IMM 0x10
+ #define REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB 0x20
+ #define REBASE_OPCODE_ADD_ADDR_ULEB 0x30
+ #define REBASE_OPCODE_ADD_ADDR_IMM_SCALED 0x40
+ #define REBASE_OPCODE_DO_REBASE_IMM_TIMES 0x50
+ #define REBASE_OPCODE_DO_REBASE_ULEB_TIMES 0x60
+ #define REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB 0x70
+ #define REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB 0x80
+
+ #define BIND_TYPE_POINTER 1
+ #define BIND_TYPE_TEXT_ABSOLUTE32 2
+ #define BIND_TYPE_TEXT_PCREL32 3
+
+ #define BIND_SPECIAL_DYLIB_SELF 0
+ #define BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE -1
+ #define BIND_SPECIAL_DYLIB_FLAT_LOOKUP -2
+
+ #define BIND_SYMBOL_FLAGS_WEAK_IMPORT 0x1
+ #define BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION 0x8
+
+ #define BIND_OPCODE_MASK 0xF0
+ #define BIND_IMMEDIATE_MASK 0x0F
+ #define BIND_OPCODE_DONE 0x00
+ #define BIND_OPCODE_SET_DYLIB_ORDINAL_IMM 0x10
+ #define BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB 0x20
+ #define BIND_OPCODE_SET_DYLIB_SPECIAL_IMM 0x30
+ #define BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM 0x40
+ #define BIND_OPCODE_SET_TYPE_IMM 0x50
+ #define BIND_OPCODE_SET_ADDEND_SLEB 0x60
+ #define BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB 0x70
+ #define BIND_OPCODE_ADD_ADDR_ULEB 0x80
+ #define BIND_OPCODE_DO_BIND 0x90
+ #define BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB 0xA0
+ #define BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED 0xB0
+ #define BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB 0xC0
+
+ #define EXPORT_SYMBOL_FLAGS_KIND_MASK 0x03
+ #define EXPORT_SYMBOL_FLAGS_KIND_REGULAR 0x00
+ #define EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL 0x01
+ #define EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION 0x04
+ #define EXPORT_SYMBOL_FLAGS_INDIRECT_DEFINITION 0x08
+ #define EXPORT_SYMBOL_FLAGS_HAS_SPECIALIZATIONS 0x10
+
+#endif
+
+
+//
+// This abstraction layer makes every mach-o file look like a 64-bit mach-o file with native endianness
+//
+
+
+
+//
+// mach-o file header
+//
+template <typename P> struct macho_header_content {};
+template <> struct macho_header_content<Pointer32<BigEndian> > { mach_header fields; };
+template <> struct macho_header_content<Pointer64<BigEndian> > { mach_header_64 fields; };
+template <> struct macho_header_content<Pointer32<LittleEndian> > { mach_header fields; };
+template <> struct macho_header_content<Pointer64<LittleEndian> > { mach_header_64 fields; };
+
+template <typename P>
+class macho_header {
+public:
+ uint32_t magic() const INLINE { return E::get32(header.fields.magic); }
+ void set_magic(uint32_t value) INLINE { E::set32(header.fields.magic, value); }
+
+ uint32_t cputype() const INLINE { return E::get32(header.fields.cputype); }
+ void set_cputype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cputype, value); }
+
+ uint32_t cpusubtype() const INLINE { return E::get32(header.fields.cpusubtype); }
+ void set_cpusubtype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cpusubtype, value); }
+
+ uint32_t filetype() const INLINE { return E::get32(header.fields.filetype); }
+ void set_filetype(uint32_t value) INLINE { E::set32(header.fields.filetype, value); }
+
+ uint32_t ncmds() const INLINE { return E::get32(header.fields.ncmds); }
+ void set_ncmds(uint32_t value) INLINE { E::set32(header.fields.ncmds, value); }
+
+ uint32_t sizeofcmds() const INLINE { return E::get32(header.fields.sizeofcmds); }
+ void set_sizeofcmds(uint32_t value) INLINE { E::set32(header.fields.sizeofcmds, value); }
+
+ uint32_t flags() const INLINE { return E::get32(header.fields.flags); }
+ void set_flags(uint32_t value) INLINE { E::set32(header.fields.flags, value); }
+
+ uint32_t reserved() const INLINE { return E::get32(header.fields.reserved); }
+ void set_reserved(uint32_t value) INLINE { E::set32(header.fields.reserved, value); }
+
+ typedef typename P::E E;
+private:
+ macho_header_content<P> header;
+};
+
+
+//
+// mach-o load command
+//
+template <typename P>
+class macho_load_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(command.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(command.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(command.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(command.cmdsize, value); }
+
+ typedef typename P::E E;
+private:
+ load_command command;
+};
+
+
+//
+// mach-o segment load command
+//
+template <typename P> struct macho_segment_content {};
+template <> struct macho_segment_content<Pointer32<BigEndian> > { segment_command fields; enum { CMD = LC_SEGMENT }; };
+template <> struct macho_segment_content<Pointer64<BigEndian> > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; };
+template <> struct macho_segment_content<Pointer32<LittleEndian> > { segment_command fields; enum { CMD = LC_SEGMENT }; };
+template <> struct macho_segment_content<Pointer64<LittleEndian> > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; };
+
+template <typename P>
+class macho_segment_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(segment.fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(segment.fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(segment.fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(segment.fields.cmdsize, value); }
+
+ const char* segname() const INLINE { return segment.fields.segname; }
+ void set_segname(const char* value) INLINE { 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); }
+
+ uint64_t vmsize() const INLINE { return P::getP(segment.fields.vmsize); }
+ void set_vmsize(uint64_t value) INLINE { P::setP(segment.fields.vmsize, value); }
+
+ uint64_t fileoff() const INLINE { return P::getP(segment.fields.fileoff); }
+ void set_fileoff(uint64_t value) INLINE { P::setP(segment.fields.fileoff, value); }
+
+ uint64_t filesize() const INLINE { return P::getP(segment.fields.filesize); }
+ void set_filesize(uint64_t value) INLINE { P::setP(segment.fields.filesize, value); }
+
+ uint32_t maxprot() const INLINE { return E::get32(segment.fields.maxprot); }
+ void set_maxprot(uint32_t value) INLINE { E::set32((uint32_t&)segment.fields.maxprot, value); }
+
+ uint32_t initprot() const INLINE { return E::get32(segment.fields.initprot); }
+ void set_initprot(uint32_t value) INLINE { E::set32((uint32_t&)segment.fields.initprot, value); }
+
+ uint32_t nsects() const INLINE { return E::get32(segment.fields.nsects); }
+ void set_nsects(uint32_t value) INLINE { E::set32(segment.fields.nsects, value); }
+
+ uint32_t flags() const INLINE { return E::get32(segment.fields.flags); }
+ void set_flags(uint32_t value) INLINE { E::set32(segment.fields.flags, value); }
+
+ enum {
+ CMD = macho_segment_content<P>::CMD
+ };
+
+ typedef typename P::E E;
+private:
+ macho_segment_content<P> segment;
+};
+
+
+//
+// mach-o section
+//
+template <typename P> struct macho_section_content {};
+template <> struct macho_section_content<Pointer32<BigEndian> > { section fields; };
+template <> struct macho_section_content<Pointer64<BigEndian> > { section_64 fields; };
+template <> struct macho_section_content<Pointer32<LittleEndian> > { section fields; };
+template <> struct macho_section_content<Pointer64<LittleEndian> > { section_64 fields; };
+
+template <typename P>
+class macho_section {
+public:
+ const char* sectname() const INLINE { return section.fields.sectname; }
+ 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 { 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); }
+
+ uint64_t size() const INLINE { return P::getP(section.fields.size); }
+ void set_size(uint64_t value) INLINE { P::setP(section.fields.size, value); }
+
+ uint32_t offset() const INLINE { return E::get32(section.fields.offset); }
+ void set_offset(uint32_t value) INLINE { E::set32(section.fields.offset, value); }
+
+ uint32_t align() const INLINE { return E::get32(section.fields.align); }
+ void set_align(uint32_t value) INLINE { E::set32(section.fields.align, value); }
+
+ uint32_t reloff() const INLINE { return E::get32(section.fields.reloff); }
+ void set_reloff(uint32_t value) INLINE { E::set32(section.fields.reloff, value); }
+
+ uint32_t nreloc() const INLINE { return E::get32(section.fields.nreloc); }
+ void set_nreloc(uint32_t value) INLINE { E::set32(section.fields.nreloc, value); }
+
+ uint32_t flags() const INLINE { return E::get32(section.fields.flags); }
+ void set_flags(uint32_t value) INLINE { E::set32(section.fields.flags, value); }
+
+ uint32_t reserved1() const INLINE { return E::get32(section.fields.reserved1); }
+ void set_reserved1(uint32_t value) INLINE { E::set32(section.fields.reserved1, value); }
+
+ uint32_t reserved2() const INLINE { return E::get32(section.fields.reserved2); }
+ void set_reserved2(uint32_t value) INLINE { E::set32(section.fields.reserved2, value); }
+
+ typedef typename P::E E;
+private:
+ macho_section_content<P> section;
+};
+
+
+//
+// mach-o dylib load command
+//
+template <typename P>
+class macho_dylib_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
+
+ uint32_t name_offset() const INLINE { return E::get32(fields.dylib.name.offset); }
+ void set_name_offset(uint32_t value) INLINE { E::set32(fields.dylib.name.offset, value); }
+
+ uint32_t timestamp() const INLINE { return E::get32(fields.dylib.timestamp); }
+ void set_timestamp(uint32_t value) INLINE { E::set32(fields.dylib.timestamp, value); }
+
+ uint32_t current_version() const INLINE { return E::get32(fields.dylib.current_version); }
+ void set_current_version(uint32_t value) INLINE { E::set32(fields.dylib.current_version, value); }
+
+ uint32_t compatibility_version() const INLINE { return E::get32(fields.dylib.compatibility_version); }
+ void set_compatibility_version(uint32_t value) INLINE { E::set32(fields.dylib.compatibility_version, value); }
+
+ const char* name() const INLINE { return (const char*)&fields + name_offset(); }
+ void set_name_offset() INLINE { set_name_offset(sizeof(fields)); }
+
+ typedef typename P::E E;
+private:
+ dylib_command fields;
+};
+
+
+//
+// mach-o dylinker load command
+//
+template <typename P>
+class macho_dylinker_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
+
+ uint32_t name_offset() const INLINE { return E::get32(fields.name.offset); }
+ void set_name_offset(uint32_t value) INLINE { E::set32(fields.name.offset, value); }
+
+ const char* name() const INLINE { return (const char*)&fields + name_offset(); }
+ void set_name_offset() INLINE { set_name_offset(sizeof(fields)); }
+
+ typedef typename P::E E;
+private:
+ dylinker_command fields;
+};
+
+
+//
+// mach-o sub_framework load command
+//
+template <typename P>
+class macho_sub_framework_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
+
+ uint32_t umbrella_offset() const INLINE { return E::get32(fields.umbrella.offset); }
+ void set_umbrella_offset(uint32_t value) INLINE { E::set32(fields.umbrella.offset, value); }
+
+ const char* umbrella() const INLINE { return (const char*)&fields + umbrella_offset(); }
+ void set_umbrella_offset() INLINE { set_umbrella_offset(sizeof(fields)); }
+
+ typedef typename P::E E;
+private:
+ sub_framework_command fields;
+};
+
+
+//
+// mach-o sub_client load command
+//
+template <typename P>
+class macho_sub_client_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
+
+ uint32_t client_offset() const INLINE { return E::get32(fields.client.offset); }
+ void set_client_offset(uint32_t value) INLINE { E::set32(fields.client.offset, value); }
+
+ const char* client() const INLINE { return (const char*)&fields + client_offset(); }
+ void set_client_offset() INLINE { set_client_offset(sizeof(fields)); }
+
+ typedef typename P::E E;
+private:
+ sub_client_command fields;
+};
+
+
+//
+// mach-o sub_umbrella load command
+//
+template <typename P>
+class macho_sub_umbrella_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
+
+ uint32_t sub_umbrella_offset() const INLINE { return E::get32(fields.sub_umbrella.offset); }
+ void set_sub_umbrella_offset(uint32_t value) INLINE { E::set32(fields.sub_umbrella.offset, value); }
+
+ const char* sub_umbrella() const INLINE { return (const char*)&fields + sub_umbrella_offset(); }
+ void set_sub_umbrella_offset() INLINE { set_sub_umbrella_offset(sizeof(fields)); }
+
+ typedef typename P::E E;
+private:
+ sub_umbrella_command fields;
+};
+
+
+//
+// mach-o sub_library load command
+//
+template <typename P>
+class macho_sub_library_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
+
+ uint32_t sub_library_offset() const INLINE { return E::get32(fields.sub_library.offset); }
+ void set_sub_library_offset(uint32_t value) INLINE { E::set32(fields.sub_library.offset, value); }
+
+ const char* sub_library() const INLINE { return (const char*)&fields + sub_library_offset(); }
+ void set_sub_library_offset() INLINE { set_sub_library_offset(sizeof(fields)); }
+
+ typedef typename P::E E;
+private:
+ sub_library_command fields;
+};
+
+
+//
+// mach-o uuid load command
+//
+template <typename P>
+class macho_uuid_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
+
+ const uint8_t* uuid() const INLINE { return fields.uuid; }
+ void set_uuid(uint8_t uuid[16]) INLINE { memcpy(&fields.uuid, uuid, 16); }
+
+ typedef typename P::E E;
+private:
+ uuid_command fields;
+};
+
+
+//
+// mach-o routines load command
+//
+template <typename P> struct macho_routines_content {};
+template <> struct macho_routines_content<Pointer32<BigEndian> > { routines_command fields; enum { CMD = LC_ROUTINES }; };
+template <> struct macho_routines_content<Pointer64<BigEndian> > { routines_command_64 fields; enum { CMD = LC_ROUTINES_64 }; };
+template <> struct macho_routines_content<Pointer32<LittleEndian> > { routines_command fields; enum { CMD = LC_ROUTINES }; };
+template <> struct macho_routines_content<Pointer64<LittleEndian> > { routines_command_64 fields; enum { CMD = LC_ROUTINES_64 }; };
+
+template <typename P>
+class macho_routines_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(routines.fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(routines.fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(routines.fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(routines.fields.cmdsize, value); }
+
+ uint64_t init_address() const INLINE { return P::getP(routines.fields.init_address); }
+ void set_init_address(uint64_t value) INLINE { P::setP(routines.fields.init_address, value); }
+
+ uint64_t init_module() const INLINE { return P::getP(routines.fields.init_module); }
+ void set_init_module(uint64_t value) INLINE { P::setP(routines.fields.init_module, value); }
+
+ uint64_t reserved1() const INLINE { return P::getP(routines.fields.reserved1); }
+ void set_reserved1(uint64_t value) INLINE { P::setP(routines.fields.reserved1, value); }
+
+ uint64_t reserved2() const INLINE { return P::getP(routines.fields.reserved2); }
+ void set_reserved2(uint64_t value) INLINE { P::setP(routines.fields.reserved2, value); }
+
+ uint64_t reserved3() const INLINE { return P::getP(routines.fields.reserved3); }
+ void set_reserved3(uint64_t value) INLINE { P::setP(routines.fields.reserved3, value); }
+
+ uint64_t reserved4() const INLINE { return P::getP(routines.fields.reserved4); }
+ void set_reserved4(uint64_t value) INLINE { P::setP(routines.fields.reserved4, value); }
+
+ uint64_t reserved5() const INLINE { return P::getP(routines.fields.reserved5); }
+ void set_reserved5(uint64_t value) INLINE { P::setP(routines.fields.reserved5, value); }
+
+ uint64_t reserved6() const INLINE { return P::getP(routines.fields.reserved6); }
+ void set_reserved6(uint64_t value) INLINE { P::setP(routines.fields.reserved6, value); }
+
+ typedef typename P::E E;
+ enum {
+ CMD = macho_routines_content<P>::CMD
+ };
+private:
+ macho_routines_content<P> routines;
+};
+
+
+//
+// mach-o symbol table load command
+//
+template <typename P>
+class macho_symtab_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
+
+ uint32_t symoff() const INLINE { return E::get32(fields.symoff); }
+ void set_symoff(uint32_t value) INLINE { E::set32(fields.symoff, value); }
+
+ uint32_t nsyms() const INLINE { return E::get32(fields.nsyms); }
+ void set_nsyms(uint32_t value) INLINE { E::set32(fields.nsyms, value); }
+
+ uint32_t stroff() const INLINE { return E::get32(fields.stroff); }
+ void set_stroff(uint32_t value) INLINE { E::set32(fields.stroff, value); }
+
+ uint32_t strsize() const INLINE { return E::get32(fields.strsize); }
+ void set_strsize(uint32_t value) INLINE { E::set32(fields.strsize, value); }
+
+
+ typedef typename P::E E;
+private:
+ symtab_command fields;
+};
+
+
+//
+// mach-o dynamic symbol table load command
+//
+template <typename P>
+class macho_dysymtab_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
+
+ uint32_t ilocalsym() const INLINE { return E::get32(fields.ilocalsym); }
+ void set_ilocalsym(uint32_t value) INLINE { E::set32(fields.ilocalsym, value); }
+
+ uint32_t nlocalsym() const INLINE { return E::get32(fields.nlocalsym); }
+ void set_nlocalsym(uint32_t value) INLINE { E::set32(fields.nlocalsym, value); }
+
+ uint32_t iextdefsym() const INLINE { return E::get32(fields.iextdefsym); }
+ void set_iextdefsym(uint32_t value) INLINE { E::set32(fields.iextdefsym, value); }
+
+ uint32_t nextdefsym() const INLINE { return E::get32(fields.nextdefsym); }
+ void set_nextdefsym(uint32_t value) INLINE { E::set32(fields.nextdefsym, value); }
+
+ uint32_t iundefsym() const INLINE { return E::get32(fields.iundefsym); }
+ void set_iundefsym(uint32_t value) INLINE { E::set32(fields.iundefsym, value); }
+
+ uint32_t nundefsym() const INLINE { return E::get32(fields.nundefsym); }
+ void set_nundefsym(uint32_t value) INLINE { E::set32(fields.nundefsym, value); }
+
+ uint32_t tocoff() const INLINE { return E::get32(fields.tocoff); }
+ void set_tocoff(uint32_t value) INLINE { E::set32(fields.tocoff, value); }
+
+ uint32_t ntoc() const INLINE { return E::get32(fields.ntoc); }
+ void set_ntoc(uint32_t value) INLINE { E::set32(fields.ntoc, value); }
+
+ uint32_t modtaboff() const INLINE { return E::get32(fields.modtaboff); }
+ void set_modtaboff(uint32_t value) INLINE { E::set32(fields.modtaboff, value); }
+
+ uint32_t nmodtab() const INLINE { return E::get32(fields.nmodtab); }
+ void set_nmodtab(uint32_t value) INLINE { E::set32(fields.nmodtab, value); }
+
+ uint32_t extrefsymoff() const INLINE { return E::get32(fields.extrefsymoff); }
+ void set_extrefsymoff(uint32_t value) INLINE { E::set32(fields.extrefsymoff, value); }
+
+ uint32_t nextrefsyms() const INLINE { return E::get32(fields.nextrefsyms); }
+ void set_nextrefsyms(uint32_t value) INLINE { E::set32(fields.nextrefsyms, value); }
+
+ uint32_t indirectsymoff() const INLINE { return E::get32(fields.indirectsymoff); }
+ void set_indirectsymoff(uint32_t value) INLINE { E::set32(fields.indirectsymoff, value); }
+
+ uint32_t nindirectsyms() const INLINE { return E::get32(fields.nindirectsyms); }
+ void set_nindirectsyms(uint32_t value) INLINE { E::set32(fields.nindirectsyms, value); }
+
+ uint32_t extreloff() const INLINE { return E::get32(fields.extreloff); }
+ void set_extreloff(uint32_t value) INLINE { E::set32(fields.extreloff, value); }
+
+ uint32_t nextrel() const INLINE { return E::get32(fields.nextrel); }
+ void set_nextrel(uint32_t value) INLINE { E::set32(fields.nextrel, value); }
+
+ uint32_t locreloff() const INLINE { return E::get32(fields.locreloff); }
+ void set_locreloff(uint32_t value) INLINE { E::set32(fields.locreloff, value); }
+
+ uint32_t nlocrel() const INLINE { return E::get32(fields.nlocrel); }
+ void set_nlocrel(uint32_t value) INLINE { E::set32(fields.nlocrel, value); }
+
+ typedef typename P::E E;
+private:
+ dysymtab_command fields;
+};
+
+
+
+
+//
+// mach-o module table entry (for compatibility with old ld/dyld)
+//
+template <typename P> struct macho_dylib_module_content {};
+template <> struct macho_dylib_module_content<Pointer32<BigEndian> > { struct dylib_module fields; };
+template <> struct macho_dylib_module_content<Pointer32<LittleEndian> > { struct dylib_module fields; };
+template <> struct macho_dylib_module_content<Pointer64<BigEndian> > { struct dylib_module_64 fields; };
+template <> struct macho_dylib_module_content<Pointer64<LittleEndian> > { struct dylib_module_64 fields; };
+
+template <typename P>
+class macho_dylib_module {
+public:
+ uint32_t module_name() const INLINE { return E::get32(module.fields.module_name); }
+ void set_module_name(uint32_t value) INLINE { E::set32(module.fields.module_name, value); }
+
+ uint32_t iextdefsym() const INLINE { return E::get32(module.fields.iextdefsym); }
+ void set_iextdefsym(uint32_t value) INLINE { E::set32(module.fields.iextdefsym, value); }
+
+ uint32_t nextdefsym() const INLINE { return E::get32(module.fields.nextdefsym); }
+ void set_nextdefsym(uint32_t value) INLINE { E::set32(module.fields.nextdefsym, value); }
+
+ uint32_t irefsym() const INLINE { return E::get32(module.fields.irefsym); }
+ void set_irefsym(uint32_t value) INLINE { E::set32(module.fields.irefsym, value); }
+
+ uint32_t nrefsym() const INLINE { return E::get32(module.fields.nrefsym); }
+ void set_nrefsym(uint32_t value) INLINE { E::set32(module.fields.nrefsym, value); }
+
+ uint32_t ilocalsym() const INLINE { return E::get32(module.fields.ilocalsym); }
+ void set_ilocalsym(uint32_t value) INLINE { E::set32(module.fields.ilocalsym, value); }
+
+ uint32_t nlocalsym() const INLINE { return E::get32(module.fields.nlocalsym); }
+ void set_nlocalsym(uint32_t value) INLINE { E::set32(module.fields.nlocalsym, value); }
+
+ uint32_t iextrel() const INLINE { return E::get32(module.fields.iextrel); }
+ void set_iextrel(uint32_t value) INLINE { E::set32(module.fields.iextrel, value); }
+
+ uint32_t nextrel() const INLINE { return E::get32(module.fields.nextrel); }
+ void set_nextrel(uint32_t value) INLINE { E::set32(module.fields.nextrel, value); }
+
+ uint16_t iinit() const INLINE { return E::get32(module.fields.iinit_iterm) & 0xFFFF; }
+ uint16_t iterm() const INLINE { return E::get32(module.fields.iinit_iterm) > 16; }
+ void set_iinit_iterm(uint16_t init, uint16_t term) INLINE { E::set32(module.fields.iinit_iterm, (term<<16) | (init &0xFFFF)); }
+
+ uint16_t ninit() const INLINE { return E::get32(module.fields.ninit_nterm) & 0xFFFF; }
+ uint16_t nterm() const INLINE { return E::get32(module.fields.ninit_nterm) > 16; }
+ void set_ninit_nterm(uint16_t init, uint16_t term) INLINE { E::set32(module.fields.ninit_nterm, (term<<16) | (init &0xFFFF)); }
+
+ uint64_t objc_module_info_addr() const INLINE { return P::getP(module.fields.objc_module_info_addr); }
+ void set_objc_module_info_addr(uint64_t value) INLINE { P::setP(module.fields.objc_module_info_addr, value); }
+
+ uint32_t objc_module_info_size() const INLINE { return E::get32(module.fields.objc_module_info_size); }
+ void set_objc_module_info_size(uint32_t value) INLINE { E::set32(module.fields.objc_module_info_size, value); }
+
+
+ typedef typename P::E E;
+private:
+ macho_dylib_module_content<P> module;
+};
+
+
+//
+// mach-o dylib_reference entry
+//
+template <typename P>
+class macho_dylib_reference {
+public:
+ uint32_t isym() const INLINE { return E::getBits(fields, 0, 24); }
+ void set_isym(uint32_t value) INLINE { E::setBits(fields, value, 0, 24); }
+
+ uint8_t flags() const INLINE { return E::getBits(fields, 24, 8); }
+ void set_flags(uint8_t value) INLINE { E::setBits(fields, value, 24, 8); }
+
+ typedef typename P::E E;
+private:
+ uint32_t fields;
+};
+
+
+
+//
+// mach-o two-level hints load command
+//
+template <typename P>
+class macho_dylib_table_of_contents {
+public:
+ uint32_t symbol_index() const INLINE { return E::get32(fields.symbol_index); }
+ void set_symbol_index(uint32_t value) INLINE { E::set32(fields.symbol_index, value); }
+
+ uint32_t module_index() const INLINE { return E::get32(fields.module_index); }
+ void set_module_index(uint32_t value) INLINE { E::set32(fields.module_index, value); }
+
+ typedef typename P::E E;
+private:
+ dylib_table_of_contents fields;
+};
+
+
+
+//
+// mach-o two-level hints load command
+//
+template <typename P>
+class macho_twolevel_hints_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
+
+ uint32_t offset() const INLINE { return E::get32(fields.offset); }
+ void set_offset(uint32_t value) INLINE { E::set32(fields.offset, value); }
+
+ uint32_t nhints() const INLINE { return E::get32(fields.nhints); }
+ void set_nhints(uint32_t value) INLINE { E::set32(fields.nhints, value); }
+
+ typedef typename P::E E;
+private:
+ twolevel_hints_command fields;
+};
+
+
+//
+// mach-o threads load command
+//
+template <typename P>
+class macho_thread_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
+
+ uint32_t flavor() const INLINE { return E::get32(fields_flavor); }
+ void set_flavor(uint32_t value) INLINE { E::set32(fields_flavor, value); }
+
+ uint32_t count() const INLINE { return E::get32(fields_count); }
+ void set_count(uint32_t value) INLINE { E::set32(fields_count, value); }
+
+ uint64_t thread_register(uint32_t index) const INLINE { return P::getP(thread_registers[index]); }
+ void set_thread_register(uint32_t index, uint64_t value) INLINE { P::setP(thread_registers[index], value); }
+
+ typedef typename P::E E;
+ typedef typename P::uint_t pint_t;
+private:
+ struct thread_command fields;
+ uint32_t fields_flavor;
+ uint32_t fields_count;
+ pint_t thread_registers[1];
+};
+
+
+//
+// mach-o misc data
+//
+template <typename P>
+class macho_linkedit_data_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
+
+ uint32_t dataoff() const INLINE { return E::get32(fields.dataoff); }
+ void set_dataoff(uint32_t value) INLINE { E::set32(fields.dataoff, value); }
+
+ uint32_t datasize() const INLINE { return E::get32(fields.datasize); }
+ void set_datasize(uint32_t value)INLINE { E::set32(fields.datasize, value); }
+
+
+ typedef typename P::E E;
+private:
+ struct linkedit_data_command fields;
+};
+
+
+//
+// mach-o rpath
+//
+template <typename P>
+class macho_rpath_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
+
+ uint32_t path_offset() const INLINE { return E::get32(fields.path.offset); }
+ void set_path_offset(uint32_t value) INLINE { E::set32(fields.path.offset, value); }
+
+ const char* path() const INLINE { return (const char*)&fields + path_offset(); }
+ void set_path_offset() INLINE { set_path_offset(sizeof(fields)); }
+
+
+ typedef typename P::E E;
+private:
+ struct rpath_command fields;
+};
+
+
+
+//
+// mach-o symbol table entry
+//
+template <typename P> struct macho_nlist_content {};
+template <> struct macho_nlist_content<Pointer32<BigEndian> > { struct nlist fields; };
+template <> struct macho_nlist_content<Pointer64<BigEndian> > { struct nlist_64 fields; };
+template <> struct macho_nlist_content<Pointer32<LittleEndian> > { struct nlist fields; };
+template <> struct macho_nlist_content<Pointer64<LittleEndian> > { struct nlist_64 fields; };
+
+template <typename P>
+class macho_nlist {
+public:
+ uint32_t n_strx() const INLINE { return E::get32(entry.fields.n_un.n_strx); }
+ void set_n_strx(uint32_t value) INLINE { E::set32((uint32_t&)entry.fields.n_un.n_strx, value); }
+
+ uint8_t n_type() const INLINE { return entry.fields.n_type; }
+ void set_n_type(uint8_t value) INLINE { entry.fields.n_type = value; }
+
+ uint8_t n_sect() const INLINE { return entry.fields.n_sect; }
+ void set_n_sect(uint8_t value) INLINE { entry.fields.n_sect = value; }
+
+ uint16_t n_desc() const INLINE { return E::get16(entry.fields.n_desc); }
+ void set_n_desc(uint16_t value) INLINE { E::set16((uint16_t&)entry.fields.n_desc, value); }
+
+ uint64_t n_value() const INLINE { return P::getP(entry.fields.n_value); }
+ void set_n_value(uint64_t value) INLINE { P::setP(entry.fields.n_value, value); }
+
+ typedef typename P::E E;
+private:
+ macho_nlist_content<P> entry;
+};
+
+
+
+//
+// mach-o relocation info
+//
+template <typename P>
+class macho_relocation_info {
+public:
+ uint32_t r_address() const INLINE { return E::get32(address); }
+ void set_r_address(uint32_t value) INLINE { E::set32(address, value); }
+
+ uint32_t r_symbolnum() const INLINE { return E::getBits(other, 0, 24); }
+ void set_r_symbolnum(uint32_t value) INLINE { E::setBits(other, value, 0, 24); }
+
+ bool r_pcrel() const INLINE { return E::getBits(other, 24, 1); }
+ void set_r_pcrel(bool value) INLINE { E::setBits(other, value, 24, 1); }
+
+ uint8_t r_length() const INLINE { return E::getBits(other, 25, 2); }
+ void set_r_length(uint8_t value) INLINE { E::setBits(other, value, 25, 2); }
+
+ bool r_extern() const INLINE { return E::getBits(other, 27, 1); }
+ void set_r_extern(bool value) INLINE { E::setBits(other, value, 27, 1); }
+
+ uint8_t r_type() const INLINE { return E::getBits(other, 28, 4); }
+ void set_r_type(uint8_t value) INLINE { E::setBits(other, value, 28, 4); }
+
+ void set_r_length() INLINE { set_r_length((sizeof(typename P::uint_t)==8) ? 3 : 2); }
+
+ typedef typename P::E E;
+private:
+ uint32_t address;
+ uint32_t other;
+};
+
+
+//
+// mach-o scattered relocation info
+// The bit fields are always in big-endian order (see mach-o/reloc.h)
+//
+template <typename P>
+class macho_scattered_relocation_info {
+public:
+ bool r_scattered() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 0, 1); }
+ void set_r_scattered(bool x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 0, 1); E::set32(other, temp); }
+
+ bool r_pcrel() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 1, 1); }
+ void set_r_pcrel(bool x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 1, 1); E::set32(other, temp); }
+
+ uint8_t r_length() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 2, 2); }
+ void set_r_length(uint8_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 2, 2); E::set32(other, temp); }
+
+ uint8_t r_type() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 4, 4); }
+ void set_r_type(uint8_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 4, 4); E::set32(other, temp); }
+
+ uint32_t r_address() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 8, 24); }
+ void set_r_address(uint32_t x) { if ( x > 0x00FFFFFF ) throw "scattered reloc r_address too large";
+ uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 8, 24); E::set32(other, temp); }
+
+ uint32_t r_value() const INLINE { return E::get32(value); }
+ void set_r_value(uint32_t x) INLINE { E::set32(value, x); }
+
+ uint32_t r_other() const INLINE { return other; }
+
+ void set_r_length() INLINE { set_r_length((sizeof(typename P::uint_t)==8) ? 3 : 2); }
+
+ typedef typename P::E E;
+private:
+ uint32_t other;
+ uint32_t value;
+};
+
+
+
+//
+// mach-o encyrption info load command
+//
+template <typename P>
+class macho_encryption_info_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
+
+ uint32_t cryptoff() const INLINE { return E::get32(fields.cryptoff); }
+ void set_cryptoff(uint32_t value) INLINE { E::set32(fields.cryptoff, value); }
+
+ uint32_t cryptsize() const INLINE { return E::get32(fields.cryptsize); }
+ void set_cryptsize(uint32_t value) INLINE { E::set32(fields.cryptsize, value); }
+
+ uint32_t cryptid() const INLINE { return E::get32(fields.cryptid); }
+ void set_cryptid(uint32_t value) INLINE { E::set32(fields.cryptid, value); }
+
+ typedef typename P::E E;
+private:
+ encryption_info_command fields;
+};
+
+
+//
+// start of __unwind_info section
+//
+template <typename P>
+class macho_unwind_info_section_header {
+public:
+ uint32_t version() const INLINE { return E::get32(fields.version); }
+ void set_version(uint32_t value) INLINE { E::set32(fields.version, value); }
+
+ uint32_t commonEncodingsArraySectionOffset() const INLINE { return E::get32(fields.commonEncodingsArraySectionOffset); }
+ void set_commonEncodingsArraySectionOffset(uint32_t value) INLINE { E::set32(fields.commonEncodingsArraySectionOffset, value); }
+
+ uint32_t commonEncodingsArrayCount() const INLINE { return E::get32(fields.commonEncodingsArrayCount); }
+ void set_commonEncodingsArrayCount(uint32_t value) INLINE { E::set32(fields.commonEncodingsArrayCount, value); }
+
+ uint32_t personalityArraySectionOffset() const INLINE { return E::get32(fields.personalityArraySectionOffset); }
+ void set_personalityArraySectionOffset(uint32_t value) INLINE { E::set32(fields.personalityArraySectionOffset, value); }
+
+ uint32_t personalityArrayCount() const INLINE { return E::get32(fields.personalityArrayCount); }
+ void set_personalityArrayCount(uint32_t value) INLINE { E::set32(fields.personalityArrayCount, value); }
+
+ uint32_t indexSectionOffset() const INLINE { return E::get32(fields.indexSectionOffset); }
+ void set_indexSectionOffset(uint32_t value) INLINE { E::set32(fields.indexSectionOffset, value); }
+
+ uint32_t indexCount() const INLINE { return E::get32(fields.indexCount); }
+ void set_indexCount(uint32_t value) INLINE { E::set32(fields.indexCount, value); }
+
+ typedef typename P::E E;
+private:
+ unwind_info_section_header fields;
+};
+
+
+
+//
+// uwind first level index entry
+//
+template <typename P>
+class macho_unwind_info_section_header_index_entry {
+public:
+ uint32_t functionOffset() const INLINE { return E::get32(fields.functionOffset); }
+ void set_functionOffset(uint32_t value) INLINE { E::set32(fields.functionOffset, value); }
+
+ uint32_t secondLevelPagesSectionOffset() const INLINE { return E::get32(fields.secondLevelPagesSectionOffset); }
+ void set_secondLevelPagesSectionOffset(uint32_t value) INLINE { E::set32(fields.secondLevelPagesSectionOffset, value); }
+
+ uint32_t lsdaIndexArraySectionOffset() const INLINE { return E::get32(fields.lsdaIndexArraySectionOffset); }
+ void set_lsdaIndexArraySectionOffset(uint32_t value) INLINE { E::set32(fields.lsdaIndexArraySectionOffset, value); }
+
+ typedef typename P::E E;
+private:
+ unwind_info_section_header_index_entry fields;
+};
+
+
+//
+// LSDA table entry
+//
+template <typename P>
+class macho_unwind_info_section_header_lsda_index_entry {
+public:
+ uint32_t functionOffset() const INLINE { return E::get32(fields.functionOffset); }
+ void set_functionOffset(uint32_t value) INLINE { E::set32(fields.functionOffset, value); }
+
+ uint32_t lsdaOffset() const INLINE { return E::get32(fields.lsdaOffset); }
+ void set_lsdaOffset(uint32_t value) INLINE { E::set32(fields.lsdaOffset, value); }
+
+ typedef typename P::E E;
+private:
+ unwind_info_section_header_lsda_index_entry fields;
+};
+
+
+//
+// regular second level entry
+//
+template <typename P>
+class macho_unwind_info_regular_second_level_entry {
+public:
+ uint32_t functionOffset() const INLINE { return E::get32(fields.functionOffset); }
+ void set_functionOffset(uint32_t value) INLINE { E::set32(fields.functionOffset, value); }
+
+ uint32_t encoding() const INLINE { return E::get32(fields.encoding); }
+ void set_encoding(uint32_t value) INLINE { E::set32(fields.encoding, value); }
+
+ typedef typename P::E E;
+private:
+ unwind_info_regular_second_level_entry fields;
+};
+
+
+//
+// start of second level regular page
+//
+template <typename P>
+class macho_unwind_info_regular_second_level_page_header {
+public:
+ uint32_t kind() const INLINE { return E::get32(fields.kind); }
+ void set_kind(uint32_t value) INLINE { E::set32(fields.kind, value); }
+
+ uint16_t entryPageOffset() const INLINE { return E::get16(fields.entryPageOffset); }
+ void set_entryPageOffset(uint16_t value) INLINE { E::set16((uint16_t&)fields.entryPageOffset, value); }
+
+ uint16_t entryCount() const INLINE { return E::get16(fields.entryCount); }
+ void set_entryCount(uint16_t value) INLINE { E::set16((uint16_t&)fields.entryCount, value); }
+
+ typedef typename P::E E;
+private:
+ unwind_info_regular_second_level_page_header fields;
+};
+
+
+//
+// start of second level compressed page
+//
+template <typename P>
+class macho_unwind_info_compressed_second_level_page_header {
+public:
+ uint32_t kind() const INLINE { return E::get32(fields.kind); }
+ void set_kind(uint32_t value) INLINE { E::set32(fields.kind, value); }
+
+ uint16_t entryPageOffset() const INLINE { return E::get16(fields.entryPageOffset); }
+ void set_entryPageOffset(uint16_t value) INLINE { E::set16((uint16_t&)fields.entryPageOffset, value); }
+
+ uint16_t entryCount() const INLINE { return E::get16(fields.entryCount); }
+ void set_entryCount(uint16_t value) INLINE { E::set16((uint16_t&)fields.entryCount, value); }
+
+ uint16_t encodingsPageOffset() const INLINE { return E::get16(fields.encodingsPageOffset); }
+ void set_encodingsPageOffset(uint16_t value) INLINE { E::set16((uint16_t&)fields.encodingsPageOffset, value); }
+
+ uint16_t encodingsCount() const INLINE { return E::get16(fields.encodingsCount); }
+ void set_encodingsCount(uint16_t value) INLINE { E::set16((uint16_t&)fields.encodingsCount, value); }
+
+ typedef typename P::E E;
+private:
+ unwind_info_compressed_second_level_page_header fields;
+};
+
+
+//
+// compressed dyld info load command
+//
+template <typename P>
+class macho_dyld_info_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
+
+ uint32_t rebase_off() const INLINE { return E::get32(fields.rebase_off); }
+ void set_rebase_off(uint32_t value) INLINE { E::set32(fields.rebase_off, value); }
+
+ uint32_t rebase_size() const INLINE { return E::get32(fields.rebase_size); }
+ void set_rebase_size(uint32_t value) INLINE { E::set32(fields.rebase_size, value); }
+
+ uint32_t bind_off() const INLINE { return E::get32(fields.bind_off); }
+ void set_bind_off(uint32_t value) INLINE { E::set32(fields.bind_off, value); }
+
+ uint32_t bind_size() const INLINE { return E::get32(fields.bind_size); }
+ void set_bind_size(uint32_t value) INLINE { E::set32(fields.bind_size, value); }
+
+ uint32_t weak_bind_off() const INLINE { return E::get32(fields.weak_bind_off); }
+ void set_weak_bind_off(uint32_t value) INLINE { E::set32(fields.weak_bind_off, value); }
+
+ uint32_t weak_bind_size() const INLINE { return E::get32(fields.weak_bind_size); }
+ void set_weak_bind_size(uint32_t value) INLINE { E::set32(fields.weak_bind_size, value); }
+
+ uint32_t lazy_bind_off() const INLINE { return E::get32(fields.lazy_bind_off); }
+ void set_lazy_bind_off(uint32_t value) INLINE { E::set32(fields.lazy_bind_off, value); }
+
+ uint32_t lazy_bind_size() const INLINE { return E::get32(fields.lazy_bind_size); }
+ void set_lazy_bind_size(uint32_t value) INLINE { E::set32(fields.lazy_bind_size, value); }
+
+ uint32_t export_off() const INLINE { return E::get32(fields.export_off); }
+ void set_export_off(uint32_t value) INLINE { E::set32(fields.export_off, value); }
+
+ uint32_t export_size() const INLINE { return E::get32(fields.export_size); }
+ void set_export_size(uint32_t value) INLINE { E::set32(fields.export_size, value); }
+
+
+ typedef typename P::E E;
+private:
+ dyld_info_command fields;
+};
+
+
+
+
+#endif // __MACH_O_FILE_ABSTRACTION__
+
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2008 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+*/
+#ifndef __MACH_O_TRIE__
+#define __MACH_O_TRIE__
+
+#include <algorithm>
+
+#include "MachOFileAbstraction.hpp"
+
+namespace mach_o {
+namespace trie {
+
+struct Edge
+{
+ Edge(const char* s, struct Node* n) : fSubString(s), fChild(n) { }
+ ~Edge() { }
+ const char* fSubString;
+ struct Node* fChild;
+
+};
+
+struct Node
+{
+ Node(const char* s) : fCummulativeString(s), fAddress(0), fFlags(0), fOrdered(false),
+ fHaveExportInfo(false), fTrieOffset(0) {}
+ ~Node() { }
+ const char* fCummulativeString;
+ std::vector<Edge> fChildren;
+ uint64_t fAddress;
+ uint32_t fFlags;
+ bool fOrdered;
+ bool fHaveExportInfo;
+ uint32_t fTrieOffset;
+
+ void addSymbol(const char* fullStr, uint64_t address, uint32_t flags) {
+ const char* partialStr = &fullStr[strlen(fCummulativeString)];
+ for (std::vector<Edge>::iterator it = fChildren.begin(); it != fChildren.end(); ++it) {
+ Edge& e = *it;
+ int subStringLen = strlen(e.fSubString);
+ if ( strncmp(e.fSubString, partialStr, subStringLen) == 0 ) {
+ // already have matching edge, go down that path
+ e.fChild->addSymbol(fullStr, address, flags);
+ return;
+ }
+ else {
+ for (int i=subStringLen-1; i > 0; --i) {
+ if ( strncmp(e.fSubString, partialStr, i) == 0 ) {
+ // found a common substring, splice in new node
+ // was A -> C, now A -> B -> C
+ char* bNodeCummStr = strdup(e.fChild->fCummulativeString);
+ bNodeCummStr[strlen(bNodeCummStr)+i-subStringLen] = '\0';
+ //node* aNode = this;
+ Node* bNode = new Node(bNodeCummStr);
+ Node* cNode = e.fChild;
+ char* abEdgeStr = strdup(e.fSubString);
+ abEdgeStr[i] = '\0';
+ char* bcEdgeStr = strdup(&e.fSubString[i]);
+ Edge& abEdge = e;
+ abEdge.fSubString = abEdgeStr;
+ abEdge.fChild = bNode;
+ Edge bcEdge(bcEdgeStr, cNode);
+ bNode->fChildren.push_back(bcEdge);
+ bNode->addSymbol(fullStr, address, flags);
+ return;
+ }
+ }
+ }
+ }
+ // no commonality with any existing child, make a new edge that is this whole string
+ Node* newNode = new Node(strdup(fullStr));
+ Edge newEdge(strdup(partialStr), newNode);
+ fChildren.push_back(newEdge);
+ newNode->fAddress = address;
+ newNode->fFlags = flags;
+ newNode->fHaveExportInfo = true;
+ }
+
+ void addOrderedNodes(const char* name, std::vector<Node*>& orderedNodes) {
+ if ( !fOrdered ) {
+ orderedNodes.push_back(this);
+ //fprintf(stderr, "ordered %p %s\n", this, fCummulativeString);
+ fOrdered = true;
+ }
+ const char* partialStr = &name[strlen(fCummulativeString)];
+ for (std::vector<Edge>::iterator it = fChildren.begin(); it != fChildren.end(); ++it) {
+ Edge& e = *it;
+ int subStringLen = strlen(e.fSubString);
+ if ( strncmp(e.fSubString, partialStr, subStringLen) == 0 ) {
+ // already have matching edge, go down that path
+ e.fChild->addOrderedNodes(name, orderedNodes);
+ return;
+ }
+ }
+ }
+
+ // byte for terminal node size in bytes, or 0x00 if not terminal node
+ // teminal node (uleb128 flags, uleb128 addr)
+ // byte for child node count
+ // each child: zero terminated substring, uleb128 node offset
+ bool updateOffset(uint32_t& offset) {
+ uint32_t nodeSize = 1; // byte for length of export info
+ if ( fHaveExportInfo )
+ nodeSize += uleb128_size(fFlags) + uleb128_size(fAddress);
+
+ // add children
+ ++nodeSize; // byte for count of chidren
+ for (std::vector<Edge>::iterator it = fChildren.begin(); it != fChildren.end(); ++it) {
+ Edge& e = *it;
+ nodeSize += strlen(e.fSubString) + 1 + uleb128_size(e.fChild->fTrieOffset);
+ }
+ bool result = (fTrieOffset != offset);
+ fTrieOffset = offset;
+ //fprintf(stderr, "updateOffset %p %05d %s\n", this, fTrieOffset, fCummulativeString);
+ offset += nodeSize;
+ // return true if fTrieOffset was changed
+ return result;
+ }
+
+ void appendToStream(std::vector<uint8_t>& out) {
+ if ( fHaveExportInfo ) {
+ // nodes with export info: size, flags, address
+ out.push_back(uleb128_size(fFlags) + uleb128_size(fAddress));
+ append_uleb128(fFlags, out);
+ append_uleb128(fAddress, out);
+ }
+ else {
+ // no export info
+ out.push_back(0);
+ }
+ // write number of children
+ out.push_back(fChildren.size());
+ // write each child
+ for (std::vector<Edge>::iterator it = fChildren.begin(); it != fChildren.end(); ++it) {
+ Edge& e = *it;
+ append_string(e.fSubString, out);
+ append_uleb128(e.fChild->fTrieOffset, out);
+ }
+ }
+
+private:
+ static void append_uleb128(uint64_t value, std::vector<uint8_t>& out) {
+ uint8_t byte;
+ do {
+ byte = value & 0x7F;
+ value &= ~0x7F;
+ if ( value != 0 )
+ byte |= 0x80;
+ out.push_back(byte);
+ value = value >> 7;
+ } while( byte >= 0x80 );
+ }
+
+ static void append_string(const char* str, std::vector<uint8_t>& out) {
+ for (const char* s = str; *s != '\0'; ++s)
+ out.push_back(*s);
+ out.push_back('\0');
+ }
+
+ static unsigned int uleb128_size(uint64_t value) {
+ uint32_t result = 0;
+ do {
+ value = value >> 7;
+ ++result;
+ } while ( value != 0 );
+ return result;
+ }
+
+
+};
+
+inline uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end) {
+ uint64_t result = 0;
+ int bit = 0;
+ do {
+ if (p == end)
+ throw "malformed uleb128 extends beyond trie";
+
+ uint64_t slice = *p & 0x7f;
+
+ if (bit >= 64 || slice << bit >> bit != slice)
+ throw "uleb128 too big for 64-bits";
+ else {
+ result |= (slice << bit);
+ bit += 7;
+ }
+ }
+ while (*p++ & 0x80);
+ return result;
+}
+
+
+
+struct Entry
+{
+ const char* name;
+ uint64_t address;
+ uint64_t flags;
+};
+
+
+inline void makeTrie(const std::vector<Entry>& input, std::vector<uint8_t>& output)
+{
+ Node start(strdup(""));
+
+ // make nodes for all exported symbols
+ for (std::vector<Entry>::const_iterator it = input.begin(); it != input.end(); ++it) {
+ start.addSymbol(it->name, it->address, it->flags);
+ }
+
+ // create vector of nodes
+ std::vector<Node*> orderedNodes;
+ orderedNodes.reserve(input.size()*2);
+ for (std::vector<Entry>::const_iterator it = input.begin(); it != input.end(); ++it) {
+ start.addOrderedNodes(it->name, orderedNodes);
+ }
+
+ // assign each node in the vector an offset in the trie stream, iterating until all uleb128 sizes have stabilized
+ bool more;
+ do {
+ uint32_t offset = 0;
+ more = false;
+ for (std::vector<Node*>::iterator it = orderedNodes.begin(); it != orderedNodes.end(); ++it) {
+ if ( (*it)->updateOffset(offset) )
+ more = true;
+ }
+ } while ( more );
+
+ // create trie stream
+ for (std::vector<Node*>::iterator it = orderedNodes.begin(); it != orderedNodes.end(); ++it) {
+ (*it)->appendToStream(output);
+ }
+}
+
+struct EntryWithOffset
+{
+ uintptr_t nodeOffset;
+ Entry entry;
+
+ bool operator<(const EntryWithOffset& other) const { return ( nodeOffset < other.nodeOffset ); }
+};
+
+
+
+static inline void processExportNode(const uint8_t* const start, const uint8_t* p, const uint8_t* const end,
+ char* cummulativeString, int curStrOffset, std::vector<EntryWithOffset>& output)
+{
+ if ( p >= end )
+ throw "malformed trie, node past end";
+ const uint8_t terminalSize = *p++;
+ const uint8_t* children = p + terminalSize;
+ if ( terminalSize != 0 ) {
+ EntryWithOffset e;
+ e.nodeOffset = p-start;
+ e.entry.name = strdup(cummulativeString);
+ e.entry.flags = read_uleb128(p, end);
+ e.entry.address = read_uleb128(p, end);
+ output.push_back(e);
+ }
+ const uint8_t childrenCount = *children++;
+ const uint8_t* s = children;
+ for (uint8_t i=0; i < childrenCount; ++i) {
+ int edgeStrLen = 0;
+ while (*s != '\0') {
+ cummulativeString[curStrOffset+edgeStrLen] = *s++;
+ ++edgeStrLen;
+ }
+ cummulativeString[curStrOffset+edgeStrLen] = *s++;
+ uint32_t childNodeOffet = read_uleb128(s, end);
+ processExportNode(start, start+childNodeOffet, end, cummulativeString, curStrOffset+edgeStrLen, output);
+ }
+}
+
+
+inline void parseTrie(const uint8_t* start, const uint8_t* end, std::vector<Entry>& output)
+{
+ // empty tree has no entries
+ if ( start == end )
+ return;
+ char cummulativeString[4000];
+ std::vector<EntryWithOffset> entries;
+ processExportNode(start, start, end, cummulativeString, 0, entries);
+ // to preserve tie layout order, sort by node offset
+ std::sort(entries.begin(), entries.end());
+ // copy to output
+ output.reserve(entries.size());
+ for (std::vector<EntryWithOffset>::iterator it=entries.begin(); it != entries.end(); ++it)
+ output.push_back(it->entry);
+}
+
+
+
+
+}; // namespace trie
+}; // namespace mach_o
+
+
+#endif // __MACH_O_TRIE__
+
+
+++ /dev/null
-/*
- * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-#ifndef KLD
-#include <string.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include "dwarf2.h"
-#include "debugline.h"
-
-struct line_reader_data
-{
- bool little_endian;
-
- /* From the line number information header. */
- uint8_t minimum_instruction_length;
- int8_t line_base;
- uint8_t line_range;
- uint8_t opcode_base;
- const uint8_t * standard_opcode_lengths;
- size_t numdir;
- const uint8_t * * dirnames;
- size_t numfile_orig;
- size_t numfile; /* As updated during execution of the table. */
- const uint8_t * * filenames;
-
- /* Current position in the line table. */
- const uint8_t * cpos;
- /* End of this part of the line table. */
- const uint8_t * end;
- /* Start of the line table. */
- const uint8_t * init;
-
- struct line_info cur;
-};
-
-/* Read in a word of fixed size, which may be unaligned, in the
- appropriate endianness. */
-#define read_16(p) (lnd->little_endian \
- ? ((p)[1] << 8 | (p)[0]) \
- : ((p)[0] << 8 | (p)[1]))
-#define read_32(p) (lnd->little_endian \
- ? ((p)[3] << 24 | (p)[2] << 16 | (p)[1] << 8 | (p)[0]) \
- : ((p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3]))
-#define read_64(p) (lnd->little_endian \
- ? ((uint64_t) (p)[7] << 56 | (uint64_t) (p)[6] << 48 \
- | (uint64_t) (p)[5] << 40 | (uint64_t) (p)[4] << 32 \
- | (uint64_t) (p)[3] << 24 | (uint64_t) (p)[2] << 16u \
- | (uint64_t) (p)[1] << 8 | (uint64_t) (p)[0]) \
- : ((uint64_t) (p)[0] << 56 | (uint64_t) (p)[1] << 48 \
- | (uint64_t) (p)[2] << 40 | (uint64_t) (p)[3] << 32 \
- | (uint64_t) (p)[4] << 24 | (uint64_t) (p)[5] << 16u \
- | (uint64_t) (p)[6] << 8 | (uint64_t) (p)[7]))
-
-/* Skip over a LEB128 value (signed or unsigned). */
-static void
-skip_leb128 (struct line_reader_data * leb)
-{
- while (leb->cpos != leb->end && *leb->cpos >= 0x80)
- leb->cpos++;
- if (leb->cpos != leb->end)
- leb->cpos++;
-}
-
-/* Read a ULEB128 into a 64-bit word. Return (uint64_t)-1 on overflow
- or error. On overflow, skip past the rest of the uleb128. */
-static uint64_t
-read_uleb128 (struct line_reader_data * leb)
-{
- uint64_t result = 0;
- int bit = 0;
-
- do {
- uint64_t b;
-
- if (leb->cpos == leb->end)
- return (uint64_t) -1;
-
- b = *leb->cpos & 0x7f;
-
- if (bit >= 64 || b << bit >> bit != b)
- result = (uint64_t) -1;
- else
- result |= b << bit, bit += 7;
- } while (*leb->cpos++ >= 0x80);
- return result;
-}
-
-
-/* Read a SLEB128 into a 64-bit word. Return 0 on overflow or error
- (which is not very helpful). On overflow, skip past the rest of
- the SLEB128. For negative numbers, this actually overflows when
- under -2^62, but since this is used for line numbers that ought to
- be OK... */
-static int64_t
-read_sleb128 (struct line_reader_data * leb)
-{
- const uint8_t * start_pos = leb->cpos;
- uint64_t v = read_uleb128 (leb);
- uint64_t signbit;
-
- if (v >= 1ull << 63)
- return 0;
- if (leb->cpos - start_pos > 9)
- return v;
-
- signbit = 1ull << ((leb->cpos - start_pos) * 7 - 1);
-
- return v | -(v & signbit);
-}
-
-/* Free a line_reader_data structure. */
-void
-line_free (struct line_reader_data * lnd)
-{
- if (! lnd)
- return;
- if (lnd->dirnames)
- free (lnd->dirnames);
- if (lnd->filenames)
- free (lnd->filenames);
- free (lnd);
-}
-
-/* Return the pathname of the file in S, or NULL on error.
- The result will have been allocated with malloc. */
-
-char *
-line_file (struct line_reader_data *lnd, uint64_t n)
-{
- const uint8_t * prev_pos = lnd->cpos;
- size_t filelen, dirlen;
- uint64_t dir;
- char * result;
-
- /* I'm not sure if this is actually an error. */
- if (n == 0
- || n > lnd->numfile)
- return NULL;
-
- filelen = strlen ((const char *)lnd->filenames[n - 1]);
- lnd->cpos = lnd->filenames[n - 1] + filelen + 1;
- dir = read_uleb128 (lnd);
- lnd->cpos = prev_pos;
- if (dir == 0
- || lnd->filenames[n - 1][0] == '/')
- return strdup ((const char *)lnd->filenames[n - 1]);
- else if (dir > lnd->numdir)
- return NULL;
-
- dirlen = strlen ((const char *) lnd->dirnames[dir - 1]);
- result = malloc (dirlen + filelen + 2);
- memcpy (result, lnd->dirnames[dir - 1], dirlen);
- result[dirlen] = '/';
- memcpy (result + dirlen + 1, lnd->filenames[n - 1], filelen);
- result[dirlen + 1 + filelen] = '\0';
- return result;
-}
-
-/* Initialize a state S. Return FALSE on error. */
-
-static void
-init_state (struct line_info *s)
-{
- s->file = 1;
- s->line = 1;
- s->col = 0;
- s->pc = 0;
- s->end_of_sequence = false;
-}
-
-/* Read a debug_line section. */
-
-struct line_reader_data *
-line_open (const uint8_t * debug_line, size_t debug_line_size,
- int little_endian)
-{
- struct line_reader_data * lnd = NULL;
- bool dwarf_size_64;
-
- uint64_t lnd_length, header_length;
- const uint8_t * table_start;
-
- if (debug_line_size < 12)
- return NULL;
-
- lnd = malloc (sizeof (struct line_reader_data));
- if (! lnd)
- goto error;
-
- lnd->little_endian = little_endian;
- lnd->cpos = debug_line;
-
- lnd_length = read_32 (lnd->cpos);
- lnd->cpos += 4;
- if (lnd_length == 0xffffffff)
- {
- lnd_length = read_64 (lnd->cpos);
- lnd->cpos += 8;
- dwarf_size_64 = true;
- }
- else if (lnd_length > 0xfffffff0)
- /* Not a format we understand. */
- goto error;
- else
- dwarf_size_64 = false;
-
- if (debug_line_size < lnd_length + (dwarf_size_64 ? 12 : 4)
- || lnd_length < (dwarf_size_64 ? 15 : 11))
- /* Too small. */
- goto error;
-
- if (read_16 (lnd->cpos) != 2)
- /* Unknown line number format. */
- goto error;
- lnd->cpos += 2;
-
- header_length = dwarf_size_64 ? (uint64_t)read_64(lnd->cpos) : (uint64_t)read_32(lnd->cpos);
- lnd->cpos += dwarf_size_64 ? 8 : 4;
- if (lnd_length < header_length + (lnd->cpos - debug_line)
- || header_length < 7)
- goto error;
-
- lnd->minimum_instruction_length = lnd->cpos[0];
- /* Ignore default_is_stmt. */
- lnd->line_base = lnd->cpos[2];
- lnd->line_range = lnd->cpos[3];
- lnd->opcode_base = lnd->cpos[4];
-
- if (lnd->opcode_base == 0)
- /* Every valid line number program must use at least opcode 0
- for DW_LNE_end_sequence. */
- goto error;
-
- lnd->standard_opcode_lengths = lnd->cpos + 5;
- if (header_length < (uint64_t)(5 + (lnd->opcode_base - 1)))
- /* Header not long enough. */
- goto error;
- lnd->cpos += 5 + lnd->opcode_base - 1;
- lnd->end = debug_line + header_length + (dwarf_size_64 ? 22 : 10);
-
- /* Make table of offsets to directory names. */
- table_start = lnd->cpos;
- lnd->numdir = 0;
- while (lnd->cpos != lnd->end && *lnd->cpos)
- {
- lnd->cpos = memchr (lnd->cpos, 0, lnd->end - lnd->cpos);
- if (! lnd->cpos)
- goto error;
- lnd->cpos++;
- lnd->numdir++;
- }
- if (lnd->cpos == lnd->end)
- goto error;
- lnd->dirnames = malloc (lnd->numdir * sizeof (const uint8_t *));
- if (! lnd->dirnames)
- goto error;
- lnd->numdir = 0;
- lnd->cpos = table_start;
- while (*lnd->cpos)
- {
- lnd->dirnames[lnd->numdir++] = lnd->cpos;
- lnd->cpos = memchr (lnd->cpos, 0, lnd->end - lnd->cpos) + 1;
- }
- lnd->cpos++;
-
- /* Make table of offsets to file entries. */
- table_start = lnd->cpos;
- lnd->numfile = 0;
- while (lnd->cpos != lnd->end && *lnd->cpos)
- {
- lnd->cpos = memchr (lnd->cpos, 0, lnd->end - lnd->cpos);
- if (! lnd->cpos)
- goto error;
- lnd->cpos++;
- skip_leb128 (lnd);
- skip_leb128 (lnd);
- skip_leb128 (lnd);
- lnd->numfile++;
- }
- if (lnd->cpos == lnd->end)
- goto error;
- lnd->filenames = malloc (lnd->numfile * sizeof (const uint8_t *));
- if (! lnd->filenames)
- goto error;
- lnd->numfile = 0;
- lnd->cpos = table_start;
- while (*lnd->cpos)
- {
- lnd->filenames[lnd->numfile++] = lnd->cpos;
- lnd->cpos = memchr (lnd->cpos, 0, lnd->end - lnd->cpos) + 1;
- skip_leb128 (lnd);
- skip_leb128 (lnd);
- skip_leb128 (lnd);
- }
- lnd->cpos++;
-
- lnd->numfile_orig = lnd->numfile;
- lnd->cpos = lnd->init = lnd->end;
- lnd->end = debug_line + lnd_length + (dwarf_size_64 ? 12 : 4);
-
- init_state (&lnd->cur);
-
- return lnd;
-
- error:
- line_free (lnd);
- return NULL;
-}
-
-/* Reset back to the beginning. */
-void
-line_reset (struct line_reader_data * lnd)
-{
- lnd->cpos = lnd->init;
- lnd->numfile = lnd->numfile_orig;
- init_state (&lnd->cur);
-}
-
-/* Is there no more line data available? */
-int
-line_at_eof (struct line_reader_data * lnd)
-{
- return lnd->cpos == lnd->end;
-}
-
-static bool
-next_state (struct line_reader_data *lnd)
-{
- if (lnd->cur.end_of_sequence)
- init_state (&lnd->cur);
-
- for (;;)
- {
- uint8_t op;
- uint64_t tmp;
-
- if (lnd->cpos == lnd->end)
- return false;
- op = *lnd->cpos++;
- if (op >= lnd->opcode_base)
- {
- op -= lnd->opcode_base;
-
- lnd->cur.line += op % lnd->line_range + lnd->line_base;
- lnd->cur.pc += (op / lnd->line_range
- * lnd->minimum_instruction_length);
- return true;
- }
- else switch (op)
- {
- case DW_LNS_extended_op:
- {
- uint64_t sz = read_uleb128 (lnd);
- const uint8_t * op = lnd->cpos;
-
- if ((uint64_t)(lnd->end - op) < sz || sz == 0)
- return false;
- lnd->cpos += sz;
- switch (*op++)
- {
- case DW_LNE_end_sequence:
- lnd->cur.end_of_sequence = true;
- return true;
-
- case DW_LNE_set_address:
- if (sz == 9)
- lnd->cur.pc = read_64 (op);
- else if (sz == 5)
- lnd->cur.pc = read_32 (op);
- else
- return false;
- break;
-
- case DW_LNE_define_file:
- {
- const uint8_t * * filenames;
- filenames = realloc
- (lnd->filenames,
- (lnd->numfile + 1) * sizeof (const uint8_t *));
- if (! filenames)
- return false;
- /* Check for zero-termination. */
- if (! memchr (op, 0, lnd->cpos - op))
- return false;
- filenames[lnd->numfile++] = op;
- lnd->filenames = filenames;
-
- /* There's other data here, like file sizes and modification
- times, but we don't need to read it so skip it. */
- }
- break;
-
- default:
- /* Don't understand it, so skip it. */
- break;
- }
- break;
- }
-
- case DW_LNS_copy:
- //fprintf(stderr, "DW_LNS_copy\n");
- return true;
- case DW_LNS_advance_pc:
- //fprintf(stderr, "DW_LNS_advance_pc\n");
- tmp = read_uleb128 (lnd);
- if (tmp == (uint64_t) -1)
- return false;
- lnd->cur.pc += tmp * lnd->minimum_instruction_length;
- break;
- case DW_LNS_advance_line:
- //fprintf(stderr, "DW_LNS_advance_line\n");
- lnd->cur.line += read_sleb128 (lnd);
- break;
- case DW_LNS_set_file:
- //fprintf(stderr, "DW_LNS_set_file\n");
- lnd->cur.file = read_uleb128 (lnd);
- break;
- case DW_LNS_set_column:
- //fprintf(stderr, "DW_LNS_set_column\n");
- lnd->cur.col = read_uleb128 (lnd);
- break;
- case DW_LNS_const_add_pc:
- //fprintf(stderr, "DW_LNS_const_add_pc\n");
- lnd->cur.pc += ((255 - lnd->opcode_base) / lnd->line_range
- * lnd->minimum_instruction_length);
- break;
- case DW_LNS_fixed_advance_pc:
- //fprintf(stderr, "DW_LNS_fixed_advance_pc\n");
- if (lnd->end - lnd->cpos < 2)
- return false;
- lnd->cur.pc += read_16 (lnd->cpos);
- lnd->cpos += 2;
- break;
- default:
- {
- /* Don't know what it is, so skip it. */
- int i;
- for (i = 0; i < lnd->standard_opcode_lengths[op - 1]; i++)
- skip_leb128 (lnd);
- break;
- }
- }
- }
-}
-
-
-/* Set RESULT to the next 'interesting' line state, as indicated
- by STOP, or return FALSE on error. The final (end-of-sequence)
- line state is always considered interesting. */
-int
-line_next (struct line_reader_data * lnd,
- struct line_info * result,
- enum line_stop_constants stop)
-{
- for (;;)
- {
- struct line_info prev = lnd->cur;
-
- if (! next_state (lnd))
- return false;
-
- if (lnd->cur.end_of_sequence)
- break;
- if (stop == line_stop_always)
- break;
- if ((stop & line_stop_pc) && lnd->cur.pc != prev.pc)
- break;
- if ((stop & line_stop_pos_mask) && lnd->cur.file != prev.file)
- break;
- if ((stop & line_stop_pos_mask) >= line_stop_line
- && lnd->cur.line != prev.line)
- break;
- if ((stop & line_stop_pos_mask) >= line_stop_col
- && lnd->cur.col != prev.col)
- break;
- }
- *result = lnd->cur;
- return true;
-}
-
-/* Find the region (START->pc through END->pc) in the debug_line
- information which contains PC. This routine starts searching at
- the current position (which is returned as END), and will go all
- the way around the debug_line information. It will return false if
- an error occurs or if there is no matching region; these may be
- distinguished by looking at START->end_of_sequence, which will be
- false on error and true if there was no matching region.
- You could write this routine using line_next, but this version
- will be slightly more efficient, and of course more convenient. */
-
-int
-line_find_addr (struct line_reader_data * lnd,
- struct line_info * start,
- struct line_info * end,
- uint64_t pc)
-{
- const uint8_t * startpos;
- struct line_info prev;
-
- if (lnd->cur.end_of_sequence && lnd->cpos == lnd->end)
- line_reset (lnd);
-
- startpos = lnd->cpos;
-
- do {
- prev = lnd->cur;
- if (! next_state (lnd))
- {
- start->end_of_sequence = false;
- return false;
- }
- if (lnd->cur.end_of_sequence && lnd->cpos == lnd->end)
- line_reset (lnd);
- if (lnd->cpos == startpos)
- {
- start->end_of_sequence = true;
- return false;
- }
- } while (lnd->cur.pc <= pc || prev.pc > pc || prev.end_of_sequence);
- *start = prev;
- *end = lnd->cur;
- return true;
-}
-#endif /* ! KLD */
-
+++ /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@
- */
-#include <stdint.h>
-#include <stddef.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* Information about a line.
- DIRECTORY is to be ignored if FILENAME is absolute.
- PC will be relative to the file the debug_line section is in. */
-struct line_info
-{
- uint64_t file;
- int64_t line;
- uint64_t col;
- uint64_t pc;
- int end_of_sequence;
-};
-
-/* Opaque status structure for the line readers. */
-struct line_reader_data;
-
-/* Create a line_reader_data, given address and size of the debug_line section.
- SIZE may be (size_t)-1 if unknown, although this suppresses checking
- for an incorrectly large size in the debug_line section.
- LITTLE_ENDIAN is set if the debug_line section is for a little-endian
- machine.
- Returns NULL on error. */
-struct line_reader_data * line_open (const uint8_t * debug_line,
- size_t debug_line_size,
- int little_endian);
-
-/* The STOP parameter to line_next is one of line_stop_{file,line,col},
- perhaps ORed with line_stop_pc; or line_stop_atend, or line_stop_always. */
-enum line_stop_constants {
- line_stop_atend = 0, /* Stop only at the end of a sequence. */
- line_stop_file = 1, /* Stop if DIRECTORY or FILENAME change. */
- line_stop_line = 2, /* Stop if LINE, DIRECTORY, or FILENAME change. */
- line_stop_col = 3, /* Stop if COL, LINE, DIRECTORY, or FILENAME change. */
- line_stop_pos_mask = 3,
- line_stop_pc = 4, /* Stop if PC changes. */
- line_stop_always = 8 /* Stop always. */
-};
-
-/* Either return FALSE on an error, in which case the line_reader_data
- may be invalid and should be passed immediately to line_free; or
- fill RESULT with the first 'interesting' line, as determined by STOP.
- The last line data in a sequence is always considered 'interesting'. */
-int line_next (struct line_reader_data * lnd,
- struct line_info * result,
- enum line_stop_constants stop);
-
-/* Find the region (START->pc through END->pc) in the debug_line
- information which contains PC. This routine starts searching at
- the current position (which is returned as END), and will go all
- the way around the debug_line information. It will return false if
- an error occurs or if there is no matching region; these may be
- distinguished by looking at START->end_of_sequence, which will be
- false on error and true if there was no matching region.
- You could write this routine using line_next, but this version
- will be slightly more efficient, and of course more convenient. */
-
-int line_find_addr (struct line_reader_data * lnd,
- struct line_info * start,
- struct line_info * end,
- uint64_t pc);
-
-/* Return TRUE if there is more line data to be fetched.
- If line_next has not been called or it has been called but did not
- set END_OF_SEQUENCE, you can assume there is more line data,
- but it's safe to call this routine anyway. */
-int line_at_eof (struct line_reader_data * lnd);
-
-/* Return the pathname of the file in S, or NULL on error.
- The result will have been allocated with malloc. */
-char * line_file (struct line_reader_data *lnd, uint64_t file);
-
-/* Reset the line_reader_data: go back to the beginning. */
-void line_reset (struct line_reader_data * lnd);
-
-/* Free a line_reader_data structure. */
-void line_free (struct line_reader_data * lnd);
-
-#ifdef __cplusplus
-}
-#endif
-
+++ /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@
- */
-/* These constants were taken from version 3 of the DWARF standard,
- which is Copyright (c) 2005 Free Standards Group, and
- Copyright (c) 1992, 1993 UNIX International, Inc. */
-
-/* This is not a complete list. */
-enum {
- DW_TAG_compile_unit = 17,
- DW_TAG_partial_unit = 60
-};
-
-/* This is not a complete list. */
-enum {
- DW_AT_sibling = 1,
- DW_AT_name = 3,
- DW_AT_stmt_list = 16,
- DW_AT_comp_dir = 27
-};
-
-enum {
- DW_FORM_addr = 1,
- DW_FORM_block2 = 3,
- DW_FORM_block4,
- DW_FORM_data2,
- DW_FORM_data4,
- DW_FORM_data8,
- DW_FORM_string,
- DW_FORM_block,
- DW_FORM_block1,
- DW_FORM_data1,
- DW_FORM_flag,
- DW_FORM_sdata,
- DW_FORM_strp,
- DW_FORM_udata,
- DW_FORM_ref_addr,
- DW_FORM_ref1,
- DW_FORM_ref2,
- DW_FORM_ref4,
- DW_FORM_ref8,
- DW_FORM_ref_udata,
- DW_FORM_indirect /* 22 */
-};
-
-enum {
- DW_LNS_extended_op = 0,
- DW_LNS_copy,
- DW_LNS_advance_pc,
- DW_LNS_advance_line,
- DW_LNS_set_file,
- DW_LNS_set_column,
- DW_LNS_negate_stmt,
- DW_LNS_set_basic_block,
- DW_LNS_const_add_pc,
- DW_LNS_fixed_advance_pc,
- DW_LNS_set_prologue_end,
- DW_LNS_set_epilogue_begin,
- DW_LNS_set_isa
-};
-
-enum {
- DW_LNE_end_sequence = 1,
- DW_LNE_set_address,
- DW_LNE_define_file
-};
+++ /dev/null
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-*
- * Copyright (c) 2005-2007 Apple 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@
- */
-
-// start temp HACK for cross builds
-extern "C" double log2 ( double );
-#define __MATH__
-// end temp HACK for cross builds
-
-
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#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>
-#include <mach/mach_init.h>
-#include <mach/mach_host.h>
-#include <dlfcn.h>
-
-#include <string>
-#include <map>
-#include <set>
-#include <string>
-#include <vector>
-#include <list>
-#include <algorithm>
-#include <ext/hash_map>
-#include <dlfcn.h>
-#include <AvailabilityMacros.h>
-
-#include "configure.h"
-#include "Options.h"
-
-#include "ObjectFile.h"
-
-#include "MachOReaderRelocatable.hpp"
-#include "ArchiveReader.hpp"
-#include "MachOReaderDylib.hpp"
-#include "MachOWriterExecutable.hpp"
-
-
-#if LTO_SUPPORT
-#include "LTOReader.hpp"
-#endif
-
-
-#include "OpaqueSection.hpp"
-
-
-class CStringComparor
-{
-public:
- bool operator()(const char* left, const char* right) const { return (strcmp(left, right) < 0); }
-};
-
-class CStringEquals
-{
-public:
- bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
-};
-
-class Section : public ObjectFile::Section
-{
-public:
- static Section* find(const char* sectionName, const char* segmentName, bool zeroFill);
- static void assignIndexes();
- const char* getName() { return fSectionName; }
-private:
- Section(const char* sectionName, const char* segmentName, bool zeroFill);
-
- struct Sorter {
- static int segmentOrdinal(const char* segName);
- bool operator()(Section* left, Section* right);
- };
-
- typedef __gnu_cxx::hash_map<const char*, uint32_t, __gnu_cxx::hash<const char*>, CStringEquals> NameToOrdinal;
- typedef __gnu_cxx::hash_map<const char*, class Section*, __gnu_cxx::hash<const char*>, CStringEquals> NameToSection;
- //typedef std::map<const char*, class Section*, CStringComparor> NameToSection;
-
- const char* fSectionName;
- const char* fSegmentName;
- bool fZeroFill;
-
- static NameToSection fgMapping;
- static std::vector<Section*> fgSections;
- static NameToOrdinal fgSegmentDiscoverOrder;
-};
-
-Section::NameToSection Section::fgMapping;
-std::vector<Section*> Section::fgSections;
-Section::NameToOrdinal Section::fgSegmentDiscoverOrder;
-
-Section::Section(const char* sectionName, const char* segmentName, bool zeroFill)
- : fSectionName(sectionName), fSegmentName(segmentName), fZeroFill(zeroFill)
-{
- this->fIndex = fgSections.size();
- //fprintf(stderr, "new Section(%s, %s) => %p, %u\n", sectionName, segmentName, this, this->getIndex());
-}
-
-Section* Section::find(const char* sectionName, const char* segmentName, bool zeroFill)
-{
- NameToSection::iterator pos = fgMapping.find(sectionName);
- if ( pos != fgMapping.end() ) {
- if ( strcmp(pos->second->fSegmentName, segmentName) == 0 )
- return pos->second;
- // otherwise same section name is used in different segments, look slow way
- for (std::vector<Section*>::iterator it=fgSections.begin(); it != fgSections.end(); it++) {
- if ( (strcmp((*it)->fSectionName, sectionName) == 0) && (strcmp((*it)->fSegmentName, segmentName) == 0) )
- return *it;
- }
- }
-
- // does not exist, so make a new one
- Section* sect = new Section(sectionName, segmentName, zeroFill);
- fgMapping[sectionName] = sect;
- fgSections.push_back(sect);
-
- if ( (strcmp(sectionName, "__text") == 0) && (strcmp(segmentName, "__TEXT") == 0) ) {
- // special case __textcoal_nt to be right after __text
- find("__textcoal_nt", "__TEXT", false);
- }
-
- // remember segment discovery order
- if ( fgSegmentDiscoverOrder.find(segmentName) == fgSegmentDiscoverOrder.end() )
- fgSegmentDiscoverOrder[segmentName] = fgSegmentDiscoverOrder.size();
-
- return sect;
-}
-
-int Section::Sorter::segmentOrdinal(const char* segName)
-{
- if ( strcmp(segName, "__PAGEZERO") == 0 )
- return 1;
- if ( strcmp(segName, "__TEXT") == 0 )
- return 2;
- if ( strcmp(segName, "__DATA") == 0 )
- return 3;
- if ( strcmp(segName, "__OBJC") == 0 )
- return 4;
- if ( strcmp(segName, "__OBJC2") == 0 )
- return 5;
- if ( strcmp(segName, "__LINKEDIT") == 0 )
- return INT_MAX; // linkedit segment should always sort last
- else
- return fgSegmentDiscoverOrder[segName]+6;
-}
-
-
-bool Section::Sorter::operator()(Section* left, Section* right)
-{
- // Segment is primary sort key
- int leftSegOrdinal = segmentOrdinal(left->fSegmentName);
- int rightSegOrdinal = segmentOrdinal(right->fSegmentName);
- if ( leftSegOrdinal < rightSegOrdinal )
- return true;
- if ( leftSegOrdinal > rightSegOrdinal )
- return false;
-
- // zerofill section sort to the end
- if ( !left->fZeroFill && right->fZeroFill )
- return true;
- if ( left->fZeroFill && !right->fZeroFill )
- return false;
-
- // section discovery order is last sort key
- return left->fIndex < right->fIndex;
-}
-
-void Section::assignIndexes()
-{
- //printf("unsorted sections:\n");
- //for (std::vector<Section*>::iterator it=fgSections.begin(); it != fgSections.end(); it++) {
- // printf("section: name=%s, segment: name=%s, discovery order=%d\n", (*it)->fSectionName, (*it)->fSegmentName, (*it)->fIndex);
- //}
-
- // sort it
- std::sort(fgSections.begin(), fgSections.end(), Section::Sorter());
-
- // assign correct section ordering to each Section object
- unsigned int newOrder = 1;
- for (std::vector<Section*>::iterator it=fgSections.begin(); it != fgSections.end(); it++)
- (*it)->fIndex = newOrder++;
-
- //printf("sorted sections:\n");
- //for (std::vector<Section*>::iterator it=fgSections.begin(); it != fgSections.end(); it++) {
- // printf("section: index=%d, obj=%p, name=%s\n", (*it)->fIndex, (*it), (*it)->fSectionName);
- //}
-}
-
-class Linker : public ObjectFile::Reader::DylibHander {
-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, const Options::FileInfo& );
- void setOutputFile(ExecutableFile::Writer* writer);
- void link();
- void optimize();
-
- // implemenation from ObjectFile::Reader::DylibHander
- virtual ObjectFile::Reader* findDylib(const char* installPath, const char* fromPath);
-
-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 processDylibs();
- void markDead(ObjectFile::Atom* atom);
- void updateConstraints(ObjectFile::Reader* reader);
- void loadAndResolve();
- void processDTrace();
- void checkObjC();
- void loadUndefines();
- void checkUndefines();
- void addWeakAtomOverrides();
- void resolveReferences();
- void deadStripResolve();
- void addLiveRoot(const char* name);
- ObjectFile::Atom* findAtom(const Options::OrderedSymbol& pair);
- void logArchive(ObjectFile::Reader* reader);
- void sortSections();
- void sortAtoms();
- void tweakLayout();
- void writeDotOutput();
- static bool minimizeStab(ObjectFile::Reader::Stab& stab);
- static const char* truncateStabString(const char* str);
- void collectDebugInfo();
- void writeOutput();
- ObjectFile::Atom* entryPoint(bool orInit);
- ObjectFile::Atom* dyldHelper();
- ObjectFile::Atom* dyldLazyLibraryHelper();
- const char* assureFullPath(const char* path);
- void markLive(ObjectFile::Atom& atom, Linker::WhyLiveBackChain* previous);
- void collectStabs(ObjectFile::Reader* reader, std::map<const class ObjectFile::Atom*, uint32_t>& atomOrdinals);
- void synthesizeDebugNotes(std::vector<class ObjectFile::Atom*>& allAtomsByReader);
- void printStatistics();
- void printTime(const char* msg, uint64_t partTime, uint64_t totalTime);
- char* commatize(uint64_t in, char* out);
- void getVMInfo(vm_statistics_data_t& info);
- cpu_type_t inferArchitecture();
- void addDtraceProbe(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* probeName);
- void checkDylibClientRestrictions(ObjectFile::Reader* reader);
- void logDylib(ObjectFile::Reader* reader, bool indirect);
-
- void resolve(ObjectFile::Reference* reference);
- void resolveFrom(ObjectFile::Reference* reference);
- std::vector<class ObjectFile::Atom*>* addJustInTimeAtoms(const char* name, bool dylibsOnly=false);
- void addJustInTimeAtomsAndMarkLive(const char* name);
-
- ObjectFile::Reader* addDylib(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen);
- ObjectFile::Reader* addObject(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen);
- ObjectFile::Reader* addArchive(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen);
-
- void logTraceInfo(const char* format, ...);
-
-
- class SymbolTable
- {
- public:
- typedef __gnu_cxx::hash_map<const char*, ObjectFile::Atom*, __gnu_cxx::hash<const char*>, CStringEquals> Mapper;
-
- SymbolTable(Linker&);
- void require(const char* name);
- bool add(ObjectFile::Atom& atom);
- ObjectFile::Atom* find(const char* name);
- unsigned int getRequireCount() { return fRequireCount; }
- void getNeededNames(bool andWeakDefintions, std::vector<const char*>& undefines);
- bool hasExternalTentativeDefinitions() { return fHasExternalTentativeDefinitions; }
- bool hasExternalWeakDefinitions() { return fHasExternalWeakDefinitions; }
- void setHasExternalWeakDefinitions() { fHasExternalWeakDefinitions = true; }
- Mapper::iterator begin() { return fTable.begin(); }
- Mapper::iterator end() { return fTable.end(); }
-
- private:
- Linker& fOwner;
- Mapper fTable;
- unsigned int fRequireCount;
- bool fHasExternalTentativeDefinitions;
- bool fHasExternalWeakDefinitions;
- };
-
- class AtomSorter
- {
- public:
- AtomSorter(std::map<const ObjectFile::Atom*, uint32_t>* map) : fOverriddenOrdinalMap(map) {}
- bool operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right);
- private:
- std::map<const ObjectFile::Atom*, uint32_t>* fOverriddenOrdinalMap;
- };
-
- typedef std::map<const char*, uint32_t, CStringComparor> SectionOrder;
-
- struct DTraceProbeInfo {
- DTraceProbeInfo(const ObjectFile::Atom* a, uint32_t o, const char* n) : atom(a), offset(o), probeName(n) {}
- const ObjectFile::Atom* atom;
- uint32_t offset;
- const char* probeName;
- };
- typedef __gnu_cxx::hash_map<const char*, std::vector<DTraceProbeInfo>, __gnu_cxx::hash<const char*>, CStringEquals> ProviderToProbes;
- typedef __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> CStringSet;
- typedef __gnu_cxx::hash_map<const char*, ObjectFile::Reader*, __gnu_cxx::hash<const char*>, CStringEquals> InstallNameToReader;
-
- struct IndirectLibrary {
- const char* path;
- uint64_t fileLen;
- ObjectFile::Reader* reader;
- std::set<ObjectFile::Reader*> parents;
- ObjectFile::Reader* reExportedViaDirectLibrary;
- };
-
- ObjectFile::Reader* findDirectLibraryWhichReExports(struct IndirectLibrary& indirectLib);
-
- Options fOptions;
- SymbolTable fGlobalSymbolTable;
- uint32_t fNextInputOrdinal;
- std::vector<class ObjectFile::Reader*> fInputFiles;
- ExecutableFile::Writer* fOutputFile;
- InstallNameToReader fDylibMap;
- std::map<ObjectFile::Reader*,DynamicLibraryOptions> fDylibOptionsMap;
- std::set<ObjectFile::Reader*> fDylibsProcessed;
- ObjectFile::Reader* fBundleLoaderReader;
- std::vector<class ObjectFile::Reader*> fReadersThatHaveSuppliedAtoms;
- std::vector<class ObjectFile::Atom*> fAllAtoms;
- std::set<class ObjectFile::Reader*> fArchiveReaders;
- std::set<class ObjectFile::Reader*> fArchiveReadersLogged;
- std::set<class ObjectFile::Atom*> fDeadAtoms;
- std::set<ObjectFile::Atom*> fLiveAtoms;
- std::set<ObjectFile::Atom*> fLiveRootAtoms;
- std::vector<class ObjectFile::Reader::Stab> fStabs;
- std::vector<class ObjectFile::Atom*> fAtomsWithUnresolvedReferences;
- std::vector<DTraceProbeInfo> fDtraceProbes;
- std::vector<DTraceProbeInfo> fDtraceProbeSites;
- std::vector<DTraceProbeInfo> fDtraceIsEnabledSites;
- std::map<const ObjectFile::Atom*,CStringSet> fDtraceAtomToTypes;
- bool fCreateUUID;
- bool fCanScatter;
- SectionOrder fSectionOrder;
- cpu_type_t fArchitecture;
- const char* fArchitectureName;
- bool fArchitectureInferred;
- bool fDirectLibrariesComplete;
- bool fBiggerThanTwoGigOutput;
- uint64_t fOutputFileSize;
- uint64_t fTotalZeroFillSize;
- uint64_t fTotalSize;
- uint64_t fStartTime;
- uint64_t fStartCreateReadersTime;
- uint64_t fStartCreateWriterTime;
- uint64_t fStartBuildAtomsTime;
- uint64_t fStartLoadAndResolveTime;
- uint64_t fStartSortTime;
- uint64_t fStartDebugTime;
- uint64_t fStartWriteTime;
- uint64_t fEndTime;
- uint64_t fTotalObjectSize;
- uint64_t fTotalArchiveSize;
- uint32_t fTotalObjectLoaded;
- uint32_t fTotalArchivesLoaded;
- uint32_t fTotalDylibsLoaded;
- vm_statistics_data_t fStartVMInfo;
- ObjectFile::Reader::ObjcConstraint fCurrentObjCConstraint;
- ObjectFile::Reader::CpuConstraint fCurrentCpuConstraint;
- bool fObjcReplacmentClasses;
- bool fAllDirectDylibsLoaded;
-};
-
-
-Linker::Linker(int argc, const char* argv[])
- : fOptions(argc, argv), fGlobalSymbolTable(*this), fNextInputOrdinal(1), fOutputFile(NULL), fBundleLoaderReader(NULL),
- fCreateUUID(fOptions.outputKind() != Options::kObjectFile), fCanScatter(true),
- fArchitecture(0), fArchitectureInferred(false), fDirectLibrariesComplete(false), fBiggerThanTwoGigOutput(false),
- fOutputFileSize(0), fTotalZeroFillSize(0), fTotalSize(0), fTotalObjectSize(0),
- fTotalArchiveSize(0), fTotalObjectLoaded(0), fTotalArchivesLoaded(0), fTotalDylibsLoaded(0),
- fCurrentObjCConstraint(ObjectFile::Reader::kObjcNone), fCurrentCpuConstraint(ObjectFile::Reader::kCpuAny),
- fObjcReplacmentClasses(false), fAllDirectDylibsLoaded(false)
-{
- 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;
- case CPU_TYPE_ARM:
- fArchitectureName = "arm";
- 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.
- // the first one found is presumably the architecture to link
- uint8_t buffer[sizeof(mach_header_64)];
- std::vector<Options::FileInfo>& files = fOptions.getInputFiles();
- for (std::vector<Options::FileInfo>::iterator it = files.begin(); it != files.end(); ++it) {
- int fd = ::open(it->path, O_RDONLY, 0);
- if ( fd != -1 ) {
- ssize_t amount = read(fd, buffer, sizeof(buffer));
- ::close(fd);
- if ( amount >= (ssize_t)sizeof(buffer) ) {
- if ( mach_o::relocatable::Reader<ppc>::validFile(buffer) ) {
- //warning("-arch not used, infering -arch ppc based on %s", it->path);
- return CPU_TYPE_POWERPC;
- }
- else if ( mach_o::relocatable::Reader<ppc64>::validFile(buffer) ) {
- //warning("-arch not used, infering -arch ppc64 based on %s", it->path);
- return CPU_TYPE_POWERPC64;
- }
- else if ( mach_o::relocatable::Reader<x86>::validFile(buffer) ) {
- //warning("-arch not used, infering -arch i386 based on %s", it->path);
- return CPU_TYPE_I386;
- }
- else if ( mach_o::relocatable::Reader<x86_64>::validFile(buffer) ) {
- //warning("-arch not used, infering -arch x86_64 based on %s", it->path);
- return CPU_TYPE_X86_64;
- }
- else if ( mach_o::relocatable::Reader<arm>::validFile(buffer) ) {
- //warning("-arch not used, infering -arch arm based on %s", it->path);
- return CPU_TYPE_ARM;
- }
- }
- }
- }
-
- // no thin .o files found, so default to same architecture this was built as
- warning("-arch not specified");
-#if __ppc__
- return CPU_TYPE_POWERPC;
-#elif __i386__
- return CPU_TYPE_I386;
-#elif __ppc64__
- return CPU_TYPE_POWERPC64;
-#elif __x86_64__
- return CPU_TYPE_X86_64;
-#elif __arm__
- return CPU_TYPE_ARM;
-#else
- #error unknown default architecture
-#endif
-}
-
-
-void Linker::addInputFile(ObjectFile::Reader* reader, const Options::FileInfo& info)
-{
- fInputFiles.push_back(reader);
- fDylibOptionsMap[reader] = info.options;
-}
-
-void Linker::setOutputFile(ExecutableFile::Writer* writer)
-{
- 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()
-{
- fStartLoadAndResolveTime = mach_absolute_time();
- 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::optimize()
-{
- // give each reader a chance to do any optimizations
- std::vector<class ObjectFile::Atom*> newAtoms;
- std::vector<const char *> additionalUndefines;
- for (std::vector<class ObjectFile::Reader*>::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) {
- (*it)->optimize(fAllAtoms, newAtoms, additionalUndefines, fNextInputOrdinal, fOutputFile,
- fOptions.allGlobalsAreDeadStripRoots(), (int)fOptions.outputKind(), fOptions.verbose(),
- fOptions.saveTempFiles(), fOptions.getOutputFilePath(), fOptions.positionIndependentExecutable(),
- fOptions.allowTextRelocs());
- }
-
- // add all newly created atoms to fAllAtoms and update symbol table
- this->addAtoms(newAtoms);
-
- // Make sure all atoms have a section. Atoms that were not originally in a mach-o file could
- // not have their section set until now.
- for(std::vector<class ObjectFile::Atom*>::iterator itr = fAllAtoms.begin(); itr != fAllAtoms.end(); ++itr) {
- ObjectFile::Atom *atom = *itr;
- if ( atom->getSection() == NULL )
- atom->setSection(Section::find(atom->getSectionName(), atom->getSegment().getName(), atom->isZeroFill()));
- }
-
- // resolve new undefines
- for(std::vector<const char*>::iterator riter = additionalUndefines.begin(); riter != additionalUndefines.end(); ++riter) {
- const char *targetName = *riter;
- //fprintf(stderr, "LTO additional undefine: %s\n", targetName);
- ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName);
- if ( target == NULL) {
- // mark that this symbol is needed
- fGlobalSymbolTable.require(targetName);
- // try to find it in some library
- this->addJustInTimeAtoms(targetName);
- }
- }
-
- if ( fOptions.deadStrip() != Options::kDeadStripOff ) {
- fLiveAtoms.clear();
- this->deadStripResolve();
- }
- else {
- this->checkUndefines();
- this->resolveReferences();
- }
-}
-
-void Linker::link()
-{
- this->buildAtomList();
- this->loadAndResolve();
- this->optimize();
- this->checkObjC();
- this->processDTrace();
- this->tweakLayout();
- this->sortSections();
- this->sortAtoms();
- this->writeDotOutput();
- this->collectDebugInfo();
- this->writeOutput();
- this->printStatistics();
-
- if ( fOptions.pauseAtEnd() )
- sleep(10);
-}
-
-void Linker::printTime(const char* msg, uint64_t partTime, uint64_t totalTime)
-{
- static uint64_t sUnitsPerSecond = 0;
- if ( sUnitsPerSecond == 0 ) {
- struct mach_timebase_info timeBaseInfo;
- if ( mach_timebase_info(&timeBaseInfo) == KERN_SUCCESS ) {
- sUnitsPerSecond = 1000000000ULL * timeBaseInfo.denom / timeBaseInfo.numer;
- //fprintf(stderr, "sUnitsPerSecond=%llu\n", sUnitsPerSecond);
- }
- }
- if ( partTime < sUnitsPerSecond ) {
- uint32_t milliSecondsTimeTen = (partTime*10000)/sUnitsPerSecond;
- uint32_t milliSeconds = milliSecondsTimeTen/10;
- uint32_t percentTimesTen = (partTime*1000)/totalTime;
- uint32_t percent = percentTimesTen/10;
- fprintf(stderr, "%s: %u.%u milliseconds (%u.%u%%)\n", msg, milliSeconds, milliSecondsTimeTen-milliSeconds*10, percent, percentTimesTen-percent*10);
- }
- else {
- uint32_t secondsTimeTen = (partTime*10)/sUnitsPerSecond;
- uint32_t seconds = secondsTimeTen/10;
- uint32_t percentTimesTen = (partTime*1000)/totalTime;
- uint32_t percent = percentTimesTen/10;
- fprintf(stderr, "%s: %u.%u seconds (%u.%u%%)\n", msg, seconds, secondsTimeTen-seconds*10, percent, percentTimesTen-percent*10);
- }
-}
-
-char* Linker::commatize(uint64_t in, char* out)
-{
- char* result = out;
- char rawNum[30];
- sprintf(rawNum, "%llu", in);
- const int rawNumLen = strlen(rawNum);
- for(int i=0; i < rawNumLen-1; ++i) {
- *out++ = rawNum[i];
- if ( ((rawNumLen-i) % 3) == 1 )
- *out++ = ',';
- }
- *out++ = rawNum[rawNumLen-1];
- *out = '\0';
- return result;
-}
-
-void Linker::getVMInfo(vm_statistics_data_t& info)
-{
- mach_msg_type_number_t count = sizeof(vm_statistics_data_t) / sizeof(natural_t);
- kern_return_t error = host_statistics(mach_host_self(), HOST_VM_INFO,
- (host_info_t)&info, &count);
- if (error != KERN_SUCCESS) {
- bzero(&info, sizeof(vm_statistics_data_t));
- }
-}
-
-void Linker::printStatistics()
-{
- fEndTime = mach_absolute_time();
- if ( fOptions.printStatistics() ) {
- vm_statistics_data_t endVMInfo;
- getVMInfo(endVMInfo);
-
- uint64_t totalTime = fEndTime - fStartTime;
- printTime("ld total time", totalTime, totalTime);
- printTime(" option parsing time", fStartCreateReadersTime - fStartTime, totalTime);
- printTime(" object file processing",fStartCreateWriterTime - fStartCreateReadersTime, totalTime);
- printTime(" output file setup", fStartBuildAtomsTime - fStartCreateWriterTime, totalTime);
- printTime(" build atom list", fStartLoadAndResolveTime - fStartBuildAtomsTime, totalTime);
- printTime(" resolve references", fStartSortTime - fStartLoadAndResolveTime, totalTime);
- printTime(" sort output", fStartDebugTime - fStartSortTime, totalTime);
- printTime(" process debug info", fStartWriteTime - fStartDebugTime, totalTime);
- printTime(" write output", fEndTime - fStartWriteTime, totalTime);
- fprintf(stderr, "pageins=%u, pageouts=%u, faults=%u\n", endVMInfo.pageins-fStartVMInfo.pageins,
- endVMInfo.pageouts-fStartVMInfo.pageouts, endVMInfo.faults-fStartVMInfo.faults);
- char temp[40];
- fprintf(stderr, "processed %3u object files, totaling %15s bytes\n", fTotalObjectLoaded, commatize(fTotalObjectSize, temp));
- fprintf(stderr, "processed %3u archive files, totaling %15s bytes\n", fTotalArchivesLoaded, commatize(fTotalArchiveSize, temp));
- fprintf(stderr, "processed %3u dylib files\n", fTotalDylibsLoaded);
- fprintf(stderr, "wrote output file totaling %15s bytes\n", commatize(fOutputFileSize, temp));
- }
-}
-
-inline void Linker::addAtom(ObjectFile::Atom& atom)
-{
- // add to list of all atoms
- fAllAtoms.push_back(&atom);
-
- 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->getTargetBinding() == ObjectFile::Reference::kUnboundByName )
- fGlobalSymbolTable.require(reference->getTargetName());
- if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName )
- fGlobalSymbolTable.require(reference->getFromTargetName());
- if ( reference->getTargetBinding() == ObjectFile::Reference::kDontBind )
- addDtraceProbe(atom, reference->getFixUpOffset(), reference->getTargetName());
- }
- // update total size info (except for __ZEROPAGE atom)
- if ( atom.getSegment().isContentReadable() ) {
- fTotalSize += atom.getSize();
- if ( atom.isZeroFill() )
- fTotalZeroFillSize += atom.getSize();
- }
- }
- else {
- if ( atom.dontDeadStrip() )
- fLiveRootAtoms.insert(&atom);
- }
-
- // if in global namespace, add atom itself to symbol table
- ObjectFile::Atom::Scope scope = atom.getScope();
- const char* name = atom.getName();
- if ( (scope != ObjectFile::Atom::scopeTranslationUnit) && (name != NULL) ) {
- // update scope based on export list
- if ( fOptions.hasExportRestrictList() ) {
- if ( scope == ObjectFile::Atom::scopeGlobal ) {
- // check for globals that are downgraded to hidden
- bool doExport = fOptions.shouldExport(name);
- if ( !doExport ) {
- atom.setScope(ObjectFile::Atom::scopeLinkageUnit);
- }
- }
- else if ( scope == ObjectFile::Atom::scopeLinkageUnit ) {
- // check for hiddens that were requested to be exported
- if ( fOptions.hasExportMaskList() && fOptions.shouldExport(name) ) {
- warning("cannot export hidden symbol %s from %s", name, atom.getFile()->getPath());
- }
- }
- }
- // add to symbol table
- if ( fOptions.outputKind() == Options::kObjectFile ) {
- // in ld -r mode don't add .eh symbols to symbol table
- // instead kGroupSubordinate references will keep them paired
- // with their functions.
- const char* sectionName = atom.getSectionName();
- if ( (sectionName != NULL) && (strcmp(sectionName, "__eh_frame") != 0) )
- fGlobalSymbolTable.add(atom);
- }
- else {
- fGlobalSymbolTable.add(atom);
- }
- }
-
- // record section orders so output file can have same order
- if (atom.getSectionName())
- atom.setSection(Section::find(atom.getSectionName(), atom.getSegment().getName(), atom.isZeroFill()));
-}
-
-
-void Linker::markDead(ObjectFile::Atom* atom)
-{
- fDeadAtoms.insert(atom);
- //
- // The kGroupSubordinate reference kind is used to model group comdat.
- // The "signature" atom in the group has a kGroupSubordinate reference to
- // all other members of the group. So, if the signature atom is
- // coalesced away, all other atoms in the group should also be removed.
- //
- std::vector<class ObjectFile::Reference*>& references = atom->getReferences();
- for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) {
- ObjectFile::Reference* ref = *rit;
- if ( ref->getKind() == 2 /*kGroupSubordinate*/ ) { // FIX FIX
- ObjectFile::Atom* targetAtom = &(ref->getTarget());
- if ( targetAtom == NULL ) {
- warning("%s has a group reference to %s but is not bound", atom->getDisplayName(), ref->getTargetName());
- }
- else {
- if ( targetAtom->getScope() != ObjectFile::Atom::scopeTranslationUnit ) {
- // ok for .eh symbols to be not static in -r mode
- if ( (fOptions.outputKind() != Options::kObjectFile) || (strcmp(targetAtom->getSectionName(), "__eh_frame") != 0) )
- warning("%s is in a comdat group but its scope is not static", targetAtom->getDisplayName());
- }
- this->markDead(targetAtom);
- }
- }
- }
-}
-
-void Linker::updateConstraints(ObjectFile::Reader* reader)
-{
- // check objc objects were compiled compatibly
- ObjectFile::Reader::ObjcConstraint objcAddition = reader->getObjCConstraint();
- if ( reader->getInstallPath() == NULL ) {
- // adding a .o file
- switch ( objcAddition ) {
- case ObjectFile::Reader::kObjcNone:
- break;
- case ObjectFile::Reader::kObjcRetainRelease:
- if ( fCurrentObjCConstraint == ObjectFile::Reader::kObjcGC )
- throwf("%s built with incompatible Garbage Collection settings to link with previous .o files", reader->getPath());
- fCurrentObjCConstraint = ObjectFile::Reader::kObjcRetainRelease;
- break;
- case ObjectFile::Reader::kObjcRetainReleaseOrGC:
- if ( fCurrentObjCConstraint == ObjectFile::Reader::kObjcNone )
- fCurrentObjCConstraint = ObjectFile::Reader::kObjcRetainReleaseOrGC;
- break;
- case ObjectFile::Reader::kObjcGC:
- if ( fCurrentObjCConstraint == ObjectFile::Reader::kObjcRetainRelease )
- throwf("%s built with incompatible Garbage Collection settings to link with previous .o files", reader->getPath());
- fCurrentObjCConstraint = ObjectFile::Reader::kObjcGC;
- break;
- }
- }
- if ( reader->objcReplacementClasses() )
- fObjcReplacmentClasses = true;
-
- // check cpu sub-types for stricter sub-type
- fCurrentCpuConstraint = (ObjectFile::Reader::CpuConstraint)reader->updateCpuConstraint(fCurrentCpuConstraint);
-}
-
-inline void Linker::addAtoms(std::vector<class ObjectFile::Atom*>& atoms)
-{
- bool scanAll = fOptions.readerOptions().fFullyLoadArchives || fOptions.readerOptions().fLoadAllObjcObjectsFromArchives;
- bool first = true;
- for (std::vector<ObjectFile::Atom*>::iterator it=atoms.begin(); it != atoms.end(); it++) {
- // usually we only need to get the first atom's reader, but
- // with -all_load all atoms from all .o files come come back together
- // so we need to scan all atoms
- if ( first || scanAll ) {
- // update fReadersThatHaveSuppliedAtoms
- ObjectFile::Reader* reader = (*it)->getFile();
- if ( std::find(fReadersThatHaveSuppliedAtoms.begin(), fReadersThatHaveSuppliedAtoms.end(), reader)
- == fReadersThatHaveSuppliedAtoms.end() ) {
- fReadersThatHaveSuppliedAtoms.push_back(reader);
- updateConstraints(reader);
- }
- }
- this->addAtom(**it);
- first = false;
- }
-}
-
-void Linker::logArchive(ObjectFile::Reader* reader)
-{
- if ( (fArchiveReaders.count(reader) != 0) && (fArchiveReadersLogged.count(reader) == 0) ) {
- fArchiveReadersLogged.insert(reader);
- const char* fullPath = reader->getPath();
- char realName[MAXPATHLEN];
- if ( realpath(fullPath, realName) != NULL )
- fullPath = realName;
- logTraceInfo("[Logging for XBS] Used static archive: %s\n", fullPath);
- }
-}
-
-
-void Linker::buildAtomList()
-{
- fStartBuildAtomsTime = mach_absolute_time();
- // add initial undefines from -u option
- std::vector<const char*>& initialUndefines = fOptions.initialUndefines();
- for (std::vector<const char*>::iterator it=initialUndefines.begin(); it != initialUndefines.end(); it++) {
- fGlobalSymbolTable.require(*it);
- }
-
- // writer can contribute atoms
- this->addAtoms(fOutputFile->getAtoms());
-
- // each reader contributes atoms
- for (std::vector<class ObjectFile::Reader*>::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) {
- ObjectFile::Reader* reader = *it;
- std::vector<class ObjectFile::Atom*>& atoms = reader->getAtoms();
- this->addAtoms(atoms);
- if ( fOptions.readerOptions().fTraceArchives && (atoms.size() != 0) )
- logArchive(reader);
- }
-
- // extra command line section always at end
- std::vector<Options::ExtraSection>& extraSections = fOptions.extraSections();
- for( std::vector<Options::ExtraSection>::iterator it=extraSections.begin(); it != extraSections.end(); ++it) {
- this->addAtoms((new opaque_section::Reader(it->segmentName, it->sectionName, it->path, it->data, it->dataLen, fNextInputOrdinal))->getAtoms());
- fNextInputOrdinal += it->dataLen;
- }
-}
-
-static const char* pathLeafName(const char* path)
-{
- const char* shortPath = strrchr(path, '/');
- if ( shortPath == NULL )
- return path;
- else
- return &shortPath[1];
-}
-
-void Linker::loadUndefines()
-{
- // keep looping until no more undefines were added in last loop
- unsigned int undefineCount = 0xFFFFFFFF;
- while ( undefineCount != fGlobalSymbolTable.getRequireCount() ) {
- undefineCount = fGlobalSymbolTable.getRequireCount();
- std::vector<const char*> undefineNames;
- fGlobalSymbolTable.getNeededNames(false, undefineNames);
- for(std::vector<const char*>::iterator it = undefineNames.begin(); it != undefineNames.end(); ++it) {
- const char* name = *it;
- ObjectFile::Atom* possibleAtom = fGlobalSymbolTable.find(name);
- if ( (possibleAtom == NULL)
- || ((possibleAtom->getDefinitionKind()==ObjectFile::Atom::kWeakDefinition)
- && (fOptions.outputKind() != Options::kObjectFile)
- && (possibleAtom->getScope() == ObjectFile::Atom::scopeGlobal)) ) {
- std::vector<class ObjectFile::Atom*>* atoms = this->addJustInTimeAtoms(name);
- if ( atoms != NULL )
- delete atoms;
- }
- }
- }
-}
-
-// temp hack for rdar://problem/4718189 map ObjC class names to new runtime names
-class ExportedObjcClass
-{
-public:
- ExportedObjcClass(Options& opt) : fOptions(opt) {}
-
- bool operator()(const char* name) const {
- if ( fOptions.shouldExport(name) ) {
- if ( strncmp(name, ".objc_class_name_", 17) == 0 )
- return true;
- if ( strncmp(name, "_OBJC_CLASS_$_", 14) == 0 )
- return true;
- if ( strncmp(name, "_OBJC_METACLASS_$_", 18) == 0 )
- return true;
- }
- //fprintf(stderr, "%s is not exported\n", name);
- return false;
- }
-private:
- Options& fOptions;
-};
-
-
-void Linker::checkUndefines()
-{
- // error out on any remaining undefines
- bool doPrint = true;
- bool doError = true;
- switch ( fOptions.undefinedTreatment() ) {
- case Options::kUndefinedError:
- break;
- case Options::kUndefinedDynamicLookup:
- doError = false;
- break;
- case Options::kUndefinedWarning:
- doError = false;
- break;
- case Options::kUndefinedSuppress:
- doError = false;
- doPrint = false;
- break;
- }
- std::vector<const char*> unresolvableUndefines;
- fGlobalSymbolTable.getNeededNames(false, unresolvableUndefines);
-
- // temp hack for rdar://problem/4718189 map ObjC class names to new runtime names
- // ignore unresolved references to Objc class names that are listed in -exported_symbols_list
- if ( fOptions.hasExportRestrictList() )
- unresolvableUndefines.erase(std::remove_if(unresolvableUndefines.begin(), unresolvableUndefines.end(), ExportedObjcClass(fOptions)), unresolvableUndefines.end());
-
- const int unresolvableCount = unresolvableUndefines.size();
- int unresolvableExportsCount = 0;
- if ( unresolvableCount != 0 ) {
- if ( doPrint ) {
- 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);
- // scan all atoms for references
- bool foundAtomReference = false;
- for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) {
- ObjectFile::Atom* atom = *it;
- std::vector<class ObjectFile::Reference*>& references = atom->getReferences();
- for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) {
- ObjectFile::Reference* reference = *rit;
- if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) {
- if ( strcmp(reference->getTargetName(), name) == 0 ) {
- fprintf(stderr, " %s in %s\n", atom->getDisplayName(), pathLeafName(atom->getFile()->getPath()));
- foundAtomReference = true;
- }
- }
- if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) {
- if ( strcmp(reference->getFromTargetName(), name) == 0 ) {
- fprintf(stderr, " %s in %s\n", atom->getDisplayName(), pathLeafName(atom->getFile()->getPath()));
- foundAtomReference = true;
- }
- }
- }
- }
- // scan command line options
- if ( !foundAtomReference && fOptions.hasExportRestrictList() && fOptions.shouldExport(name) ) {
- fprintf(stderr, " -exported_symbols_list command line option\n");
- ++unresolvableExportsCount;
- }
- }
- }
- if ( doError )
- throw "symbol(s) not found";
- }
-
- // for each tentative definition in symbol table look for dylib that exports same symbol name
- if ( fGlobalSymbolTable.hasExternalTentativeDefinitions() ) {
- for (SymbolTable::Mapper::iterator it=fGlobalSymbolTable.begin(); it != fGlobalSymbolTable.end(); ++it) {
- ObjectFile::Atom* atom = it->second;
- if ( (atom != NULL) && (atom->getDefinitionKind()==ObjectFile::Atom::kTentativeDefinition)
- && (atom->getScope() == ObjectFile::Atom::scopeGlobal) ) {
- // look for dylibs that export same name as used by global tentative definition
- addJustInTimeAtoms(atom->getName(), true);
- }
- }
- }
-
- // if we have no weak symbols, see if we override some weak symbol in some dylib
- if ( !fGlobalSymbolTable.hasExternalWeakDefinitions() ) {
- bool done = false;
- for (SymbolTable::Mapper::iterator it=fGlobalSymbolTable.begin(); !done && (it != fGlobalSymbolTable.end()); ++it) {
- ObjectFile::Atom* atom = it->second;
- if ( (atom != NULL) && (atom->getDefinitionKind()==ObjectFile::Atom::kRegularDefinition)
- && (atom->getScope() == ObjectFile::Atom::scopeGlobal) ) {
- const char* name = atom->getName();
- //fprintf(stderr, "looking for dylibs with a weak %s\n", name);
- // look for dylibs with weak exports of the same name
- for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) {
- ObjectFile::Reader* reader = it->second;
- if ( reader->hasWeakExternals() ) {
- std::vector<class ObjectFile::Atom*>* atoms = reader->getJustInTimeAtomsFor(name);
- if ( atoms != NULL ) {
- //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() );
- // if this is a weak definition in a dylib
- if ( (atoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) {
- fGlobalSymbolTable.setHasExternalWeakDefinitions();
- done = true;
- break;
- }
- }
- }
- }
- }
- }
- }
-
-}
-
-
-
-std::vector<class ObjectFile::Atom*>* Linker::addJustInTimeAtoms(const char* name, bool dylibsOnly)
-{
- // when creating final linked image, writer gets first chance
- if ( fOptions.outputKind() != Options::kObjectFile ) {
- std::vector<class ObjectFile::Atom*>* atoms = fOutputFile->getJustInTimeAtomsFor(name);
- if ( atoms != NULL ) {
- this->addAtoms(*atoms);
- //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, fOutputFile->getPath() );
- return atoms; // found a definition, no need to search anymore
- }
- }
-
- // give readers a chance
- for (std::vector<class ObjectFile::Reader*>::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) {
- ObjectFile::Reader* reader = *it;
- if ( reader != NULL ) {
- // if this reader is a static archive that has the symbol we need, pull in all atoms in that module
- // if this reader is a dylib that exports the symbol we need, have it synthesize an atom for us.
- //fprintf(stderr, "addJustInTimeAtoms(%s), looking in reader %s\n", name, reader->getPath() );
- bool isDylibReader = (reader->getInstallPath() != NULL);
- if ( !dylibsOnly || isDylibReader ) {
- std::vector<class ObjectFile::Atom*>* atoms = reader->getJustInTimeAtomsFor(name);
- if ( atoms != NULL ) {
- this->addAtoms(*atoms);
- //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() );
- if ( !isDylibReader && fOptions.readerOptions().fTraceArchives ) {
- logArchive(reader);
- }
- // if this is a weak definition in a dylib
- if ( isDylibReader && (atoms->size() == 1) && (atoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) {
- // keep looking for a non-weak definition
- }
- else {
- // found a definition, no need to search anymore
- return atoms;
- }
- }
- }
- }
- }
-
- // for two level namesapce, give all implicitly link dylibs a chance
- if ( fOptions.nameSpace() == Options::kTwoLevelNameSpace ) {
- for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) {
- if ( it->second->implicitlyLinked() ) {
- //fprintf(stderr, "addJustInTimeAtoms(%s), looking in implicitly linked %s\n", name, it->second->getPath() );
- std::vector<class ObjectFile::Atom*>* atoms = it->second->getJustInTimeAtomsFor(name);
- if ( atoms != NULL ) {
- this->addAtoms(*atoms);
- //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() );
- // if this is a weak definition in a dylib
- if ( (atoms->size() == 1) && (atoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) {
- // keep looking for a non-weak definition
- }
- else {
- // found a definition, no need to search anymore
- return atoms;
- }
- }
- }
- }
- }
-
- // for flat namespace, give indirect dylibs
- if ( fOptions.nameSpace() != Options::kTwoLevelNameSpace ) {
- for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) {
- if ( ! it->second->explicitlyLinked() ) {
- std::vector<class ObjectFile::Atom*>* atoms = it->second->getJustInTimeAtomsFor(name);
- if ( atoms != NULL ) {
- this->addAtoms(*atoms);
- //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() );
- return atoms; // found a definition, no need to search anymore
- }
- }
- }
- }
-
- // writer creates a proxy in two cases:
- // 1) ld -r is being used to create a .o file
- // 2) -undefined dynamic_lookup is being used
- // 3) -U _foo is being used
- if ( (fOptions.outputKind() == Options::kObjectFile)
- || ((fOptions.undefinedTreatment() != Options::kUndefinedError) && !dylibsOnly)
- || (fOptions.someAllowedUndefines() && !dylibsOnly) ) {
- ObjectFile::Atom* atom = fOutputFile->getUndefinedProxyAtom(name);
- if ( atom != NULL ) {
- this->addAtom(*atom);
- return NULL;
- }
- }
- //fprintf(stderr, "addJustInTimeAtoms(%s) => not found\n", name);
- return NULL;
-}
-
-void Linker::resolve(ObjectFile::Reference* reference)
-{
- // look in global symbol table
- const char* targetName = reference->getTargetName();
- ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName);
- if ( target == NULL ) {
- fprintf(stderr, "Undefined symbol: %s\n", targetName);
- }
- reference->setTarget(*target, reference->getTargetOffset());
-}
-
-void Linker::resolveFrom(ObjectFile::Reference* reference)
-{
- // handle references that have two (from and to) targets
- const char* fromTargetName = reference->getFromTargetName();
- ObjectFile::Atom* fromTarget = fGlobalSymbolTable.find(fromTargetName);
- if ( fromTarget == NULL ) {
- fprintf(stderr, "Undefined symbol: %s\n", fromTargetName);
- }
- reference->setFromTarget(*fromTarget);
-}
-
-
-void Linker::resolveReferences()
-{
- // note: the atom list may grow during this loop as libraries supply needed atoms
- for (unsigned int j=0; j < fAllAtoms.size(); ++j) {
- ObjectFile::Atom* atom = fAllAtoms[j];
- std::vector<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->getTargetBinding() == ObjectFile::Reference::kUnboundByName )
- this->resolve(reference);
- if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName )
- this->resolveFrom(reference);
- }
- }
-}
-
-
-// used to remove stabs associated with atoms that won't be in output file
-class NotInSet
-{
-public:
- NotInSet(std::set<ObjectFile::Atom*>& theSet) : fSet(theSet) {}
-
- bool operator()(const ObjectFile::Reader::Stab& stab) const {
- if ( stab.atom == NULL )
- return false; // leave stabs that are not associated with any atome
- else
- return ( fSet.count(stab.atom) == 0 );
- }
-
-private:
- std::set<ObjectFile::Atom*>& fSet;
-};
-
-
-class NotLive
-{
-public:
- NotLive(std::set<ObjectFile::Atom*>& set) : fLiveAtoms(set) {}
-
- bool operator()(ObjectFile::Atom*& atom) const {
- //if ( fLiveAtoms.count(atom) == 0 )
- // fprintf(stderr, "dead strip %s\n", atom->getDisplayName());
- return ( fLiveAtoms.count(atom) == 0 );
- }
-private:
- std::set<ObjectFile::Atom*>& fLiveAtoms;
-};
-
-
-void Linker::addJustInTimeAtomsAndMarkLive(const char* name)
-{
- std::vector<class ObjectFile::Atom*>* atoms = this->addJustInTimeAtoms(name);
- if ( atoms != NULL ) {
- if ( fOptions.allGlobalsAreDeadStripRoots() ) {
- for (std::vector<ObjectFile::Atom*>::iterator it=atoms->begin(); it != atoms->end(); it++) {
- ObjectFile::Atom* atom = *it;
- if ( atom->getScope() == ObjectFile::Atom::scopeGlobal ) {
- WhyLiveBackChain rootChain;
- rootChain.previous = NULL;
- rootChain.name = atom->getDisplayName();
- this->markLive(*atom, &rootChain);
- }
- }
- }
- delete atoms;
- }
-}
-
-void Linker::markLive(ObjectFile::Atom& atom, struct Linker::WhyLiveBackChain* previous)
-{
- 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
- fLiveAtoms.insert(&atom);
- // update total size info (except for __ZEROPAGE atom)
- if ( atom.getSegment().isContentReadable() ) {
- fTotalSize += atom.getSize();
- if ( atom.isZeroFill() )
- fTotalZeroFillSize += atom.getSize();
- }
- // and all atoms it references
- 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->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) {
- // look in global symbol table
- const char* targetName = reference->getTargetName();
- ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName);
- if ( target == NULL ) {
- // load archives or dylibs
- this->addJustInTimeAtomsAndMarkLive(targetName);
- }
- // look again
- target = fGlobalSymbolTable.find(targetName);
- if ( target != NULL ) {
- reference->setTarget(*target, reference->getTargetOffset());
- }
- else {
- // mark as undefined, for later error processing
- fAtomsWithUnresolvedReferences.push_back(&atom);
- fGlobalSymbolTable.require(targetName);
- }
- }
- switch ( reference->getTargetBinding() ) {
- case ObjectFile::Reference::kBoundDirectly:
- case ObjectFile::Reference::kBoundByName:
- thisChain.name = reference->getTargetName();
- markLive(reference->getTarget(), &thisChain);
- break;
- case ObjectFile::Reference::kDontBind:
- addDtraceProbe(atom, reference->getFixUpOffset(), reference->getTargetName());
- break;
- case ObjectFile::Reference::kUnboundByName:
- // do nothing
- break;
- }
- // do the same as above, for "from target"
- if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) {
- // look in global symbol table
- const char* targetName = reference->getFromTargetName();
- ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName);
- if ( target == NULL ) {
- // load archives or dylibs
- this->addJustInTimeAtomsAndMarkLive(targetName);
- }
- // look again
- target = fGlobalSymbolTable.find(targetName);
- if ( target != NULL ) {
- reference->setFromTarget(*target);
- }
- else {
- // mark as undefined, for later error processing
- fGlobalSymbolTable.require(targetName);
- }
- }
- switch ( reference->getFromTargetBinding() ) {
- case ObjectFile::Reference::kBoundDirectly:
- case ObjectFile::Reference::kBoundByName:
- thisChain.name = reference->getFromTargetName();
- markLive(reference->getFromTarget(), &thisChain);
- break;
- case ObjectFile::Reference::kUnboundByName:
- case ObjectFile::Reference::kDontBind:
- // do nothing
- break;
- }
- }
- }
-}
-
-
-void Linker::addLiveRoot(const char* name)
-{
- ObjectFile::Atom* target = fGlobalSymbolTable.find(name);
- if ( target == NULL ) {
- this->addJustInTimeAtomsAndMarkLive(name);
- target = fGlobalSymbolTable.find(name);
- }
- if ( target != NULL )
- fLiveRootAtoms.insert(target);
-}
-
-
-void Linker::deadStripResolve()
-{
- // add main() to live roots
- ObjectFile::Atom* entryPoint = this->entryPoint(false);
- if ( entryPoint != NULL )
- fLiveRootAtoms.insert(entryPoint);
-
- // add dyld_stub_binding_helper() to live roots
- ObjectFile::Atom* dyldHelper = this->dyldHelper();
- if ( dyldHelper != NULL )
- fLiveRootAtoms.insert(dyldHelper);
-
- // if using lazy dylib loading, add dyld_lazy_dylib_stub_binding_helper() to live roots
- if ( fOptions.usingLazyDylibLinking() ) {
- ObjectFile::Atom* dyldLazyDylibHelper = this->dyldLazyLibraryHelper();
- if ( dyldLazyDylibHelper != NULL )
- fLiveRootAtoms.insert(dyldLazyDylibHelper);
- }
-
- // 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);
-
- // if -exported_symbols_list that has wildcards, we need to find all matches and make them the roots
- // <rdar://problem/5524973>
- if ( fOptions.hasWildCardExportRestrictList() ) {
- for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) {
- ObjectFile::Atom* atom = *it;
- if ( (atom->getScope() == ObjectFile::Atom::scopeGlobal)
- && (fDeadAtoms.count(atom) == 0)
- && fOptions.shouldExport(atom->getName()) )
- fLiveRootAtoms.insert(atom);
- }
- }
-
- // 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;
- if ( (atom->getScope() == ObjectFile::Atom::scopeGlobal) && (fDeadAtoms.count(atom) == 0) )
- fLiveRootAtoms.insert(atom);
- }
- }
-
- // 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);
- }
-
- // it is possible that there are unresolved references that can be resolved now
- // this can happen if the first reference to a common symbol in an archive.
- // common symbols are not in the archive TOC, but the .o could have been pulled in later.
- // <rdar://problem/4654131> ld64 while linking cc1 [ when dead_strip is ON]
- for (std::vector<ObjectFile::Atom*>::iterator it=fAtomsWithUnresolvedReferences.begin(); it != fAtomsWithUnresolvedReferences.end(); it++) {
- std::vector<class ObjectFile::Reference*>& references = (*it)->getReferences();
- for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) {
- ObjectFile::Reference* reference = *rit;
- if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) {
- ObjectFile::Atom* target = fGlobalSymbolTable.find(reference->getTargetName());
- if ( target != NULL ) {
- reference->setTarget(*target, reference->getTargetOffset());
- fLiveAtoms.insert(target);
- // by just adding this atom to fLiveAtoms set, we are assuming it has no
- // references, which is true for commons.
- if ( target->getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition )
- warning("internal error %s is not a tentative definition", target->getDisplayName());
- }
- }
- if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) {
- ObjectFile::Atom* target = fGlobalSymbolTable.find(reference->getFromTargetName());
- if ( target != NULL ) {
- reference->setFromTarget(*target);
- fLiveAtoms.insert(target);
- // by just adding this atom to fLiveAtoms set, we are assuming it has no
- // references, which is true for commons.
- if ( target->getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition )
- warning("internal error %s is not a tentative definition", target->getDisplayName());
- }
- }
- }
- }
-
- // now remove all non-live atoms from fAllAtoms
- fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), NotLive(fLiveAtoms)), fAllAtoms.end());
-}
-
-void Linker::checkObjC()
-{
- // check dylibs
- switch ( fCurrentObjCConstraint ) {
- case ObjectFile::Reader::kObjcNone:
- // can link against any dylib
- break;
- case ObjectFile::Reader::kObjcRetainRelease:
- // cannot link against GC-only dylibs
- for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) {
- if ( it->second->explicitlyLinked() ) {
- if ( it->second->getObjCConstraint() == ObjectFile::Reader::kObjcGC )
- throwf("this linkage unit uses Retain/Release. It cannot link against the GC-only dylib: %s", it->second->getPath());
- }
- }
- break;
- case ObjectFile::Reader::kObjcRetainReleaseOrGC:
- // can link against GC or RR dylibs
- break;
- case ObjectFile::Reader::kObjcGC:
- // cannot link against RR-only dylibs
- for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) {
- if ( it->second->explicitlyLinked() ) {
- if ( it->second->getObjCConstraint() == ObjectFile::Reader::kObjcRetainRelease )
- throwf("this linkage unit requires GC. It cannot link against Retain/Release dylib: %s", it->second->getPath());
- }
- }
- break;
- }
-
- // synthesize __OBJC __image_info atom if needed
- if ( fCurrentObjCConstraint != ObjectFile::Reader::kObjcNone ) {
- this->addAtom(fOutputFile->makeObjcInfoAtom(fCurrentObjCConstraint, fObjcReplacmentClasses));
- }
-}
-
-void Linker::addDtraceProbe(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* probeName)
-{
- if ( probeName != NULL ) {
- if ( strncmp(probeName, "___dtrace_probe$", 16) == 0 )
- fDtraceProbeSites.push_back(DTraceProbeInfo(&atom, offsetInAtom, probeName));
- else if ( strncmp(probeName, "___dtrace_isenabled$", 20) == 0 )
- fDtraceIsEnabledSites.push_back(DTraceProbeInfo(&atom, offsetInAtom, probeName));
- else if ( strncmp(probeName, "___dtrace_", 10) == 0 )
- fDtraceAtomToTypes[&atom].insert(probeName);
- else if ( fOptions.dTrace() && (strncmp(probeName, "__dtrace_probe$", 15) == 0) )
- fDtraceProbes.push_back(DTraceProbeInfo(&atom, offsetInAtom, probeName));
- }
-}
-
-static uint8_t pointerKind(cpu_type_t arch)
-{
- switch ( arch ) {
- case CPU_TYPE_POWERPC:
- return ppc::kPointer;
- case CPU_TYPE_POWERPC64:
- return ppc64::kPointer;
- case CPU_TYPE_I386:
- return x86::kPointer;
- case CPU_TYPE_X86_64:
- return x86_64::kPointer;
- case CPU_TYPE_ARM:
- return arm::kPointer;
- }
- throw "uknown architecture";
-}
-
-static uint8_t pcRelKind(cpu_type_t arch)
-{
- switch ( arch ) {
- case CPU_TYPE_POWERPC:
- return ppc::kPointerDiff32;
- case CPU_TYPE_POWERPC64:
- return ppc64::kPointerDiff32;
- case CPU_TYPE_I386:
- return x86::kPointerDiff;
- case CPU_TYPE_X86_64:
- return x86_64::kPointerDiff32;
- case CPU_TYPE_ARM:
- return arm::kPointerDiff;
- }
- throw "uknown architecture";
-}
-
-typedef uint8_t* (*oldcreatedof_func_t) (const char*, cpu_type_t, unsigned int, const char*[], const char*[], uint64_t offsetsInDOF[], size_t* size);
-typedef uint8_t* (*createdof_func_t)(cpu_type_t, unsigned int, const char*[], unsigned int, const char*[], const char*[], uint64_t offsetsInDOF[], size_t* size);
-
-
-void Linker::processDTrace()
-{
- // handle dtrace 2.0 static probes
- if ( (fOptions.outputKind() != Options::kObjectFile) && ((fDtraceProbeSites.size() != 0) || (fDtraceIsEnabledSites.size() != 0)) ) {
- // partition probes by provider name
- // The symbol names looks like:
- // "___dtrace_isenabled$" provider-name "$" probe-name [ "$"... ]
- // "___dtrace_probe$" provider-name "$" probe-name [ "$"... ]
- ProviderToProbes providerToProbes;
- std::vector<DTraceProbeInfo> emptyList;
- for(std::vector<DTraceProbeInfo>::iterator it = fDtraceProbeSites.begin(); it != fDtraceProbeSites.end(); ++it) {
- // ignore probes in functions that were coalesed away rdar://problem/5628149
- if ( fDeadAtoms.count((ObjectFile::Atom*)(it->atom)) == 0 ) {
- const char* providerStart = &it->probeName[16];
- const char* providerEnd = strchr(providerStart, '$');
- if ( providerEnd != NULL ) {
- char providerName[providerEnd-providerStart+1];
- strlcpy(providerName, providerStart, providerEnd-providerStart+1);
- ProviderToProbes::iterator pos = providerToProbes.find(providerName);
- if ( pos == providerToProbes.end() ) {
- const char* dup = strdup(providerName);
- providerToProbes[dup] = emptyList;
- }
- providerToProbes[providerName].push_back(*it);
- }
- }
- }
- for(std::vector<DTraceProbeInfo>::iterator it = fDtraceIsEnabledSites.begin(); it != fDtraceIsEnabledSites.end(); ++it) {
- // ignore probes in functions that were coalesed away rdar://problem/5628149
- if ( fDeadAtoms.count((ObjectFile::Atom*)(it->atom)) == 0 ) {
- const char* providerStart = &it->probeName[20];
- const char* providerEnd = strchr(providerStart, '$');
- if ( providerEnd != NULL ) {
- char providerName[providerEnd-providerStart+1];
- strlcpy(providerName, providerStart, providerEnd-providerStart+1);
- ProviderToProbes::iterator pos = providerToProbes.find(providerName);
- if ( pos == providerToProbes.end() ) {
- const char* dup = strdup(providerName);
- providerToProbes[dup] = emptyList;
- }
- providerToProbes[providerName].push_back(*it);
- }
- }
- }
-
- // create a DOF section for each provider
- int dofIndex=1;
- CStringSet sectionNamesUsed;
- for(ProviderToProbes::iterator pit = providerToProbes.begin(); pit != providerToProbes.end(); ++pit, ++dofIndex) {
- const char* providerName = pit->first;
- const std::vector<DTraceProbeInfo>& probes = pit->second;
-
- // open library and find dtrace_create_dof()
- void* handle = dlopen("/usr/lib/libdtrace.dylib", RTLD_LAZY);
- if ( handle == NULL )
- throwf("couldn't dlopen() /usr/lib/libdtrace.dylib: %s", dlerror());
- createdof_func_t pCreateDOF = (createdof_func_t)dlsym(handle, "dtrace_ld_create_dof");
- if ( pCreateDOF == NULL )
- throwf("couldn't find \"dtrace_ld_create_dof\" in /usr/lib/libdtrace.dylib: %s", dlerror());
- // build list of typedefs/stability infos for this provider
- CStringSet types;
- for(std::vector<DTraceProbeInfo>::const_iterator it = probes.begin(); it != probes.end(); ++it) {
- std::map<const ObjectFile::Atom*,CStringSet>::iterator pos = fDtraceAtomToTypes.find(it->atom);
- if ( pos != fDtraceAtomToTypes.end() ) {
- for(CStringSet::iterator sit = pos->second.begin(); sit != pos->second.end(); ++sit) {
- const char* providerStart = strchr(*sit, '$')+1;
- const char* providerEnd = strchr(providerStart, '$');
- if ( providerEnd != NULL ) {
- char aProviderName[providerEnd-providerStart+1];
- strlcpy(aProviderName, providerStart, providerEnd-providerStart+1);
- if ( strcmp(aProviderName, providerName) == 0 )
- types.insert(*sit);
- }
- }
- }
- }
- int typeCount = types.size();
- const char* typeNames[typeCount];
- //fprintf(stderr, "types for %s:\n", providerName);
- uint32_t index = 0;
- for(CStringSet::iterator it = types.begin(); it != types.end(); ++it) {
- typeNames[index] = *it;
- //fprintf(stderr, "\t%s\n", *it);
- ++index;
- }
-
- // build list of probe/isenabled sites
- const uint32_t probeCount = probes.size();
- const char* probeNames[probeCount];
- const char* funtionNames[probeCount];
- uint64_t offsetsInDOF[probeCount];
- index = 0;
- for(std::vector<DTraceProbeInfo>::const_iterator it = probes.begin(); it != probes.end(); ++it) {
- probeNames[index] = it->probeName;
- funtionNames[index] = it->atom->getName();
- offsetsInDOF[index] = 0;
- ++index;
- }
- //fprintf(stderr, "calling libtrace to create DOF\n");
- //for(uint32_t i=0; i < probeCount; ++i)
- // fprintf(stderr, " [%u]\t %s\t%s\n", i, probeNames[i], funtionNames[i]);
- // call dtrace library to create DOF section
- size_t dofSectionSize;
- uint8_t* p = (*pCreateDOF)(fArchitecture, typeCount, typeNames, probeCount, probeNames, funtionNames, offsetsInDOF, &dofSectionSize);
- if ( p != NULL ) {
- char sectionName[18];
- strcpy(sectionName, "__dof_");
- strlcpy(§ionName[6], providerName, 10);
- // create unique section name so each DOF is in its own section
- if ( sectionNamesUsed.count(sectionName) != 0 ) {
- sectionName[15] = '0';
- sectionName[16] = '\0';
- while ( sectionNamesUsed.count(sectionName) != 0 )
- ++sectionName[15];
- }
- sectionNamesUsed.insert(sectionName);
- char symbolName[strlen(providerName)+64];
- sprintf(symbolName, "__dtrace_dof_for_provider_%s", providerName);
- opaque_section::Reader* reader = new opaque_section::Reader("__TEXT", sectionName,
- "dtrace", p, dofSectionSize, fNextInputOrdinal, symbolName);
- fNextInputOrdinal += dofSectionSize;
- // add references
- for (uint32_t i=0; i < probeCount; ++i) {
- uint64_t offset = offsetsInDOF[i];
- //fprintf(stderr, "%s offset[%d]=0x%08llX\n", providerName, i, offset);
- if ( offset > dofSectionSize )
- throwf("offsetsInDOF[i]=%0llX > dofSectionSize=%0lX\n", i, offset, dofSectionSize);
- reader->addSectionReference(pcRelKind(fArchitecture), offset, probes[i].atom, probes[i].offset, reader->getAtoms()[0], 0);
- }
- this->addAtoms(reader->getAtoms());
- }
- else {
- throw "error creating dtrace DOF section";
- }
- }
- }
- // create a __DATA __dof section iff -dtrace option was used and static probes were found in .o files
- else if ( fOptions.dTrace() && (fDtraceProbes.size() != 0) ) {
- const uint32_t probeCount = fDtraceProbes.size();
- const char* labels[probeCount];
- const char* funtionNames[probeCount];
- uint64_t offsetsInDOF[probeCount];
-
- // open libray and find dtrace_ld64_create_dof()
- void* handle = dlopen("/usr/lib/libdtrace.dylib", RTLD_LAZY);
- if ( handle == NULL )
- throwf("couldn't dlopen() /usr/lib/libdtrace.dylib: %s\n", dlerror());
- oldcreatedof_func_t pCreateDOF = (oldcreatedof_func_t)dlsym(handle, "dtrace_ld64_create_dof");
- if ( pCreateDOF == NULL )
- throwf("couldn't find \"dtrace_ld64_create_dof\" in /usr/lib/libdtrace.dylib: %s\n", dlerror());
-
- // build argument list
- uint32_t index = 0;
- for(std::vector<DTraceProbeInfo>::iterator it = fDtraceProbes.begin(); it != fDtraceProbes.end(); ++it) {
- labels[index] = it->probeName;
- funtionNames[index] = it->atom->getName();
- offsetsInDOF[index] = 0;
- ++index;
- }
- size_t dofSectionSize;
- // call dtrace library to create DOF section
- uint8_t* p = (*pCreateDOF)(fOptions.dTraceScriptName(), fArchitecture, probeCount, labels, funtionNames, offsetsInDOF, &dofSectionSize);
- if ( p != NULL ) {
- opaque_section::Reader* reader = new opaque_section::Reader("__DATA", "__dof", "dtrace", p, dofSectionSize, fNextInputOrdinal);
- fNextInputOrdinal += dofSectionSize;
- // add references
- for (uint32_t i=0; i < probeCount; ++i) {
- uint64_t offset = offsetsInDOF[i];
- if ( offset > dofSectionSize )
- throwf("offsetsInDOF[i]=%0llX > dofSectionSize=%0lX", i, offset, dofSectionSize);
- reader->addSectionReference(pointerKind(fArchitecture), offset, fDtraceProbes[i].atom, fDtraceProbes[i].offset);
- }
- this->addAtoms(reader->getAtoms());
- }
- else {
- throw "error created dtrace DOF section";
- }
- }
-}
-
-
-static bool matchesObjectFile(ObjectFile::Atom* atom, const char* objectFileLeafName)
-{
- if ( objectFileLeafName == NULL )
- return true;
- const char* atomFullPath = atom->getFile()->getPath();
- const char* lastSlash = strrchr(atomFullPath, '/');
- if ( lastSlash != NULL ) {
- if ( strcmp(&lastSlash[1], objectFileLeafName) == 0 )
- return true;
- }
- else {
- if ( strcmp(atomFullPath, objectFileLeafName) == 0 )
- return true;
- }
- return false;
-}
-
-
-static bool usesAnonymousNamespace(const char* symbol)
-{
- return ( (strncmp(symbol, "__Z", 3) == 0) && (strstr(symbol, "_GLOBAL__N_") != NULL) );
-}
-
-
-//
-// convert:
-// __ZN20_GLOBAL__N__Z5main2v3barEv => _ZN-3barEv
-// __ZN37_GLOBAL__N_main.cxx_00000000_493A01A33barEv => _ZN-3barEv
-//
-static void canonicalizeAnonymousName(const char* inSymbol, char outSymbol[])
-{
- const char* globPtr = strstr(inSymbol, "_GLOBAL__N_");
- while ( isdigit(*(--globPtr)) )
- ; // loop
- char* endptr;
- unsigned long length = strtoul(globPtr+1, &endptr, 10);
- const char* globEndPtr = endptr + length;
- int startLen = globPtr-inSymbol+1;
- memcpy(outSymbol, inSymbol, startLen);
- outSymbol[startLen] = '-';
- strcpy(&outSymbol[startLen+1], globEndPtr);
-}
-
-
-ObjectFile::Atom* Linker::findAtom(const Options::OrderedSymbol& orderedSymbol)
-{
- ObjectFile::Atom* atom = fGlobalSymbolTable.find(orderedSymbol.symbolName);
- if ( atom != NULL ) {
- if ( matchesObjectFile(atom, orderedSymbol.objectFileName) )
- return atom;
- }
- else {
- // slow case. The requested symbol is not in symbol table, so might be static function
- static SymbolTable::Mapper hashTableOfTranslationUnitScopedSymbols;
- static SymbolTable::Mapper hashTableOfSymbolsWithAnonymousNamespace;
- static bool built = false;
- // build a hash_map the first time
- if ( !built ) {
- for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) {
- atom = *it;
- const char* name = atom->getName();
- if ( name != NULL) {
- if ( usesAnonymousNamespace(name) ) {
- // symbol that uses anonymous namespace
- char canonicalName[strlen(name)+2];
- canonicalizeAnonymousName(name, canonicalName);
- const char* hashName = strdup(canonicalName);
- SymbolTable::Mapper::iterator pos = hashTableOfSymbolsWithAnonymousNamespace.find(hashName);
- if ( pos == hashTableOfSymbolsWithAnonymousNamespace.end() )
- hashTableOfSymbolsWithAnonymousNamespace[hashName] = atom;
- else
- hashTableOfSymbolsWithAnonymousNamespace[hashName] = NULL; // collision, denote with NULL
- }
- else if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) {
- // static function or data
- SymbolTable::Mapper::iterator pos = hashTableOfTranslationUnitScopedSymbols.find(name);
- if ( pos == hashTableOfTranslationUnitScopedSymbols.end() )
- hashTableOfTranslationUnitScopedSymbols[name] = atom;
- else
- hashTableOfTranslationUnitScopedSymbols[name] = NULL; // collision, denote with NULL
- }
- }
- }
- //fprintf(stderr, "built hash table of %lu static functions\n", hashTableOfTranslationUnitScopedSymbols.size());
- built = true;
- }
-
- // look for name in hashTableOfTranslationUnitScopedSymbols
- SymbolTable::Mapper::iterator pos = hashTableOfTranslationUnitScopedSymbols.find(orderedSymbol.symbolName);
- if ( pos != hashTableOfTranslationUnitScopedSymbols.end() ) {
- if ( (pos->second != NULL) && matchesObjectFile(pos->second, orderedSymbol.objectFileName) ) {
- //fprintf(stderr, "found %s in hash table\n", orderedSymbol.symbolName);
- return pos->second;
- }
- if ( pos->second == NULL )
- // name is in hash table, but atom is NULL, so that means there are duplicates, so we use super slow way
- for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) {
- atom = *it;
- if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) {
- const char* name = atom->getName();
- if ( (name != NULL) && (strcmp(name, orderedSymbol.symbolName) == 0) ) {
- if ( matchesObjectFile(atom, orderedSymbol.objectFileName) ) {
- if ( fOptions.printOrderFileStatistics() )
- warning("%s specified in order_file but it exists in multiple .o files. "
- "Prefix symbol with .o filename in order_file to disambiguate", orderedSymbol.symbolName);
- return atom;
- }
- }
- }
- }
- }
-
- // look for name in hashTableOfSymbolsWithAnonymousNamespace
- if ( usesAnonymousNamespace(orderedSymbol.symbolName) ) {
- // symbol that uses anonymous namespace
- char canonicalName[strlen(orderedSymbol.symbolName)+2];
- canonicalizeAnonymousName(orderedSymbol.symbolName, canonicalName);
- SymbolTable::Mapper::iterator pos = hashTableOfSymbolsWithAnonymousNamespace.find(canonicalName);
- if ( pos != hashTableOfSymbolsWithAnonymousNamespace.end() ) {
- if ( (pos->second != NULL) && matchesObjectFile(pos->second, orderedSymbol.objectFileName) ) {
- //fprintf(stderr, "found %s in anonymous namespace hash table\n", canonicalName);
- return pos->second;
- }
- if ( pos->second == NULL )
- // name is in hash table, but atom is NULL, so that means there are duplicates, so we use super slow way
- for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) {
- atom = *it;
- const char* name = atom->getName();
- if ( (name != NULL) && usesAnonymousNamespace(name) ) {
- char canonicalAtomName[strlen(name)+2];
- canonicalizeAnonymousName(name, canonicalAtomName);
- if ( strcmp(canonicalAtomName, canonicalName) == 0 ) {
- if ( matchesObjectFile(atom, orderedSymbol.objectFileName) ) {
- if ( fOptions.printOrderFileStatistics() )
- warning("%s specified in order_file but it exists in multiple .o files. "
- "Prefix symbol with .o filename in order_file to disambiguate", orderedSymbol.symbolName);
- return atom;
- }
- }
- }
- }
- }
- }
- }
- return NULL;
-}
-
-
-void Linker::sortSections()
-{
- Section::assignIndexes();
-}
-
-
-//
-// Linker::sortAtoms()
-//
-// The purpose of this method is to take the graph of all Atoms and produce an ordered
-// sequence of atoms. The constraints are that: 1) all Atoms of the same Segment must
-// be contiguous, 2) all Atoms of the same Section must be contigous, 3) Atoms specified
-// in an order_file are seqenced as in the order_file and before Atoms not specified,
-// 4) Atoms in the same section from the same .o file should be contiguous and sequenced
-// in the same order they were in the .o file, 5) Atoms in the same Section but which came
-// from different .o files should be sequenced in the same order that the .o files
-// were passed to the linker (i.e. command line order).
-//
-// The way this is implemented is that the linker passes a "base ordinal" to each Reader
-// as it is constructed. The reader should construct it Atoms so that calling getOrdinal()
-// on its atoms returns a contiguous range of values starting at the base ordinal. Then
-// sorting is just sorting by section, then by ordinal.
-//
-// If an order_file is specified, it gets more complicated. First, an override-ordinal map
-// is created. It causes the sort routine to ignore the value returned by getOrdinal() and
-// use the override value instead. Next some Atoms must be layed out consecutively
-// (e.g. hand written assembly that does not end with return, but rather falls into
-// the next label). This is modeled in Readers via a "kFollowOn" reference. The use of
-// kFollowOn refernces produces "clusters" of atoms that must stay together.
-// If an order_file tries to move one atom, it may need to move a whole cluster. The
-// algorithm to do this models clusters using two maps. The "starts" maps maps any
-// atom in a cluster to the first Atom in the cluster. The "nexts" maps an Atom in a
-// cluster to the next Atom in the cluster. With this in place, while processing an
-// order_file, if any entry is in a cluster (in "starts" map), then the entire cluster is
-// given ordinal overrides.
-//
-void Linker::sortAtoms()
-{
- fStartSortTime = mach_absolute_time();
- // if -order_file is used, build map of atom ordinal overrides
- std::map<const ObjectFile::Atom*, uint32_t>* ordinalOverrideMap = NULL;
- std::map<const ObjectFile::Atom*, uint32_t> theOrdinalOverrideMap;
- const bool log = false;
- if ( fOptions.orderedSymbols().size() != 0 ) {
- // first make a pass to find all follow-on references and build start/next maps
- // which are a way to represent clusters of atoms that must layout together
- std::map<const ObjectFile::Atom*, const ObjectFile::Atom*> followOnStarts;
- std::map<const ObjectFile::Atom*, const ObjectFile::Atom*> followOnNexts;
- for (std::vector<ObjectFile::Atom*>::iterator ait=fAllAtoms.begin(); ait != fAllAtoms.end(); ait++) {
- ObjectFile::Atom* atom = *ait;
- std::vector<class ObjectFile::Reference*>& references = atom->getReferences();
- for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) {
- ObjectFile::Reference* ref = *rit;
- if ( ref->getKind() == 1 ) { // FIX FIX
- ObjectFile::Atom* targetAtom = &ref->getTarget();
- if ( log ) fprintf(stderr, "ref %s -> %s", atom->getDisplayName(), targetAtom->getDisplayName());
- std::map<const ObjectFile::Atom*, const ObjectFile::Atom*>::iterator startFrom = followOnStarts.find(atom);
- std::map<const ObjectFile::Atom*, const ObjectFile::Atom*>::iterator startTo = followOnStarts.find(targetAtom);
- if ( (startFrom == followOnStarts.end()) && (startTo == followOnStarts.end()) ) {
- // this is first time we've seen either atom, make simple cluster of the two
- if ( log ) fprintf(stderr, " new cluster\n");
- followOnStarts[atom] = atom;
- followOnStarts[targetAtom] = atom;
- followOnNexts[atom] = targetAtom;
- followOnNexts[targetAtom] = NULL;
- }
- else if ( (startFrom != followOnStarts.end()) && (startTo == followOnStarts.end()) && (followOnNexts[atom] == NULL) ) {
- // atom is at end of an existing cluster, so append target to end of cluster
- if ( log ) fprintf(stderr, " end of cluster starting with %s\n", followOnStarts[atom]->getDisplayName());
- followOnNexts[atom] = targetAtom;
- followOnNexts[targetAtom] = NULL;
- followOnStarts[targetAtom] = followOnStarts[atom];
- }
- else {
- // gerneral case of inserting into an existing cluster
- if ( followOnNexts[atom] != NULL ) {
- // an atom with two follow-ons is illegal
- warning("can't order %s because both %s and %s must follow it",
- atom->getDisplayName(), targetAtom->getDisplayName(), followOnNexts[atom]->getDisplayName());
- }
- else {
- // there already exists an atom that says target must be its follow-on
- const ObjectFile::Atom* originalStart = startTo->second;
- const ObjectFile::Atom* originalPrevious = originalStart;
- while ( followOnNexts[originalPrevious] != targetAtom )
- originalPrevious = followOnNexts[originalPrevious];
- bool otherIsAlias = (originalPrevious->getSize() == 0);
- bool thisIsAlias = (atom->getSize() == 0);
- if ( !otherIsAlias && !thisIsAlias ) {
- warning("can't order %s because both %s and %s must preceed it",
- targetAtom->getDisplayName(), originalPrevious->getDisplayName(), atom->getDisplayName());
- }
- else if ( otherIsAlias ) {
- if ( originalPrevious == originalStart ) {
- // other is alias at start of cluster, make this the new start of cluster
- if ( log ) fprintf(stderr, " becomes new start of cluster previous starting with %s\n", originalStart->getDisplayName());
- followOnNexts[atom] = originalPrevious;
- for(const ObjectFile::Atom* nextAtom = atom; nextAtom != NULL; nextAtom = followOnNexts[nextAtom])
- followOnStarts[nextAtom] = atom;
- }
- else {
- // other is alias in middle of cluster, insert new atom before it
- if ( log ) fprintf(stderr, " insert into cluster starting with %s before alias %s\n", originalStart->getDisplayName(), originalPrevious->getDisplayName());
- followOnStarts[atom] = originalStart;
- followOnNexts[atom] = originalPrevious;
- for(const ObjectFile::Atom* a = originalStart; a != NULL; a = followOnNexts[a]) {
- if ( followOnNexts[a] == originalPrevious ) {
- followOnNexts[a] = atom;
- break;
- }
- }
- }
- }
- else {
- // this is alias, so it can go inbetween originalPrevious and targetAtom
- if ( log ) fprintf(stderr, " insert into cluster starting with %s after %s\n", originalStart->getDisplayName(), originalPrevious->getDisplayName());
- followOnStarts[atom] = originalStart;
- followOnNexts[atom] = followOnNexts[originalPrevious];
- followOnNexts[originalPrevious] = atom;
- }
- }
- }
- }
- }
- }
-
- if ( log ) {
- for(std::map<const ObjectFile::Atom*, const ObjectFile::Atom*>::iterator it = followOnStarts.begin(); it != followOnStarts.end(); ++it)
- fprintf(stderr, "start %s -> %s\n", it->first->getDisplayName(), it->second->getDisplayName());
-
- for(std::map<const ObjectFile::Atom*, const ObjectFile::Atom*>::iterator it = followOnNexts.begin(); it != followOnNexts.end(); ++it)
- fprintf(stderr, "next %s -> %s\n", it->first->getDisplayName(), (it->second != NULL) ? it->second->getDisplayName() : "null");
- }
-
- // with the start/next maps of follow-on atoms we can process the order file and produce override ordinals
- ordinalOverrideMap = &theOrdinalOverrideMap;
- uint32_t index = 0;
- uint32_t matchCount = 0;
- std::vector<Options::OrderedSymbol>& orderedSymbols = fOptions.orderedSymbols();
- for(std::vector<Options::OrderedSymbol>::iterator it = orderedSymbols.begin(); it != orderedSymbols.end(); ++it) {
- ObjectFile::Atom* atom = this->findAtom(*it);
- if ( atom != NULL ) {
- std::map<const ObjectFile::Atom*, const ObjectFile::Atom*>::iterator start = followOnStarts.find(atom);
- if ( start != followOnStarts.end() ) {
- // this symbol for the order file corresponds to an atom that is in a cluster that must lay out together
- for(const ObjectFile::Atom* nextAtom = start->second; nextAtom != NULL; nextAtom = followOnNexts[nextAtom]) {
- std::map<const ObjectFile::Atom*, uint32_t>::iterator pos = theOrdinalOverrideMap.find(nextAtom);
- if ( pos == theOrdinalOverrideMap.end() ) {
- theOrdinalOverrideMap[nextAtom] = index++;
- if (log ) fprintf(stderr, "override ordinal %u assigned to %s in cluster from %s\n", index, nextAtom->getDisplayName(), nextAtom->getFile()->getPath());
- }
- else {
- if (log ) fprintf(stderr, "could not order %s as %u because it was already laid out earlier by %s as %u\n",
- atom->getDisplayName(), index, followOnStarts[atom]->getDisplayName(), theOrdinalOverrideMap[atom] );
- }
- }
- }
- else {
- theOrdinalOverrideMap[atom] = index;
- if (log ) fprintf(stderr, "override ordinal %u assigned to %s from %s\n", index, atom->getDisplayName(), atom->getFile()->getPath());
- }
- }
- else {
- ++matchCount;
- //fprintf(stderr, "can't find match for order_file entry %s/%s\n", it->objectFileName, it->symbolName);
- }
- ++index;
- }
- if ( fOptions.printOrderFileStatistics() && (fOptions.orderedSymbols().size() != matchCount) ) {
- warning("only %u out of %lu order_file symbols were applicable", matchCount, fOptions.orderedSymbols().size() );
- }
- }
-
- // sort atoms
- std::sort(fAllAtoms.begin(), fAllAtoms.end(), Linker::AtomSorter(ordinalOverrideMap));
-
- //fprintf(stderr, "Sorted atoms:\n");
- //for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) {
- // fprintf(stderr, "\t%p, %u %s\n", (*it)->getSection(), (*it)->getSection()->getIndex(), (*it)->getDisplayName());
- //}
-}
-
-
-// make sure given addresses are within reach of branches, etc
-void Linker::tweakLayout()
-{
- // > 2GB images need their large zero fill atoms sorted to the end to keep access with +/- 2GB
- if ( fTotalSize > 0x7F000000 ) {
- fBiggerThanTwoGigOutput = true;
-
- if ( (fTotalSize-fTotalZeroFillSize) > 0x7F000000 )
- throwf("total output size exceeds 2GB (%lldMB)", (fTotalSize-fTotalZeroFillSize)/(1024*1024));
-
- // move very large (>1MB) zero fill atoms to a new section at very end of __DATA segment
- Section* hugeZeroFills = Section::find("__huge", "__DATA", true);
- for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) {
- ObjectFile::Atom* atom = *it;
- if ( atom->isZeroFill() && (atom->getSize() > 1024*1024) && (strcmp(atom->getSegment().getName(), "__DATA") == 0) )
- atom->setSection(hugeZeroFills);
- }
- }
-}
-
-
-void Linker::writeDotOutput()
-{
- const char* dotOutFilePath = fOptions.dotOutputFile();
- if ( dotOutFilePath != NULL ) {
- FILE* out = fopen(dotOutFilePath, "w");
- if ( out != NULL ) {
- // print header
- fprintf(out, "digraph dg\n{\n");
- fprintf(out, "\tconcentrate = true;\n");
- fprintf(out, "\trankdir = LR;\n");
-
- // print each atom as a node
- for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) {
- ObjectFile::Atom* atom = *it;
- if ( atom->getFile() != fOutputFile ) {
- const char* name = atom->getDisplayName();
- if ( (atom->getDefinitionKind() == ObjectFile::Atom::kExternalDefinition)
- || (atom->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) {
- fprintf(out, "\taddr%p [ shape = plaintext, label = \"%s\" ];\n", atom, name);
- }
- else if ( strcmp(atom->getSectionName(), "__cstring") == 0 ) {
- char cstring[atom->getSize()+2];
- atom->copyRawContent((uint8_t*)cstring);
- fprintf(out, "\taddr%p [ label = \"string: '", atom);
- for (const char* s=cstring; *s != '\0'; ++s) {
- if ( *s == '\n' )
- fprintf(out, "\\\\n");
- else
- fputc(*s, out);
- }
- fprintf(out, "'\" ];\n");
- }
- else {
- fprintf(out, "\taddr%p [ label = \"%s\" ];\n", atom, name);
- }
- }
- }
- fprintf(out, "\n");
-
- // print each reference as an edge
- for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) {
- ObjectFile::Atom* fromAtom = *it;
- if ( fromAtom->getFile() != fOutputFile ) {
- std::vector<ObjectFile::Reference*>& references = fromAtom->getReferences();
- std::set<ObjectFile::Atom*> seenTargets;
- for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) {
- ObjectFile::Reference* reference = *rit;
- ObjectFile::Atom* toAtom = &(reference->getTarget());
- if ( seenTargets.count(toAtom) == 0 ) {
- seenTargets.insert(toAtom);
- fprintf(out, "\taddr%p -> addr%p;\n", fromAtom, toAtom);
- }
- }
- }
- }
- fprintf(out, "\n");
-
- // push all imports to bottom of graph
- fprintf(out, "{ rank = same; ");
- for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) {
- ObjectFile::Atom* atom = *it;
- if ( atom->getFile() != fOutputFile )
- if ( (atom->getDefinitionKind() == ObjectFile::Atom::kExternalDefinition)
- || (atom->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) {
- fprintf(out, "addr%p; ", atom);
- }
- }
- fprintf(out, "};\n ");
-
- // print footer
- fprintf(out, "}\n");
- fclose(out);
- }
- else {
- warning("could not write dot output file: %s", dotOutFilePath);
- }
- }
-}
-
-ObjectFile::Atom* Linker::entryPoint(bool orInit)
-{
- // if main executable, find entry point atom
- ObjectFile::Atom* entryPoint = NULL;
- switch ( fOptions.outputKind() ) {
- case Options::kDynamicExecutable:
- case Options::kStaticExecutable:
- case Options::kDyld:
- entryPoint = fGlobalSymbolTable.find(fOptions.entryName());
- if ( entryPoint == NULL ) {
- throwf("could not find entry point \"%s\" (perhaps missing crt1.o)", fOptions.entryName());
- }
- break;
- case Options::kDynamicLibrary:
- if ( orInit && (fOptions.initFunctionName() != NULL) ) {
- entryPoint = fGlobalSymbolTable.find(fOptions.initFunctionName());
- if ( entryPoint == NULL ) {
- throwf("could not find -init function: \"%s\"", fOptions.initFunctionName());
- }
- }
- break;
- case Options::kObjectFile:
- case Options::kDynamicBundle:
- entryPoint = NULL;
- break;
- }
- return entryPoint;
-}
-
-ObjectFile::Atom* Linker::dyldHelper()
-{
- return fGlobalSymbolTable.find("dyld_stub_binding_helper");
-}
-
-ObjectFile::Atom* Linker::dyldLazyLibraryHelper()
-{
- return fGlobalSymbolTable.find("dyld_lazy_dylib_stub_binding_helper");
-}
-
-const char* Linker::assureFullPath(const char* path)
-{
- if ( path[0] == '/' )
- return path;
- char cwdbuff[MAXPATHLEN];
- if ( getcwd(cwdbuff, MAXPATHLEN) != NULL ) {
- char* result;
- asprintf(&result, "%s/%s", cwdbuff, path);
- if ( result != NULL )
- return result;
- }
- return path;
-}
-
-
-//
-// The stab strings are of the form:
-// <name> ':' <type-code> <number-pari>
-// 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) )
-//
-const char* Linker::truncateStabString(const char* str)
-{
- enum { start, inObjc } state = start;
- for (const char* s = str; *s != 0; ++s) {
- char c = *s;
- switch (state) {
- case start:
- if ( c == '[' ) {
- state = inObjc;
- }
- else {
- if ( c == ':' ) {
- if ( s[1] == ':' ) {
- ++s;
- }
- else {
- // found colon
- // Duplicate strndup behavior here.
- int trunStrLen = s-str+2;
- char* temp = new char[trunStrLen+1];
- memcpy(temp, str, trunStrLen);
- temp[trunStrLen] = '\0';
- return temp;
- }
- }
- }
- break;
- case inObjc:
- if ( c == ']' ) {
- state = start;
- }
- break;
- }
- }
- // malformed
- return str;
-}
-
-
-bool Linker::minimizeStab(ObjectFile::Reader::Stab& stab)
-{
- switch(stab.type){
- case N_GSYM:
- case N_STSYM:
- case N_LCSYM:
- case N_FUN:
- // these all need truncated strings
- stab.string = truncateStabString(stab.string);
- return true;
- case N_SO:
- case N_OSO:
- case N_OPT:
- case N_SOL:
- // these are included in the minimal stabs, but they keep their full string
- return true;
- default:
- return false;
- }
-}
-
-
-struct HeaderRange {
- std::vector<ObjectFile::Reader::Stab>::iterator begin;
- std::vector<ObjectFile::Reader::Stab>::iterator end;
- int parentRangeIndex;
- uint32_t sum;
- bool sumPrecomputed;
- bool useEXCL;
- bool cannotEXCL; // because of SLINE, etc stabs
-};
-
-
-typedef __gnu_cxx::hash_map<const char*, std::vector<uint32_t>, __gnu_cxx::hash<const char*>, CStringEquals> PathToSums;
-
-// hash table that maps header path to a vector of known checksums for that path
-static PathToSums sKnownBINCLs;
-
-
-void Linker::collectStabs(ObjectFile::Reader* reader, std::map<const class ObjectFile::Atom*, uint32_t>& atomOrdinals)
-{
- const 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());
- 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 ) {
- case N_BINCL:
- {
- HeaderRange range;
- range.begin = it;
- range.end = readerStabs->end();
- range.parentRangeIndex = curRangeIndex;
- range.sum = it->value;
- range.sumPrecomputed = (range.sum != 0);
- range.useEXCL = false;
- range.cannotEXCL = false;
- curRangeIndex = ranges.size();
- if ( log ) fprintf(stderr, "[%d]BINCL %s\n", curRangeIndex, it->string);
- ranges.push_back(range);
- }
- break;
- case N_EINCL:
- if ( curRangeIndex == -1 ) {
- warning("EINCL missing BINCL in %s", reader->getPath());
- }
- else {
- ranges[curRangeIndex].end = it+1;
- if ( log ) fprintf(stderr, "[%d->%d]EINCL %s\n", curRangeIndex, ranges[curRangeIndex].parentRangeIndex, it->string);
- curRangeIndex = ranges[curRangeIndex].parentRangeIndex;
- }
- break;
- case N_FUN:
- {
- std::map<const 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:
- case N_RBRAC:
- case N_SLINE:
- case N_STSYM:
- case N_LCSYM:
- if ( curRangeIndex != -1 ) {
- ranges[curRangeIndex].cannotEXCL = true;
- if ( fOptions.warnStabs() )
- warning("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 ) {
- uint32_t sum = 0;
- const char* s = it->string;
- char c;
- while ( (c = *s++) != 0 ) {
- sum += c;
- // don't checkusm first number (file index) after open paren in string
- if ( c == '(' ) {
- while(isdigit(*s))
- ++s;
- }
- }
- ranges[curRangeIndex].sum += sum;
- }
- }
-
- }
- }
- if ( log ) fprintf(stderr, "processesed %d stabs for %s\n", count, reader->getPath());
- if ( curRangeIndex != -1 )
- warning("BINCL (%s) missing EINCL in %s", ranges[curRangeIndex].begin->string, reader->getPath());
-
- // if no BINCLs
- if ( ranges.size() == 0 ) {
- unsigned 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 ( soIndex < soRanges.size() ) {
- 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);
- }
- }
- 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 ) {
- const char* header = it->begin->string;
- uint32_t sum = it->sum;
- PathToSums::iterator pos = sKnownBINCLs.find(header);
- if ( pos != sKnownBINCLs.end() ) {
- std::vector<uint32_t>& sums = pos->second;
- for(std::vector<uint32_t>::iterator sit=sums.begin(); sit != sums.end(); ++sit) {
- if (*sit == sum) {
- //fprintf(stderr, "use EXCL for %s in %s\n", header, reader->getPath());
- it->useEXCL = true;
- break;
- }
- }
- if ( ! it->useEXCL ) {
- // have seen this path, but not this checksum
- //fprintf(stderr, "registering another checksum %08X for %s\n", sum, header);
- sums.push_back(sum);
- }
- }
- else {
- // have not seen this path, so add to known BINCLs
- std::vector<uint32_t> empty;
- sKnownBINCLs[header] = empty;
- sKnownBINCLs[header].push_back(sum);
- //fprintf(stderr, "registering checksum %08X for %s\n", sum, header);
- }
- }
- }
-
- // add a new set of stabs with BINCL/EINCL runs that have been seen before, replaced with EXCLs
- curRangeIndex = -1;
- const int maxRangeIndex = ranges.size();
- int soIndex = 0;
- for(std::vector<ObjectFile::Reader::Stab>::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) {
- switch ( it->type ) {
- case N_BINCL:
- for(int i=curRangeIndex+1; i < maxRangeIndex; ++i) {
- if ( ranges[i].begin == it ) {
- curRangeIndex = i;
- HeaderRange& range = ranges[curRangeIndex];
- ObjectFile::Reader::Stab stab = *it;
- stab.value = range.sum; // BINCL and EXCL have n_value set to checksum
- if ( range.useEXCL )
- stab.type = N_EXCL; // transform BINCL into EXCL
- if ( !minimal )
- fStabs.push_back(stab);
- break;
- }
- }
- break;
- case N_EINCL:
- if ( curRangeIndex != -1 ) {
- if ( !ranges[curRangeIndex].useEXCL && !minimal )
- fStabs.push_back(*it);
- curRangeIndex = ranges[curRangeIndex].parentRangeIndex;
- }
- break;
- default:
- if ( (curRangeIndex == -1) || !ranges[curRangeIndex].useEXCL ) {
- 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);
- }
- }
- }
- }
-
-}
-
-
-// used to prune out atoms that don't need debug notes generated
-class NoDebugNoteAtom
-{
-public:
- NoDebugNoteAtom(const std::map<class ObjectFile::Reader*, uint32_t>& readersWithDwarfOrdinals)
- : fReadersWithDwarfOrdinals(readersWithDwarfOrdinals) {}
-
- bool operator()(const ObjectFile::Atom* atom) const {
- if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn )
- return true;
- if ( atom->getName() == NULL )
- return true;
- if ( fReadersWithDwarfOrdinals.find(atom->getFile()) == fReadersWithDwarfOrdinals.end() )
- return true;
- return false;
- }
-
-private:
- const std::map<class ObjectFile::Reader*, uint32_t>& fReadersWithDwarfOrdinals;
-};
-
-// used to sort atoms with debug notes
-class ReadersWithDwarfSorter
-{
-public:
- ReadersWithDwarfSorter(const std::map<class ObjectFile::Reader*, uint32_t>& readersWithDwarfOrdinals,
- const std::map<const class ObjectFile::Atom*, uint32_t>& atomOrdinals)
- : fReadersWithDwarfOrdinals(readersWithDwarfOrdinals), fAtomOrdinals(atomOrdinals) {}
-
- bool operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right) const
- {
- // first sort by reader
- unsigned int leftReaderIndex = fReadersWithDwarfOrdinals.find(left->getFile())->second;
- unsigned int rightReaderIndex = fReadersWithDwarfOrdinals.find(right->getFile())->second;
- if ( leftReaderIndex != rightReaderIndex )
- return (leftReaderIndex < rightReaderIndex);
-
- // then sort by atom ordinal
- unsigned int leftAtomIndex = fAtomOrdinals.find(left)->second;
- unsigned int rightAtomIndex = fAtomOrdinals.find(right)->second;
- return leftAtomIndex < rightAtomIndex;
- }
-
-private:
- const std::map<class ObjectFile::Reader*, uint32_t>& fReadersWithDwarfOrdinals;
- const std::map<const class ObjectFile::Atom*, uint32_t>& fAtomOrdinals;
-};
-
-
-
-
-
-void Linker::synthesizeDebugNotes(std::vector<class ObjectFile::Atom*>& allAtomsByReader)
-{
- // synthesize "debug notes" and add them to master stabs vector
- const char* dirPath = NULL;
- const char* filename = NULL;
- bool wroteStartSO = false;
- bool useZeroOSOModTime = (getenv("RC_RELEASE") != NULL);
- __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> seenFiles;
- for (std::vector<ObjectFile::Atom*>::iterator it=allAtomsByReader.begin(); it != allAtomsByReader.end(); it++) {
- ObjectFile::Atom* atom = *it;
- const char* newDirPath;
- const char* newFilename;
- //fprintf(stderr, "debug note for %s\n", atom->getDisplayName());
- if ( atom->getTranslationUnitSource(&newDirPath, &newFilename) ) {
- // need SO's whenever the translation unit source file changes
- if ( newFilename != filename ) {
- // 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);
- if ( filename != NULL ) {
- // translation unit change, emit ending SO
- ObjectFile::Reader::Stab endFileStab;
- endFileStab.atom = NULL;
- endFileStab.type = N_SO;
- endFileStab.other = 1;
- endFileStab.desc = 0;
- endFileStab.value = 0;
- endFileStab.string = "";
- fStabs.push_back(endFileStab);
- }
- // new translation unit, emit start SO's
- ObjectFile::Reader::Stab dirPathStab;
- dirPathStab.atom = NULL;
- dirPathStab.type = N_SO;
- dirPathStab.other = 0;
- dirPathStab.desc = 0;
- dirPathStab.value = 0;
- dirPathStab.string = newDirPath;
- fStabs.push_back(dirPathStab);
- ObjectFile::Reader::Stab fileStab;
- fileStab.atom = NULL;
- fileStab.type = N_SO;
- fileStab.other = 0;
- fileStab.desc = 0;
- fileStab.value = 0;
- fileStab.string = newFilename;
- fStabs.push_back(fileStab);
- // Synthesize OSO for start of file
- ObjectFile::Reader::Stab objStab;
- objStab.atom = NULL;
- objStab.type = N_OSO;
- objStab.other = 0;
- objStab.desc = 1;
- objStab.value = useZeroOSOModTime ? 0 : atom->getFile()->getModificationTime();
- objStab.string = assureFullPath(atom->getFile()->getPath());
- fStabs.push_back(objStab);
- wroteStartSO = true;
- // add the source file path to seenFiles so it does not show up in SOLs
- seenFiles.insert(newFilename);
- }
- filename = newFilename;
- dirPath = newDirPath;
- if ( atom->getSegment().isContentExecutable() && (strncmp(atom->getSectionName(), "__text", 6) == 0) ) {
- // Synthesize BNSYM and start FUN stabs
- ObjectFile::Reader::Stab beginSym;
- beginSym.atom = atom;
- beginSym.type = N_BNSYM;
- beginSym.other = 1;
- beginSym.desc = 0;
- beginSym.value = 0;
- beginSym.string = "";
- fStabs.push_back(beginSym);
- ObjectFile::Reader::Stab startFun;
- startFun.atom = atom;
- startFun.type = N_FUN;
- startFun.other = 1;
- startFun.desc = 0;
- startFun.value = 0;
- startFun.string = atom->getName();
- fStabs.push_back(startFun);
- // Synthesize any SOL stabs needed
- std::vector<ObjectFile::LineInfo>* lineInfo = atom->getLineInfo();
- if ( lineInfo != NULL ) {
- const char* curFile = NULL;
- for (std::vector<ObjectFile::LineInfo>::iterator it = lineInfo->begin(); it != lineInfo->end(); ++it) {
- if ( it->fileName != curFile ) {
- if ( seenFiles.count(it->fileName) == 0 ) {
- seenFiles.insert(it->fileName);
- ObjectFile::Reader::Stab sol;
- sol.atom = 0;
- sol.type = N_SOL;
- sol.other = 0;
- sol.desc = 0;
- sol.value = 0;
- sol.string = it->fileName;
- fStabs.push_back(sol);
- }
- curFile = it->fileName;
- }
- }
- }
- // Synthesize end FUN and ENSYM stabs
- ObjectFile::Reader::Stab endFun;
- endFun.atom = atom;
- endFun.type = N_FUN;
- endFun.other = 0;
- endFun.desc = 0;
- endFun.value = 0;
- endFun.string = "";
- fStabs.push_back(endFun);
- ObjectFile::Reader::Stab endSym;
- endSym.atom = atom;
- endSym.type = N_ENSYM;
- endSym.other = 1;
- endSym.desc = 0;
- endSym.value = 0;
- endSym.string = "";
- fStabs.push_back(endSym);
- }
- else if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn ) {
- // no stabs for atoms that would not be in the symbol table
- }
- else if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAsAbsolute ) {
- // no stabs for absolute symbols
- }
- else if ( (strcmp(atom->getSectionName(), "__eh_frame") == 0) ) {
- // no stabs for .eh atoms
- }
- else {
- ObjectFile::Reader::Stab globalsStab;
- const char* name = atom->getName();
- if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) {
- // Synthesize STSYM stab for statics
- globalsStab.atom = atom;
- globalsStab.type = N_STSYM;
- globalsStab.other = 1;
- globalsStab.desc = 0;
- globalsStab.value = 0;
- globalsStab.string = name;
- fStabs.push_back(globalsStab);
- }
- else {
- // Synthesize GSYM stab for other globals
- globalsStab.atom = atom;
- globalsStab.type = N_GSYM;
- globalsStab.other = 1;
- globalsStab.desc = 0;
- globalsStab.value = 0;
- globalsStab.string = name;
- fStabs.push_back(globalsStab);
- }
- }
- }
- }
-
- if ( wroteStartSO ) {
- // emit ending SO
- ObjectFile::Reader::Stab endFileStab;
- endFileStab.atom = NULL;
- endFileStab.type = N_SO;
- endFileStab.other = 1;
- endFileStab.desc = 0;
- endFileStab.value = 0;
- endFileStab.string = "";
- fStabs.push_back(endFileStab);
- }
-}
-
-
-
-
-void Linker::collectDebugInfo()
-{
- std::map<const class ObjectFile::Atom*, uint32_t> atomOrdinals;
- fStartDebugTime = mach_absolute_time();
- if ( fOptions.readerOptions().fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone ) {
-
- // determine mixture of stabs and dwarf
- bool someStabs = false;
- bool someDwarf = false;
- for (std::vector<class ObjectFile::Reader*>::iterator it=fReadersThatHaveSuppliedAtoms.begin();
- it != fReadersThatHaveSuppliedAtoms.end();
- it++) {
- ObjectFile::Reader* reader = *it;
- if ( reader != NULL ) {
- switch ( reader->getDebugInfoKind() ) {
- case ObjectFile::Reader::kDebugInfoNone:
- break;
- case ObjectFile::Reader::kDebugInfoStabs:
- someStabs = true;
- break;
- case ObjectFile::Reader::kDebugInfoDwarf:
- someDwarf = true;
- fCreateUUID = true;
- break;
- case ObjectFile::Reader::kDebugInfoStabsUUID:
- someStabs = true;
- fCreateUUID = true;
- break;
- default:
- throw "Unhandled type of debug information";
- }
- }
- }
-
- if ( someDwarf || someStabs ) {
- // try to minimize re-allocations
- fStabs.reserve(1024);
-
- // make mapping from atoms to ordinal
- uint32_t ordinal = 1;
- for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) {
- atomOrdinals[*it] = ordinal++;
- }
- }
-
- // process all dwarf .o files as a batch
- if ( someDwarf ) {
- // make mapping from readers with dwarf to ordinal
- std::map<class ObjectFile::Reader*, uint32_t> readersWithDwarfOrdinals;
- uint32_t readerOrdinal = 1;
- for (std::vector<class ObjectFile::Reader*>::iterator it=fReadersThatHaveSuppliedAtoms.begin();
- it != fReadersThatHaveSuppliedAtoms.end();
- it++) {
- ObjectFile::Reader* reader = *it;
- if ( (reader != NULL) && (reader->getDebugInfoKind() == ObjectFile::Reader::kDebugInfoDwarf) ) {
- readersWithDwarfOrdinals[reader] = readerOrdinal++;
- }
- }
-
- // make a vector of atoms
- std::vector<class ObjectFile::Atom*> allAtomsByReader(fAllAtoms.begin(), fAllAtoms.end());
- // remove those not from a reader that has dwarf
- allAtomsByReader.erase(std::remove_if(allAtomsByReader.begin(), allAtomsByReader.end(),
- NoDebugNoteAtom(readersWithDwarfOrdinals)), allAtomsByReader.end());
- // sort by reader then atom ordinal
- std::sort(allAtomsByReader.begin(), allAtomsByReader.end(), ReadersWithDwarfSorter(readersWithDwarfOrdinals, atomOrdinals));
- // add debug notes for each atom
- this->synthesizeDebugNotes(allAtomsByReader);
- }
-
- // process all stabs .o files one by one
- if ( someStabs ) {
- // get stabs from each reader, in command line order
- for (std::vector<class ObjectFile::Reader*>::iterator it=fReadersThatHaveSuppliedAtoms.begin();
- it != fReadersThatHaveSuppliedAtoms.end();
- it++) {
- ObjectFile::Reader* reader = *it;
- if ( reader != NULL ) {
- switch ( reader->getDebugInfoKind() ) {
- case ObjectFile::Reader::kDebugInfoDwarf:
- case ObjectFile::Reader::kDebugInfoNone:
- // do nothing
- break;
- case ObjectFile::Reader::kDebugInfoStabs:
- case ObjectFile::Reader::kDebugInfoStabsUUID:
- collectStabs(reader, atomOrdinals);
- break;
- default:
- throw "Unhandled type of debug information";
- }
- }
- }
- // remove stabs associated with atoms that won't be in output
- std::set<class ObjectFile::Atom*> allAtomsSet;
- allAtomsSet.insert(fAllAtoms.begin(), fAllAtoms.end());
- fStabs.erase(std::remove_if(fStabs.begin(), fStabs.end(), NotInSet(allAtomsSet)), fStabs.end());
- }
- }
-}
-
-void Linker::writeOutput()
-{
- if ( fOptions.forceCpuSubtypeAll() )
- fCurrentCpuConstraint = ObjectFile::Reader::kCpuAny;
-
- fStartWriteTime = mach_absolute_time();
- // tell writer about each segment's atoms
- fOutputFileSize = fOutputFile->write(fAllAtoms, fStabs, this->entryPoint(true),
- this->dyldHelper(), this->dyldLazyLibraryHelper(),
- fCreateUUID, fCanScatter,
- fCurrentCpuConstraint, fBiggerThanTwoGigOutput,
- fGlobalSymbolTable.hasExternalWeakDefinitions());
-}
-
-ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info)
-{
- // map in whole file
- uint64_t len = info.fileLen;
- int fd = ::open(info.path, O_RDONLY, 0);
- if ( fd == -1 )
- throwf("can't open file, errno=%d", errno);
- if ( info.fileLen < 20 )
- throw "file too small";
-
- uint8_t* p = (uint8_t*)::mmap(NULL, info.fileLen, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
- if ( p == (uint8_t*)(-1) )
- throwf("can't map file, errno=%d", errno);
-
- // if fat file, skip to architecture we want
- // Note: fat header is always big-endian
- const fat_header* fh = (fat_header*)p;
- if ( fh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
- const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header));
- uint32_t sliceToUse;
- bool sliceFound = false;
- if ( fOptions.preferSubArchitecture() ) {
- // first try to find a slice that match cpu-type and cpu-sub-type
- for (uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) {
- if ( (OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)fArchitecture)
- && (OSSwapBigToHostInt32(archs[i].cpusubtype) == (uint32_t)fOptions.subArchitecture()) ) {
- sliceToUse = i;
- sliceFound = true;
- break;
- }
- }
- }
- if ( !sliceFound ) {
- // look for any slice that matches just cpu-type
- for (uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) {
- if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)fArchitecture ) {
- sliceToUse = i;
- sliceFound = true;
- break;
- }
- }
- }
- if ( sliceFound ) {
- uint32_t fileOffset = OSSwapBigToHostInt32(archs[sliceToUse].offset);
- len = OSSwapBigToHostInt32(archs[sliceToUse].size);
- // if requested architecture is page aligned within fat file, then remap just that portion of file
- if ( (fileOffset & 0x00000FFF) == 0 ) {
- // unmap whole file
- munmap((caddr_t)p, info.fileLen);
- // re-map just part we need
- p = (uint8_t*)::mmap(NULL, len, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, fileOffset);
- if ( p == (uint8_t*)(-1) )
- throwf("can't re-map file, errno=%d", errno);
- }
- else {
- p = &p[fileOffset];
- }
- }
- }
- ::close(fd);
-
- switch (fArchitecture) {
- case CPU_TYPE_POWERPC:
- if ( mach_o::relocatable::Reader<ppc>::validFile(p) )
- return this->addObject(new mach_o::relocatable::Reader<ppc>::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len);
- else if ( mach_o::dylib::Reader<ppc>::validFile(p, info.options.fBundleLoader) )
- return this->addDylib(new mach_o::dylib::Reader<ppc>::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len);
- else if ( archive::Reader<ppc>::validFile(p, len) )
- return this->addArchive(new archive::Reader<ppc>::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len);
- break;
- case CPU_TYPE_POWERPC64:
- if ( mach_o::relocatable::Reader<ppc64>::validFile(p) )
- return this->addObject(new mach_o::relocatable::Reader<ppc64>::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len);
- else if ( mach_o::dylib::Reader<ppc64>::validFile(p, info.options.fBundleLoader) )
- return this->addDylib(new mach_o::dylib::Reader<ppc64>::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len);
- else if ( archive::Reader<ppc64>::validFile(p, len) )
- return this->addArchive(new archive::Reader<ppc64>::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len);
- break;
- case CPU_TYPE_I386:
- if ( mach_o::relocatable::Reader<x86>::validFile(p) )
- return this->addObject(new mach_o::relocatable::Reader<x86>::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len);
- else if ( mach_o::dylib::Reader<x86>::validFile(p, info.options.fBundleLoader) )
- return this->addDylib(new mach_o::dylib::Reader<x86>::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len);
- else if ( archive::Reader<x86>::validFile(p, len) )
- return this->addArchive(new archive::Reader<x86>::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len);
- break;
- case CPU_TYPE_X86_64:
- if ( mach_o::relocatable::Reader<x86_64>::validFile(p) )
- return this->addObject(new mach_o::relocatable::Reader<x86_64>::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len);
- else if ( mach_o::dylib::Reader<x86_64>::validFile(p, info.options.fBundleLoader) )
- return this->addDylib(new mach_o::dylib::Reader<x86_64>::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len);
- else if ( archive::Reader<x86_64>::validFile(p, len) )
- return this->addArchive(new archive::Reader<x86_64>::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len);
- case CPU_TYPE_ARM:
- if ( mach_o::relocatable::Reader<arm>::validFile(p) )
- return this->addObject(new mach_o::relocatable::Reader<arm>::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len);
- else if ( mach_o::dylib::Reader<arm>::validFile(p, info.options.fBundleLoader) )
- return this->addDylib(new mach_o::dylib::Reader<arm>::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len);
- else if ( archive::Reader<arm>::validFile(p, len) )
- return this->addArchive(new archive::Reader<arm>::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len);
- break;
- break;
- }
-
-#if LTO_SUPPORT
- if ( lto::Reader::validFile(p, len, fArchitecture) ) {
- return this->addObject(new lto::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fArchitecture), info, len);
- }
- else if ( !lto::Reader::loaded() && (p[0] == 'B') && (p[1] == 'C') ) {
- throw "could not process object file. Looks like an llvm bitcode object file, but libLTO.dylib could not be loaded";
- }
-#endif
- // error handling
- if ( ((fat_header*)p)->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
- throwf("missing required architecture %s in file", fArchitectureName);
- }
- else {
- throw "file is not of required architecture";
- }
-}
-
-void Linker::logDylib(ObjectFile::Reader* reader, bool indirect)
-{
- if ( fOptions.readerOptions().fTraceDylibs ) {
- const char* fullPath = reader->getPath();
- char realName[MAXPATHLEN];
- if ( realpath(fullPath, realName) != NULL )
- fullPath = realName;
- if ( indirect )
- logTraceInfo("[Logging for XBS] Used indirect dynamic library: %s\n", fullPath);
- else
- logTraceInfo("[Logging for XBS] Used dynamic library: %s\n", fullPath);
- }
-}
-
-
-
-ObjectFile::Reader* Linker::findDylib(const char* installPath, const char* fromPath)
-{
- //fprintf(stderr, "findDylib(%s, %s)\n", installPath, fromPath);
- InstallNameToReader::iterator pos = fDylibMap.find(installPath);
- if ( pos != fDylibMap.end() ) {
- return pos->second;
- }
- else {
- // allow -dylib_path option to override indirect library to use
- for (std::vector<Options::DylibOverride>::iterator dit = fOptions.dylibOverrides().begin(); dit != fOptions.dylibOverrides().end(); ++dit) {
- if ( strcmp(dit->installName,installPath) == 0 ) {\
- try {
- Options::FileInfo info = fOptions.findFile(dit->useInstead);
- ObjectFile::Reader* reader = this->createReader(info);
- fDylibMap[strdup(installPath)] = reader;
- this->logDylib(reader, true);
- return reader;
- }
- catch (const char* msg) {
- warning("ignoring -dylib_file option, %s", msg);
- }
- }
- }
- char newPath[MAXPATHLEN];
- // handle @loader_path
- if ( strncmp(installPath, "@loader_path/", 13) == 0 ) {
- strcpy(newPath, fromPath);
- char* addPoint = strrchr(newPath,'/');
- if ( addPoint != NULL )
- strcpy(&addPoint[1], &installPath[13]);
- else
- strcpy(newPath, &installPath[13]);
- installPath = newPath;
- }
- // note: @executable_path case is handled inside findFileUsingPaths()
- // search for dylib using -F and -L paths
- Options::FileInfo info = fOptions.findFileUsingPaths(installPath);
- try {
- ObjectFile::Reader* reader = this->createReader(info);
- fDylibMap[strdup(installPath)] = reader;
- this->logDylib(reader, true);
- return reader;
- }
- catch (const char* msg) {
- throwf("in %s, %s", info.path, msg);
- }
- }
-}
-
-
-void Linker::processDylibs()
-{
- fAllDirectDylibsLoaded = true;
-
- // mark all dylibs initially specified as required and check if they can be used
- for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) {
- it->second->setExplicitlyLinked();
- this->checkDylibClientRestrictions(it->second);
- }
-
- // keep processing dylibs until no more dylibs are added
- unsigned long lastMapSize = 0;
- while ( lastMapSize != fDylibMap.size() ) {
- lastMapSize = fDylibMap.size();
- // can't iterator fDylibMap while modifying it, so use temp buffer
- std::vector<ObjectFile::Reader*> currentUnprocessedReaders;
- for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) {
- if ( fDylibsProcessed.count(it->second) == 0 )
- currentUnprocessedReaders.push_back(it->second);
- }
- for (std::vector<ObjectFile::Reader*>::iterator it=currentUnprocessedReaders.begin(); it != currentUnprocessedReaders.end(); it++) {
- fDylibsProcessed.insert(*it);
- (*it)->processIndirectLibraries(this);
- }
- }
-
- // go back over original dylibs and mark sub frameworks as re-exported
- if ( fOptions.outputKind() == Options::kDynamicLibrary ) {
- const char* myLeaf = strrchr(fOptions.installPath(), '/');
- if ( myLeaf != NULL ) {
- for (std::vector<class ObjectFile::Reader*>::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) {
- ObjectFile::Reader* reader = *it;
- const char* childParent = reader->parentUmbrella();
- if ( childParent != NULL ) {
- if ( strcmp(childParent, &myLeaf[1]) == 0 ) {
- // set re-export bit of info
- std::map<ObjectFile::Reader*,DynamicLibraryOptions>::iterator pos = fDylibOptionsMap.find(reader);
- if ( pos != fDylibOptionsMap.end() ) {
- pos->second.fReExport = true;
- }
- }
- }
- }
- }
- }
-
-}
-
-
-
-void Linker::createReaders()
-{
- fStartCreateReadersTime = mach_absolute_time();
- std::vector<Options::FileInfo>& files = fOptions.getInputFiles();
- const int count = files.size();
- if ( count == 0 )
- throw "no object files specified";
- // add all direct object, archives, and dylibs
- for (int i=0; i < count; ++i) {
- Options::FileInfo& entry = files[i];
- // ignore /usr/lib/dyld on command line in crt.o build
- if ( strcmp(entry.path, "/usr/lib/dyld") != 0 ) {
- try {
- this->addInputFile(this->createReader(entry), entry);
- }
- catch (const char* msg) {
- if ( strstr(msg, "architecture") != NULL ) {
- if ( fOptions.ignoreOtherArchInputFiles() ) {
- // ignore, because this is about an architecture not in use
- }
- else {
- warning("in %s, %s", entry.path, msg);
- }
- }
- else {
- throwf("in %s, %s", entry.path, msg);
- }
- }
- }
- }
-
- this->processDylibs();
-}
-
-
-
-ObjectFile::Reader* Linker::addArchive(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen)
-{
- fNextInputOrdinal += mappedLen;
- // remember which readers are archives because they are logged differently
- fArchiveReaders.insert(reader);
-
- // update stats
- fTotalArchiveSize += mappedLen;
- ++fTotalArchivesLoaded;
- return reader;
-}
-
-ObjectFile::Reader* Linker::addObject(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen)
-{
- fNextInputOrdinal += mappedLen;
- // any .o files that don't have MH_SUBSECTIONS_VIA_SYMBOLS, that means a generated .o file can't
- if ( (fOptions.outputKind() == Options::kObjectFile) && !reader->canScatterAtoms() )
- fCanScatter = false;
-
- // update stats
- fTotalObjectSize += mappedLen;
- ++fTotalObjectLoaded;
- return reader;
-}
-
-
-void Linker::checkDylibClientRestrictions(ObjectFile::Reader* reader)
-{
- // Check for any restrictions on who can link with this dylib
- const char* readerParentName = reader->parentUmbrella() ;
- std::vector<const char*>* clients = reader->getAllowableClients();
- if ( (readerParentName != NULL) || (clients != NULL) ) {
- // only dylibs that are in an umbrella or have a client list need verification
- const char* installName = fOptions.installPath();
- const char* installNameLastSlash = strrchr(installName, '/');
- bool isParent = false;
- bool isSibling = false;
- bool isAllowableClient = false;
- // There are three cases:
- if ( (readerParentName != NULL) && (installNameLastSlash != NULL) ) {
- // case 1) The dylib has a parent umbrella, and we are creating the parent umbrella
- isParent = ( strcmp(&installNameLastSlash[1], readerParentName) == 0 );
-
- // hack to support umbrella variants that encode the variant name in the install name
- // e.g. CoreServices_profile
- if ( !isParent ) {
- const char* underscore = strchr(&installNameLastSlash[1], '_');
- if ( underscore != NULL ) {
- isParent = ( strncmp(&installNameLastSlash[1], readerParentName, underscore-installNameLastSlash-1) == 0 );
- }
- }
-
- // case 2) The dylib has a parent umbrella, and we are creating a sibling with the same parent
- isSibling = ( (fOptions.umbrellaName() != NULL) && (strcmp(fOptions.umbrellaName(), readerParentName) == 0) );
- }
-
- if ( !isParent && !isSibling && (clients != NULL) ) {
- // case 3) the dylib has a list of allowable clients, and we are creating one of them
- const char* clientName = fOptions.clientName();
- int clientNameLen = 0;
- if ( clientName != NULL ) {
- // use client name as specified on command line
- clientNameLen = strlen(clientName);
- }
- else {
- // infer client name from output path (e.g. xxx/libfoo_variant.A.dylib --> foo, Bar.framework/Bar_variant --> Bar)
- clientName = installName;
- clientNameLen = strlen(clientName);
- // starts after last slash
- if ( installNameLastSlash != NULL )
- clientName = &installNameLastSlash[1];
- if ( strncmp(clientName, "lib", 3) == 0 )
- clientName = &clientName[3];
- // up to first dot
- const char* firstDot = strchr(clientName, '.');
- if ( firstDot != NULL )
- clientNameLen = firstDot - clientName;
- // up to first underscore
- const char* firstUnderscore = strchr(clientName, '_');
- if ( (firstUnderscore != NULL) && ((firstUnderscore - clientName) < clientNameLen) )
- clientNameLen = firstUnderscore - 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 )
- isAllowableClient = true;
- }
- }
-
- if ( !isParent && !isSibling && !isAllowableClient ) {
- if ( readerParentName != NULL ) {
- throwf("cannot link directly with %s. Link against the umbrella framework '%s.framework' instead.",
- reader->getPath(), readerParentName);
- }
- else {
- throwf("cannot link directly with %s", reader->getPath());
- }
- }
- }
-
-
-}
-
-ObjectFile::Reader* Linker::addDylib(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen)
-{
- fNextInputOrdinal += mappedLen;
- if ( (reader->getInstallPath() == NULL) && !info.options.fBundleLoader ) {
- // this is a "blank" stub
- // silently ignore it
- return reader;
- }
- // add to map of loaded dylibs
- const char* installPath = reader->getInstallPath();
- if ( installPath != NULL ) {
- InstallNameToReader::iterator pos = fDylibMap.find(installPath);
- if ( pos == fDylibMap.end() ) {
- fDylibMap[strdup(installPath)] = reader;
- }
- else {
- InstallNameToReader::iterator pos2 = fDylibMap.find(reader->getPath());
- if ( pos2 == fDylibMap.end() )
- fDylibMap[strdup(reader->getPath())] = reader;
- else
- warning("duplicate dylib %s", reader->getPath());
- }
- }
- else if ( info.options.fBundleLoader )
- fBundleLoaderReader = reader;
-
- // log direct readers
- if ( !fAllDirectDylibsLoaded )
- this->logDylib(reader, false);
-
- // update stats
- ++fTotalDylibsLoaded;
-
- return reader;
-}
-
-
-void Linker::logTraceInfo (const char* format, ...)
-{
- static int trace_file = -1;
- char trace_buffer[MAXPATHLEN * 2];
- char *buffer_ptr;
- int length;
- ssize_t amount_written;
- const char *trace_file_path = fOptions.readerOptions().fTraceOutputFile;
-
- if(trace_file == -1) {
- if(trace_file_path != NULL) {
- trace_file = open(trace_file_path, O_WRONLY | O_APPEND | O_CREAT, 0666);
- if(trace_file == -1)
- throwf("Could not open or create trace file: %s", trace_file_path);
- }
- else {
- trace_file = fileno(stderr);
- }
- }
-
- va_list ap;
- va_start(ap, format);
- length = vsnprintf(trace_buffer, sizeof(trace_buffer), format, ap);
- va_end(ap);
- buffer_ptr = trace_buffer;
-
- while(length > 0) {
- amount_written = write(trace_file, buffer_ptr, length);
- if(amount_written == -1)
- /* Failure to write shouldn't fail the build. */
- return;
- buffer_ptr += amount_written;
- length -= amount_written;
- }
-}
-
-
-
-void Linker::createWriter()
-{
- fStartCreateWriterTime = mach_absolute_time();
-
- // make a vector out of all required dylibs in fDylibMap
- std::vector<ExecutableFile::DyLibUsed> dynamicLibraries;
- // need to preserve command line order
- for (std::vector<class ObjectFile::Reader*>::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) {
- ObjectFile::Reader* reader = *it;
- for (InstallNameToReader::iterator mit=fDylibMap.begin(); mit != fDylibMap.end(); mit++) {
- if ( reader == mit->second ) {
- ExecutableFile::DyLibUsed dylibInfo;
- dylibInfo.reader = reader;
- dylibInfo.options = fDylibOptionsMap[reader];
- dynamicLibraries.push_back(dylibInfo);
- break;
- }
- }
- }
- // then add any other dylibs
- for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) {
- if ( it->second->implicitlyLinked() ) {
- // if not already in dynamicLibraries
- bool alreadyInDynamicLibraries = false;
- for (std::vector<ExecutableFile::DyLibUsed>::iterator dit=dynamicLibraries.begin(); dit != dynamicLibraries.end(); dit++) {
- if ( dit->reader == it->second ) {
- alreadyInDynamicLibraries = true;
- break;
- }
- }
- if ( ! alreadyInDynamicLibraries ) {
- ExecutableFile::DyLibUsed dylibInfo;
- dylibInfo.reader = it->second;
- std::map<ObjectFile::Reader*,DynamicLibraryOptions>::iterator pos = fDylibOptionsMap.find(it->second);
- if ( pos != fDylibOptionsMap.end() ) {
- dylibInfo.options = pos->second;
- }
- else {
- dylibInfo.options.fWeakImport = false; // FIX ME
- dylibInfo.options.fReExport = false;
- dylibInfo.options.fBundleLoader = false;
- }
- dynamicLibraries.push_back(dylibInfo);
- }
- }
- }
- if ( fBundleLoaderReader != NULL ) {
- ExecutableFile::DyLibUsed dylibInfo;
- dylibInfo.reader = fBundleLoaderReader;
- dylibInfo.options.fWeakImport = false;
- dylibInfo.options.fReExport = false;
- dylibInfo.options.fBundleLoader = true;
- dynamicLibraries.push_back(dylibInfo);
- }
-
- const char* path = fOptions.getOutputFilePath();
- switch ( fArchitecture ) {
- case CPU_TYPE_POWERPC:
- this->setOutputFile(new mach_o::executable::Writer<ppc>(path, fOptions, dynamicLibraries));
- break;
- case CPU_TYPE_POWERPC64:
- this->setOutputFile(new mach_o::executable::Writer<ppc64>(path, fOptions, dynamicLibraries));
- break;
- case CPU_TYPE_I386:
- this->setOutputFile(new mach_o::executable::Writer<x86>(path, fOptions, dynamicLibraries));
- break;
- case CPU_TYPE_X86_64:
- this->setOutputFile(new mach_o::executable::Writer<x86_64>(path, fOptions, dynamicLibraries));
- break;
- case CPU_TYPE_ARM:
- this->setOutputFile(new mach_o::executable::Writer<arm>(path, fOptions, dynamicLibraries));
- break;
- default:
- throw "unknown architecture";
- }
-}
-
-
-Linker::SymbolTable::SymbolTable(Linker& owner)
- : fOwner(owner), fRequireCount(0), fHasExternalTentativeDefinitions(false), fHasExternalWeakDefinitions(false)
-{
-}
-
-void Linker::SymbolTable::require(const char* name)
-{
- //fprintf(stderr, "require(%s)\n", name);
- Mapper::iterator pos = fTable.find(name);
- if ( pos == fTable.end() ) {
- fTable[name] = NULL;
- ++fRequireCount;
- }
-}
-
-// convenience labels for 2-dimensional switch statement
-enum AllDefinitionCombinations {
- kRegAndReg = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kRegularDefinition,
- kRegAndWeak = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kWeakDefinition,
- kRegAndTent = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kTentativeDefinition,
- kRegAndExtern = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kExternalDefinition,
- kRegAndExternWeak = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition,
- kRegAndAbsolute = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol,
- kWeakAndReg = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kRegularDefinition,
- kWeakAndWeak = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kWeakDefinition,
- kWeakAndTent = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kTentativeDefinition,
- kWeakAndExtern = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kExternalDefinition,
- kWeakAndExternWeak = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition,
- kWeakAndAbsolute = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol,
- kTentAndReg = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kRegularDefinition,
- kTentAndWeak = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kWeakDefinition,
- kTentAndTent = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kTentativeDefinition,
- kTentAndExtern = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kExternalDefinition,
- kTentAndExternWeak = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition,
- kTentAndAbsolute = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol,
- kExternAndReg = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kRegularDefinition,
- kExternAndWeak = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kWeakDefinition,
- kExternAndTent = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kTentativeDefinition,
- kExternAndExtern = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kExternalDefinition,
- kExternAndExternWeak = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition,
- kExternAndAbsolute = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol,
- kExternWeakAndReg = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kRegularDefinition,
- kExternWeakAndWeak = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kWeakDefinition,
- kExternWeakAndTent = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kTentativeDefinition,
- kExternWeakAndExtern = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kExternalDefinition,
- kExternWeakAndExternWeak= (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition,
- kExternWeakAndAbsolute = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol,
- kAbsoluteAndReg = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kRegularDefinition,
- kAbsoluteAndWeak = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kWeakDefinition,
- kAbsoluteAndTent = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kTentativeDefinition,
- kAbsoluteAndExtern = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kExternalDefinition,
- kAbsoluteAndExternWeak = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kExternalWeakDefinition,
- kAbsoluteAndAbsolute = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kAbsoluteSymbol
-};
-
-bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom)
-{
- bool useNew = true;
- bool checkVisibilityMismatch = false;
- const char* name = newAtom.getName();
- if ( newAtom.getScope() == ObjectFile::Atom::scopeGlobal ) {
- switch ( newAtom.getDefinitionKind() ) {
- case ObjectFile::Atom::kTentativeDefinition:
- fHasExternalTentativeDefinitions = true;
- break;
- case ObjectFile::Atom::kWeakDefinition:
- fHasExternalWeakDefinitions = true;
- break;
- default:
- break;
- }
- }
- //fprintf(stderr, "map.add(%s => %p from %s)\n", name, &newAtom, newAtom.getFile()->getPath());
- Mapper::iterator pos = fTable.find(name);
- ObjectFile::Atom* existingAtom = NULL;
- if ( pos != fTable.end() )
- existingAtom = pos->second;
- if ( existingAtom != NULL ) {
- // already have atom with same name in symbol table
- switch ( (AllDefinitionCombinations)((existingAtom->getDefinitionKind() << 3) | newAtom.getDefinitionKind()) ) {
- case kRegAndReg:
- throwf("duplicate symbol %s in %s and %s", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath());
- case kRegAndWeak:
- // ignore new weak atom, because we already have a non-weak one
- useNew = false;
- break;
- case kRegAndTent:
- // ignore new tentative atom, because we already have a regular one
- useNew = false;
- checkVisibilityMismatch = true;
- if ( newAtom.getSize() > existingAtom->getSize() ) {
- warning("for symbol %s tentative definition of size %llu from %s is "
- "is smaller than the real definition of size %llu from %s",
- newAtom.getDisplayName(), newAtom.getSize(), newAtom.getFile()->getPath(),
- existingAtom->getSize(), existingAtom->getFile()->getPath());
- }
- break;
- case kRegAndExtern:
- // ignore external atom, because we already have a one
- useNew = false;
- break;
- case kRegAndExternWeak:
- // ignore external atom, because we already have a one
- useNew = false;
- break;
- case kRegAndAbsolute:
- throwf("duplicate symbol %s in %s and %s", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath());
- break;
- case kWeakAndReg:
- // replace existing weak atom with regular one
- break;
- case kWeakAndWeak:
- // have another weak atom, use whichever has largest alignment requirement
- // because codegen of some client may require alignment
- useNew = ( newAtom.getAlignment().trailingZeros() > existingAtom->getAlignment().trailingZeros() );
- checkVisibilityMismatch = true;
- break;
- case kWeakAndTent:
- // replace existing weak atom with tentative one ???
- break;
- case kWeakAndExtern:
- // keep weak atom, at runtime external one may override
- useNew = false;
- break;
- case kWeakAndExternWeak:
- // keep weak atom, at runtime external one may override
- useNew = false;
- break;
- case kWeakAndAbsolute:
- // replace existing weak atom with absolute one
- break;
- case kTentAndReg:
- // replace existing tentative atom with regular one
- checkVisibilityMismatch = true;
- if ( newAtom.getSize() < existingAtom->getSize() ) {
- warning("for symbol %s tentative definition of size %llu from %s is "
- "being replaced by a real definition of size %llu from %s",
- newAtom.getDisplayName(), existingAtom->getSize(), existingAtom->getFile()->getPath(),
- newAtom.getSize(), newAtom.getFile()->getPath());
- }
- break;
- case kTentAndWeak:
- // replace existing tentative atom with weak one ???
- break;
- case kTentAndTent:
- // use largest
- checkVisibilityMismatch = true;
- if ( newAtom.getSize() < existingAtom->getSize() ) {
- useNew = false;
- }
- else {
- if ( newAtom.getAlignment().trailingZeros() < existingAtom->getAlignment().trailingZeros() )
- warning("alignment lost in merging tentative definition %s", newAtom.getDisplayName());
- }
- break;
- case kTentAndExtern:
- case kTentAndExternWeak:
- // a tentative definition and a dylib definition, so commons-mode decides how to handle
- switch ( fOwner.fOptions.commonsMode() ) {
- case Options::kCommonsIgnoreDylibs:
- if ( fOwner.fOptions.warnCommons() )
- warning("using common symbol %s from %s and ignoring defintion from dylib %s",
- existingAtom->getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath());
- useNew = false;
- break;
- case Options::kCommonsOverriddenByDylibs:
- if ( fOwner.fOptions.warnCommons() )
- warning("replacing common symbol %s from %s with true definition from dylib %s",
- existingAtom->getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath());
- break;
- case Options::kCommonsConflictsDylibsError:
- throwf("common symbol %s from %s conflicts with defintion from dylib %s",
- existingAtom->getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath());
- }
- break;
- case kTentAndAbsolute:
- // replace tentative with absolute (can't size check because absolutes have no size)
- break;
- case kExternAndReg:
- // replace external atom with regular one
- break;
- case kExternAndWeak:
- // replace external atom with weak one
- break;
- case kExternAndTent:
- // a tentative definition and a dylib definition, so commons-mode decides how to handle
- switch ( fOwner.fOptions.commonsMode() ) {
- case Options::kCommonsIgnoreDylibs:
- if ( fOwner.fOptions.warnCommons() )
- warning("using common symbol %s from %s and ignoring defintion from dylib %s",
- newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath());
- break;
- case Options::kCommonsOverriddenByDylibs:
- if ( fOwner.fOptions.warnCommons() )
- warning("replacing defintion of %s from dylib %s with common symbol from %s",
- newAtom.getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath());
- useNew = false;
- break;
- case Options::kCommonsConflictsDylibsError:
- throwf("common symbol %s from %s conflicts with defintion from dylib %s",
- newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath());
- }
- break;
- case kExternAndExtern:
- throwf("duplicate symbol %s in %s and %s\n", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath());
- case kExternAndExternWeak:
- // keep strong dylib atom, ignore weak one
- useNew = false;
- break;
- case kExternAndAbsolute:
- // replace external atom with absolute one
- break;
- case kExternWeakAndReg:
- // replace existing weak external with regular
- break;
- case kExternWeakAndWeak:
- // replace existing weak external with weak (let dyld decide at runtime which to use)
- break;
- case kExternWeakAndTent:
- // a tentative definition and a dylib definition, so commons-mode decides how to handle
- switch ( fOwner.fOptions.commonsMode() ) {
- case Options::kCommonsIgnoreDylibs:
- if ( fOwner.fOptions.warnCommons() )
- warning("using common symbol %s from %s and ignoring defintion from dylib %s",
- newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath());
- break;
- case Options::kCommonsOverriddenByDylibs:
- if ( fOwner.fOptions.warnCommons() )
- warning("replacing defintion of %s from dylib %s with common symbol from %s",
- newAtom.getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath());
- useNew = false;
- break;
- case Options::kCommonsConflictsDylibsError:
- throwf("common symbol %s from %s conflicts with defintion from dylib %s",
- newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath());
- }
- break;
- case kExternWeakAndExtern:
- // replace existing weak external with external
- break;
- case kExternWeakAndExternWeak:
- // keep existing external weak
- useNew = false;
- break;
- case kExternWeakAndAbsolute:
- // replace existing weak external with absolute
- break;
- case kAbsoluteAndReg:
- throwf("duplicate symbol %s in %s and %s", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath());
- case kAbsoluteAndWeak:
- // ignore new weak atom, because we already have a non-weak one
- useNew = false;
- break;
- case kAbsoluteAndTent:
- // ignore new tentative atom, because we already have a regular one
- useNew = false;
- break;
- case kAbsoluteAndExtern:
- // ignore external atom, because we already have a one
- useNew = false;
- break;
- case kAbsoluteAndExternWeak:
- // ignore external atom, because we already have a one
- useNew = false;
- break;
- case kAbsoluteAndAbsolute:
- throwf("duplicate symbol %s in %s and %s", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath());
- break;
- }
- }
- if ( (existingAtom != NULL) && checkVisibilityMismatch && (newAtom.getScope() != existingAtom->getScope()) ) {
- warning("%s has different visibility (%s) in %s and (%s) in %s",
- newAtom.getDisplayName(), (newAtom.getScope() == 1 ? "hidden" : "default"), newAtom.getFile()->getPath(), (existingAtom->getScope() == 1 ? "hidden" : "default"), existingAtom->getFile()->getPath());
- }
- if ( useNew ) {
- fTable[name] = &newAtom;
- if ( existingAtom != NULL )
- fOwner.markDead(existingAtom);
- }
- else {
- fOwner.markDead(&newAtom);
- }
- return useNew;
-}
-
-
-
-ObjectFile::Atom* Linker::SymbolTable::find(const char* name)
-{
- Mapper::iterator pos = fTable.find(name);
- if ( pos != fTable.end() ) {
- return pos->second;
- }
- return NULL;
-}
-
-
-void Linker::SymbolTable::getNeededNames(bool andWeakDefintions, std::vector<const char*>& undefines)
-{
- for (Mapper::iterator it=fTable.begin(); it != fTable.end(); it++) {
- if ( (it->second == NULL) || (andWeakDefintions && (it->second->getDefinitionKind()==ObjectFile::Atom::kWeakDefinition)) ) {
- undefines.push_back(it->first);
- }
- }
-}
-
-
-
-bool Linker::AtomSorter::operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right)
-{
- if ( left == right )
- return false;
-
- // first sort by section order (which is already sorted by segment)
- unsigned int leftSectionIndex = left->getSection()->getIndex();
- unsigned int rightSectionIndex = right->getSection()->getIndex();
- if ( leftSectionIndex != rightSectionIndex)
- return (leftSectionIndex < rightSectionIndex);
-
- // if a -order_file is specified, then sorting is altered to sort those symbols first
- if ( fOverriddenOrdinalMap != NULL ) {
- std::map<const ObjectFile::Atom*, uint32_t>::iterator leftPos = fOverriddenOrdinalMap->find(left);
- std::map<const ObjectFile::Atom*, uint32_t>::iterator rightPos = fOverriddenOrdinalMap->find(right);
- std::map<const ObjectFile::Atom*, uint32_t>::iterator end = fOverriddenOrdinalMap->end();
- if ( leftPos != end ) {
- if ( rightPos != end ) {
- // both left and right are overridden, so compare overridden ordinals
- return leftPos->second < rightPos->second;
- }
- else {
- // left is overridden and right is not, so left < right
- return true;
- }
- }
- else {
- if ( rightPos != end ) {
- // right is overridden and left is not, so right < left
- return false;
- }
- else {
- // neither are overridden, do default sort
- // fall into default sorting below
- }
- }
- }
-
- // the __common section can have real or tentative definitions
- // we want the real ones to sort before tentative ones
- bool leftIsTent = (left->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition);
- bool rightIsTent = (right->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition);
- if ( leftIsTent != rightIsTent )
- return rightIsTent;
-
- // lastly sort by atom ordinal. this is already sorted by .o order
- return left->getOrdinal() < right->getOrdinal();
-}
-
-
-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();
-
- // open output file
- ld.createWriter();
-
- // do linking
- ld.link();
- }
- catch (const char* msg) {
- if ( archInferred )
- fprintf(stderr, "ld: %s for inferred architecture %s\n", msg, archName);
- else if ( showArch )
- fprintf(stderr, "ld: %s for architecture %s\n", msg, archName);
- else
- fprintf(stderr, "ld: %s\n", msg);
- return 1;
- }
-
- return 0;
-}
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2005-2007 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifndef __ARCHITECTURES__
+#define __ARCHITECTURES__
+
+#include "FileAbstraction.hpp"
+
+
+//
+// Architectures
+//
+struct ppc
+{
+ typedef Pointer32<BigEndian> P;
+
+ enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport,
+ kPointerDiff16, kPointerDiff32, kPointerDiff=kPointerDiff32, kPointerDiff64,
+ kBranch24, kBranch24WeakImport, kBranch14,
+ kPICBaseLow16, kPICBaseLow14, kPICBaseHigh16,
+ kAbsLow16, kAbsLow14, kAbsHigh16, kAbsHigh16AddLow,
+ kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference };
+};
+
+struct ppc64
+{
+ typedef Pointer64<BigEndian> P;
+
+ enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport,
+ kPointerDiff16, kPointerDiff32, kPointerDiff64, kPointerDiff=kPointerDiff64,
+ kBranch24, kBranch24WeakImport, kBranch14,
+ kPICBaseLow16, kPICBaseLow14, kPICBaseHigh16,
+ kAbsLow16, kAbsLow14, kAbsHigh16, kAbsHigh16AddLow,
+ kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference };
+};
+
+struct x86
+{
+ typedef Pointer32<LittleEndian> P;
+
+ enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff, kPointerDiff32=kPointerDiff, kPointerDiff16,
+ kPCRel32, kPCRel32WeakImport, kAbsolute32, kPCRel16, kPCRel8,
+ kImageOffset32, kPointerDiff24, kSectionOffset24,
+ kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference };
+};
+
+struct x86_64
+{
+ typedef Pointer64<LittleEndian> P;
+
+ enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointer32, kPointerWeakImport, kPointerDiff, kPointerDiff32,
+ kPCRel32, kPCRel32_1, kPCRel32_2, kPCRel32_4,
+ kBranchPCRel32, kBranchPCRel32WeakImport,
+ kPCRel32GOTLoad, kPCRel32GOTLoadWeakImport,
+ kPCRel32GOT, kPCRel32GOTWeakImport, kBranchPCRel8, kGOTNoFixUp,
+ kImageOffset32, kPointerDiff24, kSectionOffset24,
+ kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference };
+};
+
+struct arm
+{
+ typedef Pointer32<LittleEndian> P;
+
+ enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff, kPointerDiff32=kPointerDiff, kReadOnlyPointer,
+ kBranch24, kBranch24WeakImport, kThumbBranch22, kThumbBranch22WeakImport,
+ kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference };
+};
+
+#endif // __ARCHITECTURES__
+
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2005-2008 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifndef __OBJECT_FILE_ARCHIVE__
+#define __OBJECT_FILE_ARCHIVE__
+
+#include <stdint.h>
+#include <math.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <mach-o/ranlib.h>
+#include <ar.h>
+
+#include <vector>
+#include <set>
+#include <algorithm>
+#include <ext/hash_map>
+
+#include "MachOFileAbstraction.hpp"
+#include "ObjectFile.h"
+#include "MachOReaderRelocatable.hpp"
+#if LTO_SUPPORT
+ #include "LTOReader.hpp"
+#endif
+
+namespace archive {
+
+typedef const struct ranlib* ConstRanLibPtr;
+
+template <typename A>
+class Reader : public ObjectFile::Reader
+{
+public:
+ static bool validFile(const uint8_t* fileContent, uint64_t fileLength);
+ Reader(const uint8_t fileContent[], uint64_t fileLength,
+ const char* path, time_t modTime,
+ const LibraryOptions& archiveOptions,
+ const ObjectFile::ReaderOptions& options, uint32_t ordinalBase);
+ virtual ~Reader() {}
+
+ virtual const char* getPath() { return fPath; }
+ virtual time_t getModificationTime(){ return fModTime; }
+ virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; }
+ virtual std::vector<class ObjectFile::Atom*>& getAtoms();
+ virtual std::vector<class ObjectFile::Atom*>* getJustInTimeAtomsFor(const char* name);
+ virtual std::vector<Stab>* getStabs() { return NULL; }
+ virtual bool optimize(const std::vector<ObjectFile::Atom*>&, std::vector<ObjectFile::Atom*>&,
+ std::vector<const char*>&, const std::set<ObjectFile::Atom*>&,
+ std::vector<ObjectFile::Atom*>& newDeadAtoms,
+ uint32_t, ObjectFile::Reader* writer,
+ ObjectFile::Atom* entryPointAtom,
+ const std::vector<const char*>& llvmOptions,
+ bool allGlobalsAReDeadStripRoots, int okind,
+ bool verbose, bool saveTemps, const char* outputFilePath,
+ bool pie, bool allowTextRelocs);
+
+private:
+ static bool validMachOFile(const uint8_t* fileContent, uint64_t fileLength);
+ static bool validLTOFile(const uint8_t* fileContent, uint64_t fileLength);
+ static cpu_type_t architecture();
+
+
+ class Entry : ar_hdr
+ {
+ public:
+ const char* getName() const;
+ time_t getModTime() const;
+ const uint8_t* getContent() const;
+ uint32_t getContentSize() const;
+ const Entry* getNext() const;
+ private:
+ bool hasLongName() const;
+ unsigned int getLongNameSpace() const;
+
+ };
+
+ class CStringEquals
+ {
+ public:
+ bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
+ };
+ typedef __gnu_cxx::hash_map<const char*, const struct ranlib*, __gnu_cxx::hash<const char*>, CStringEquals> NameToEntryMap;
+
+ typedef typename A::P P;
+ typedef typename A::P::E E;
+
+ const struct ranlib* ranlibHashSearch(const char* name);
+ ObjectFile::Reader* makeObjectReaderForMember(const Entry* member);
+ void dumpTableOfContents();
+ void buildHashTable();
+
+ const char* fPath;
+ time_t fModTime;
+ const ObjectFile::ReaderOptions& fOptions;
+ uint32_t fOrdinalBase;
+ const uint8_t* fFileContent;
+ uint64_t fFileLength;
+ const struct ranlib* fTableOfContents;
+ uint32_t fTableOfContentCount;
+ const char* fStringPool;
+ std::vector<class ObjectFile::Atom*> fAllAtoms;
+ std::vector<class ObjectFile::Reader*> fInstantiatedReaders;
+ std::set<const class Entry*> fInstantiatedEntries;
+ std::set<const class Entry*> fPossibleEntries;
+ NameToEntryMap fHashTable;
+ bool fForceLoad;
+
+ static std::vector<class ObjectFile::Atom*> fgEmptyList;
+};
+
+template <typename A>
+std::vector<class ObjectFile::Atom*> Reader<A>::fgEmptyList;
+
+
+template <typename A>
+bool Reader<A>::Entry::hasLongName() const
+{
+ return ( strncmp(this->ar_name, AR_EFMT1, strlen(AR_EFMT1)) == 0 );
+}
+
+template <typename A>
+unsigned int Reader<A>::Entry::getLongNameSpace() const
+{
+ char* endptr;
+ long result = strtol(&this->ar_name[strlen(AR_EFMT1)], &endptr, 10);
+ return result;
+}
+
+template <typename A>
+const char* Reader<A>::Entry::getName() const
+{
+ if ( this->hasLongName() ) {
+ int len = this->getLongNameSpace();
+ static char longName[256];
+ strncpy(longName, ((char*)this)+sizeof(ar_hdr), len);
+ longName[len] = '\0';
+ return longName;
+ }
+ else {
+ static char shortName[20];
+ strncpy(shortName, this->ar_name, 16);
+ shortName[16] = '\0';
+ char* space = strchr(shortName, ' ');
+ if ( space != NULL )
+ *space = '\0';
+ return shortName;
+ }
+}
+
+template <typename A>
+time_t Reader<A>::Entry::getModTime() const
+{
+ char temp[14];
+ strncpy(temp, this->ar_date, 12);
+ temp[12] = '\0';
+ char* endptr;
+ return (time_t)strtol(temp, &endptr, 10);
+}
+
+
+template <typename A>
+const uint8_t* Reader<A>::Entry::getContent() const
+{
+ if ( this->hasLongName() )
+ return ((uint8_t*)this) + sizeof(ar_hdr) + this->getLongNameSpace();
+ else
+ return ((uint8_t*)this) + sizeof(ar_hdr);
+}
+
+
+template <typename A>
+uint32_t Reader<A>::Entry::getContentSize() const
+{
+ char temp[12];
+ strncpy(temp, this->ar_size, 10);
+ temp[10] = '\0';
+ char* endptr;
+ long size = strtol(temp, &endptr, 10);
+ // long name is included in ar_size
+ if ( this->hasLongName() )
+ size -= this->getLongNameSpace();
+ return size;
+}
+
+
+template <typename A>
+const class Reader<A>::Entry* Reader<A>::Entry::getNext() const
+{
+ const uint8_t* p = this->getContent() + getContentSize();
+ p = (const uint8_t*)(((uintptr_t)p+3) & (-4)); // 4-byte align
+ return (class Reader<A>::Entry*)p;
+}
+
+
+template <> cpu_type_t Reader<ppc>::architecture() { return CPU_TYPE_POWERPC; }
+template <> cpu_type_t Reader<ppc64>::architecture() { return CPU_TYPE_POWERPC64; }
+template <> cpu_type_t Reader<x86>::architecture() { return CPU_TYPE_I386; }
+template <> cpu_type_t Reader<x86_64>::architecture() { return CPU_TYPE_X86_64; }
+template <> cpu_type_t Reader<arm>::architecture() { return CPU_TYPE_ARM; }
+
+
+template <typename A>
+bool Reader<A>::validMachOFile(const uint8_t* fileContent, uint64_t fileLength)
+{
+ return mach_o::relocatable::Reader<A>::validFile(fileContent);
+}
+
+template <typename A>
+bool Reader<A>::validLTOFile(const uint8_t* fileContent, uint64_t fileLength)
+{
+#if LTO_SUPPORT
+ return lto::Reader::validFile(fileContent, fileLength, architecture());
+#else
+ return false;
+#endif
+}
+
+
+
+template <typename A>
+bool Reader<A>::validFile(const uint8_t* fileContent, uint64_t fileLength)
+{
+ // must have valid archive header
+ if ( strncmp((const char*)fileContent, "!<arch>\n", 8) != 0 )
+ return false;
+
+ // peak at first .o file and verify it is correct architecture
+ const Entry* const start = (Entry*)&fileContent[8];
+ const Entry* const end = (Entry*)&fileContent[fileLength];
+ for (const Entry* p=start; p < end; p = p->getNext()) {
+ const char* memberName = p->getName();
+ // skip option table-of-content member
+ if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) )
+ continue;
+ // archive is valid if first .o file is valid
+ return (validMachOFile(p->getContent(), p->getContentSize()) || validLTOFile(p->getContent(), p->getContentSize()));
+ }
+ // empty archive
+ return true;
+}
+
+template <typename A>
+Reader<A>::Reader(const uint8_t fileContent[], uint64_t fileLength, const char* path, time_t modTime,
+ const LibraryOptions& archiveOptions,
+ const ObjectFile::ReaderOptions& options, uint32_t ordinalBase)
+ : fPath(NULL), fModTime(modTime), fOptions(options), fOrdinalBase(ordinalBase), fFileContent(NULL),
+ fTableOfContents(NULL), fTableOfContentCount(0), fStringPool(NULL), fForceLoad(archiveOptions.fForceLoad)
+{
+ fPath = strdup(path);
+ fFileContent = fileContent;
+ fFileLength = fileLength;
+
+ if ( strncmp((const char*)fileContent, "!<arch>\n", 8) != 0 )
+ throw "not an archive";
+
+ // write out path for -whatsloaded option
+ if ( options.fLogAllFiles )
+ printf("%s\n", path);
+
+ if ( !options.fFullyLoadArchives && !fForceLoad ) {
+ const Entry* const firstMember = (Entry*)&fFileContent[8];
+ if ( (strcmp(firstMember->getName(), SYMDEF_SORTED) == 0) || (strcmp(firstMember->getName(), SYMDEF) == 0) ) {
+ const uint8_t* contents = firstMember->getContent();
+ uint32_t ranlibArrayLen = E::get32(*((uint32_t*)contents));
+ fTableOfContents = (const struct ranlib*)&contents[4];
+ fTableOfContentCount = ranlibArrayLen / sizeof(struct ranlib);
+ fStringPool = (const char*)&contents[ranlibArrayLen+8];
+ if ( ((uint8_t*)(&fTableOfContents[fTableOfContentCount]) > &fileContent[fileLength])
+ || ((uint8_t*)fStringPool > &fileContent[fileLength]) )
+ throw "malformed archive, perhaps wrong architecture";
+ this->buildHashTable();
+ }
+ else
+ throw "archive has no table of contents";
+ }
+}
+
+
+template <typename A>
+ObjectFile::Reader* Reader<A>::makeObjectReaderForMember(const Entry* member)
+{
+ const char* memberName = member->getName();
+ char memberPath[strlen(fPath) + strlen(memberName)+4];
+ strcpy(memberPath, fPath);
+ strcat(memberPath, "(");
+ strcat(memberPath, memberName);
+ strcat(memberPath, ")");
+ //fprintf(stderr, "using %s from %s\n", memberName, fPath);
+ try {
+ // offset the ordinals in this mach-o .o file, so that atoms layout in same order as in archive
+ uint32_t ordinalBase = fOrdinalBase + (uint8_t*)member - fFileContent;
+ if ( validMachOFile(member->getContent(), member->getContentSize()) ) {
+ return new typename mach_o::relocatable::Reader<A>::Reader(member->getContent(), memberPath, member->getModTime(), fOptions, ordinalBase);
+ }
+#if LTO_SUPPORT
+ else if ( validLTOFile(member->getContent(), member->getContentSize()) ) {
+ return new typename lto::Reader(member->getContent(), member->getContentSize(), memberPath, member->getModTime(), fOptions, architecture());
+ }
+#endif
+ throwf("archive member '%s' with length %d is not mach-o or bitcode", memberName, member->getContentSize());
+ }
+ catch (const char* msg) {
+ throwf("in %s, %s", memberPath, msg);
+ }
+}
+
+
+template <typename A>
+std::vector<class ObjectFile::Atom*>& Reader<A>::getAtoms()
+{
+ if ( fOptions.fFullyLoadArchives || fForceLoad ) {
+ // build vector of all atoms from all .o files in this archive
+ const Entry* const start = (Entry*)&fFileContent[8];
+ const Entry* const end = (Entry*)&fFileContent[fFileLength];
+ for (const Entry* p=start; p < end; p = p->getNext()) {
+ const char* memberName = p->getName();
+ if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) )
+ continue;
+ if ( fOptions.fWhyLoad ) {
+ if ( fForceLoad )
+ printf("-force_load forced load of %s(%s)\n", this->getPath(), memberName);
+ else
+ printf("-all_load forced load of %s(%s)\n", this->getPath(), memberName);
+ }
+ ObjectFile::Reader* r = this->makeObjectReaderForMember(p);
+ std::vector<class ObjectFile::Atom*>& atoms = r->getAtoms();
+ fAllAtoms.insert(fAllAtoms.end(), atoms.begin(), atoms.end());
+ fInstantiatedReaders.push_back(r);
+ }
+ return fAllAtoms;
+ }
+ else if ( fOptions.fLoadAllObjcObjectsFromArchives ) {
+ // build vector of all atoms from all .o files containing objc classes in this archive
+ for(class NameToEntryMap::iterator it = fHashTable.begin(); it != fHashTable.end(); ++it) {
+ if ( (strncmp(it->first, ".objc_c", 7) == 0) || (strncmp(it->first, "_OBJC_CLASS_$_", 14) == 0) ) {
+ const Entry* member = (Entry*)&fFileContent[E::get32(it->second->ran_off)];
+ if ( fInstantiatedEntries.count(member) == 0 ) {
+ if ( fOptions.fWhyLoad )
+ printf("-ObjC forced load of %s(%s)\n", this->getPath(), member->getName());
+ // only return these atoms once
+ fInstantiatedEntries.insert(member);
+ ObjectFile::Reader* r = makeObjectReaderForMember(member);
+ std::vector<class ObjectFile::Atom*>& atoms = r->getAtoms();
+ fAllAtoms.insert(fAllAtoms.end(), atoms.begin(), atoms.end());
+ fInstantiatedReaders.push_back(r);
+ }
+ }
+ }
+ return fAllAtoms;
+ }
+ else {
+ // return nonthing for now, getJustInTimeAtomsFor() will return atoms as needed
+ return fgEmptyList;
+ }
+}
+
+template <typename A>
+bool Reader<A>::optimize(const std::vector<ObjectFile::Atom*>& allAtoms, std::vector<ObjectFile::Atom*>& newAtoms,
+ std::vector<const char*>& additionalUndefines, const std::set<ObjectFile::Atom*>& deadAtoms,
+ std::vector<ObjectFile::Atom*>& newDeadAtoms,
+ uint32_t nextOrdinal, ObjectFile::Reader* writer, ObjectFile::Atom* entryPointAtom,
+ const std::vector<const char*>& llvmOptions,
+ bool allGlobalsAReDeadStripRoots, int okind,
+ bool verbose, bool saveTemps, const char* outputFilePath,
+ bool pie, bool allowTextRelocs)
+{
+ bool result = false;
+ for(std::vector<ObjectFile::Reader*>::iterator it=fInstantiatedReaders.begin(); it != fInstantiatedReaders.end(); ++it) {
+ result |= (*it)->optimize(allAtoms, newAtoms, additionalUndefines, deadAtoms, newDeadAtoms, nextOrdinal,
+ writer, entryPointAtom, llvmOptions, allGlobalsAReDeadStripRoots, okind,
+ verbose, saveTemps, outputFilePath, pie, allowTextRelocs);
+ }
+ return result;
+}
+
+
+
+template <typename A>
+ConstRanLibPtr Reader<A>::ranlibHashSearch(const char* name)
+{
+ class NameToEntryMap::iterator pos = fHashTable.find(name);
+ if ( pos != fHashTable.end() )
+ return pos->second;
+ else
+ return NULL;
+}
+
+template <typename A>
+void Reader<A>::buildHashTable()
+{
+ // walk through list backwards, adding/overwriting entries
+ // this assures that with duplicates those earliest in the list will be found
+ for (int i = fTableOfContentCount-1; i >= 0; --i) {
+ const struct ranlib* entry = &fTableOfContents[i];
+ const char* entryName = &fStringPool[E::get32(entry->ran_un.ran_strx)];
+ const Entry* member = (Entry*)&fFileContent[E::get32(entry->ran_off)];
+ //fprintf(stderr, "adding hash %d, %s -> %p\n", i, entryName, entry);
+ fHashTable[entryName] = entry;
+ fPossibleEntries.insert(member);
+ }
+}
+
+template <typename A>
+void Reader<A>::dumpTableOfContents()
+{
+ for (unsigned int i=0; i < fTableOfContentCount; ++i) {
+ const struct ranlib* e = &fTableOfContents[i];
+ printf("%s in %s\n", &fStringPool[E::get32(e->ran_un.ran_strx)], ((Entry*)&fFileContent[E::get32(e->ran_off)])->getName());
+ }
+}
+
+template <typename A>
+std::vector<class ObjectFile::Atom*>* Reader<A>::getJustInTimeAtomsFor(const char* name)
+{
+ if ( fOptions.fFullyLoadArchives || fForceLoad ) {
+ return NULL;
+ }
+ else {
+ const struct ranlib* result = NULL;
+ // do a hash search of table of contents looking for requested symbol
+ result = ranlibHashSearch(name);
+ if ( result != NULL ) {
+ const Entry* member = (Entry*)&fFileContent[E::get32(result->ran_off)];
+ if ( fInstantiatedEntries.count(member) == 0 ) {
+ 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);
+ fInstantiatedReaders.push_back(r);
+ return new std::vector<class ObjectFile::Atom*>(r->getAtoms());
+ }
+ }
+ //fprintf(stderr, "%s NOT found in archive %s\n", name, fPath);
+ return NULL;
+ }
+}
+
+
+
+
+
+}; // namespace archive
+
+
+#endif // __OBJECT_FILE_ARCHIVE__
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2005-2007 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifndef __EXECUTABLEFILE__
+#define __EXECUTABLEFILE__
+
+#include <stdint.h>
+#include <vector>
+
+#include "ObjectFile.h"
+#include "Options.h"
+
+
+namespace ExecutableFile {
+
+ struct DyLibUsed
+ {
+ ObjectFile::Reader* reader;
+ LibraryOptions options;
+ };
+
+ class Writer : public ObjectFile::Reader
+ {
+ public:
+ virtual ~Writer() {};
+
+ virtual const char* getPath() = 0;
+ virtual std::vector<class ObjectFile::Atom*>& getAtoms() = 0;
+ virtual std::vector<class ObjectFile::Atom*>* getJustInTimeAtomsFor(const char* name) = 0;
+ virtual ObjectFile::Atom& makeObjcInfoAtom(ObjectFile::Reader::ObjcConstraint objcContraint,
+ bool objcReplacementClasses) = 0;
+ virtual class ObjectFile::Atom* getUndefinedProxyAtom(const char* name) = 0;
+ virtual uint64_t write(std::vector<class ObjectFile::Atom*>& atoms,
+ std::vector<class ObjectFile::Reader::Stab>& stabs,
+ class ObjectFile::Atom* entryPointAtom,
+ class ObjectFile::Atom* dyldClassicHelperAtom,
+ class ObjectFile::Atom* dyldCompressedHelperAtom,
+ class ObjectFile::Atom* dyldLazyDylibHelperAtom,
+ bool createUUID, bool canScatter,
+ ObjectFile::Reader::CpuConstraint cpuConstraint,
+ bool biggerThanTwoGigs,
+ std::set<const class ObjectFile::Atom*>& atomsThatOverrideWeak,
+ bool hasExternalWeakDefinitions) = 0;
+
+ protected:
+ Writer(std::vector<DyLibUsed>&) {};
+ };
+
+};
+
+#endif // __EXECUTABLEFILE__
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2006-2008 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifndef __LTO_READER_H__
+#define __LTO_READER_H__
+
+#include <stdlib.h>
+#include <mach-o/dyld.h>
+#include <vector>
+#include <ext/hash_set>
+#include <ext/hash_map>
+
+#include "MachOFileAbstraction.hpp"
+#include "Architectures.hpp"
+#include "ObjectFile.h"
+#include "Options.h"
+
+#include "llvm-c/lto.h"
+
+
+namespace lto {
+
+
+//
+// Reference handles Atom references. These references facilitate
+// symbol resolution.
+//
+
+class Reference : public ObjectFile::Reference
+{
+public:
+ Reference(const char* name) : fTargetName(name), fTargetAtom(NULL) { }
+ Reference(ObjectFile::Atom& atom) : fTargetName(NULL), fTargetAtom(&atom) { }
+
+ bool isTargetUnbound() const { return fTargetAtom == NULL; }
+ bool isFromTargetUnbound() const { return true; }
+ uint8_t getKind() const { return 0; }
+ uint64_t getFixUpOffset() const { return 0; }
+ const char * getTargetName() const { return fTargetName; }
+ ObjectFile::Atom& getTarget() const { return *fTargetAtom; }
+ uint64_t getTargetOffset() const { return 0; }
+ bool hasFromTarget() const { return false; }
+ ObjectFile::Atom& getFromTarget() const { return *((ObjectFile::Atom*)NULL); }
+ const char * getFromTargetName() const { return NULL; }
+ uint64_t getFromTargetOffset() const { return 0; }
+ TargetBinding getTargetBinding() const;
+ TargetBinding getFromTargetBinding() const { return kDontBind; }
+ void setTarget (ObjectFile::Atom& a, uint64_t offset)
+ { fTargetAtom = &a; }
+ void setFromTarget(ObjectFile::Atom &a) { }
+ const char * getDescription() const;
+
+private:
+ const char * fTargetName;
+ ObjectFile::Atom * fTargetAtom;
+};
+
+
+ObjectFile::Reference::TargetBinding Reference::getTargetBinding() const
+{
+ if ( fTargetAtom == NULL )
+ return kUnboundByName;
+ else if ( fTargetName == NULL )
+ return kBoundDirectly;
+ else
+ return kBoundByName;
+}
+
+const char* Reference::getDescription() const
+{
+ static char temp[256];
+ strcpy(temp, "reference to ");
+ if ( fTargetName != NULL )
+ strcat(temp, fTargetName);
+ else
+ strcat(temp, fTargetAtom->getDisplayName());
+ return temp;
+}
+
+
+class Segment : public ObjectFile::Segment
+{
+public:
+ Segment(const char* name, bool readable, bool writable, bool executable, bool fixedAddress)
+ : fName(name), fReadable(readable), fWritable(writable), fExecutable(executable), fFixedAddress(fixedAddress) {}
+ virtual const char* getName() const { return fName; }
+ virtual bool isContentReadable() const { return fReadable; }
+ virtual bool isContentWritable() const { return fWritable; }
+ virtual bool isContentExecutable() const { return fExecutable; }
+ virtual bool hasFixedAddress() const { return fFixedAddress; }
+
+ static Segment fgBootstrapSegment;
+
+private:
+ const char* fName;
+ const bool fReadable;
+ const bool fWritable;
+ const bool fExecutable;
+ const bool fFixedAddress;
+};
+
+Segment Segment:: fgBootstrapSegment("__TEMP", true, false, false, false);
+
+
+
+
+//
+// Atom acts as a proxy Atom for the symbols that are exported by LLVM bitcode file. Initially,
+// Reader creates Atoms to allow linker proceed with usual symbol resolution phase. After
+// optimization is performed, real Atoms are created for these symobls. However these real Atoms
+// are not inserted into global symbol table. Atom holds real Atom and forwards appropriate
+// methods to real atom.
+//
+class Atom : public ObjectFile::Atom
+{
+public:
+ Atom(class Reader& owner, const char* name, Scope, DefinitionKind, uint8_t alignment, ObjectFile::Atom& internalAtom);
+
+ ObjectFile::Reader* getFile() const { return (ObjectFile::Reader*)&fOwner; }
+ bool getTranslationUnitSource (const char **dir, const char **name) const
+ { return fRealAtom->getTranslationUnitSource(dir, name); }
+ const char * getName () const { return fName; }
+ const char * getDisplayName() const { return this->getName(); }
+ Scope getScope() const { return (fRealAtom ? fRealAtom->getScope() : fScope); }
+ DefinitionKind getDefinitionKind() const { return (fRealAtom ? fRealAtom->getDefinitionKind() : fKind); }
+ SymbolTableInclusion getSymbolTableInclusion() const
+ { return fRealAtom->getSymbolTableInclusion(); }
+ bool dontDeadStrip() const { return false; }
+ bool isZeroFill() const { return (fRealAtom ? fRealAtom->isZeroFill() : false); }
+ bool isThumb() const { return false; }
+ uint64_t getSize() const { return (fRealAtom ? fRealAtom->getSize() : 0); }
+ std::vector<ObjectFile::Reference*>& getReferences() const
+ { return (fRealAtom ? fRealAtom->getReferences() : (std::vector<ObjectFile::Reference*>&)fReferences); }
+ bool mustRemainInSection() const { return fRealAtom->mustRemainInSection(); }
+ const char * getSectionName() const { return (fRealAtom ? fRealAtom->getSectionName() : NULL); }
+ // Linker::optimize() sets section for this atom, not fRealAtom. Use this Atom's fSection.
+ class ObjectFile::Section * getSection() const { return fSection; }
+ ObjectFile::Segment& getSegment() const { return (fRealAtom ? fRealAtom->getSegment() : Segment::fgBootstrapSegment); }
+ uint32_t getOrdinal() const { return (fRealAtom ? fRealAtom->getOrdinal() : 0); }
+ ObjectFile::Atom& getFollowOnAtom() const { return fRealAtom->getFollowOnAtom(); }
+ std::vector<ObjectFile::LineInfo>* getLineInfo() const { return (fRealAtom ? fRealAtom->getLineInfo() : NULL); }
+ ObjectFile::Alignment getAlignment() const { return (fRealAtom ? fRealAtom->getAlignment() : ObjectFile::Alignment(fAlignment)); }
+ void copyRawContent(uint8_t buffer[]) const
+ { if (fRealAtom) fRealAtom->copyRawContent(buffer); }
+ void setScope(Scope s) { if (fRealAtom) fRealAtom->setScope(s); else fScope = s; }
+
+ void setRealAtom (ObjectFile::Atom *atom)
+ { fRealAtom = atom; }
+ ObjectFile::Atom * getRealAtom() { return fRealAtom; }
+ void addReference(ObjectFile::Reference *ref)
+ { fReferences.push_back(ref); }
+
+ void setSectionOffset(uint64_t offset) { fSectionOffset = offset; if (fRealAtom) fRealAtom->setSectionOffset(offset); }
+ void setSection(class ObjectFile::Section* sect) { fSection = sect; if (fRealAtom) fRealAtom->setSection(sect); }
+
+private:
+ class Reader& fOwner;
+ const char* fName;
+ ObjectFile::Atom::Scope fScope;
+ ObjectFile::Atom::DefinitionKind fKind;
+ uint8_t fAlignment;
+ ObjectFile::Atom* fRealAtom;
+ std::vector<ObjectFile::Reference*> fReferences;
+};
+
+
+Atom::Atom(class Reader& owner, const char* name, Scope scope, DefinitionKind kind, uint8_t alignment, ObjectFile::Atom& internalAtom)
+: fOwner(owner), fName(name), fScope(scope), fKind(kind), fAlignment(alignment), fRealAtom(NULL)
+{
+ // every Atom references the InternalAtom for its reader
+ fReferences.push_back(new Reference(internalAtom));
+}
+
+
+//
+// ld64 only tracks non-internal symbols from an llvm bitcode file.
+// We model this by having an InternalAtom which represent all internal functions and data.
+// All non-interal symbols from a bitcode file are represented by a Atom
+// and each Atom has a reference to the InternalAtom. The InternalAtom
+// also has references to each symbol external to the bitcode file.
+//
+class InternalAtom : public ObjectFile::Atom
+{
+public:
+ InternalAtom(class Reader& owner) : fOwner(owner) {}
+
+ ObjectFile::Reader * getFile() const { return (ObjectFile::Reader*)&fOwner; }
+ bool getTranslationUnitSource (const char **dir, const char **name) const
+ { return false; }
+ const char * getName () const { return "__llvm-internal-atom"; }
+ const char * getDisplayName() const { return "llvm bitcode"; }
+ Scope getScope() const { return scopeTranslationUnit; }
+ DefinitionKind getDefinitionKind() const { return kRegularDefinition; }
+ SymbolTableInclusion getSymbolTableInclusion() const { return kSymbolTableNotIn; }
+ bool dontDeadStrip() const { return false; }
+ bool isZeroFill() const { return false; }
+ bool isThumb() const { return false; }
+ uint64_t getSize() const { return 0; }
+ std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)fReferences; }
+ bool mustRemainInSection() const { return false; }
+ const char * getSectionName() const { return NULL; }
+ class ObjectFile::Section * getSection() const { return NULL; }
+ ObjectFile::Segment& getSegment() const { return Segment::fgBootstrapSegment; }
+ uint32_t getOrdinal() const { return 0; }
+ ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); }
+ std::vector<ObjectFile::LineInfo>* getLineInfo() const { return NULL; }
+ ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); }
+ void copyRawContent(uint8_t buffer[]) const { }
+ void setScope(Scope s) { }
+
+ void addReference(const char* targetName);
+
+private:
+ class Reader& fOwner;
+ std::vector<ObjectFile::Reference*> fReferences;
+};
+
+
+void InternalAtom::addReference(const char* name)
+{
+ fReferences.push_back(new Reference(name));
+}
+
+
+
+
+class RemovableAtoms
+{
+public:
+ RemovableAtoms(std::set<ObjectFile::Atom*>& iAtoms) : fAtoms(iAtoms) {}
+
+ bool operator()(ObjectFile::Atom*& atom) const {
+ return ( fAtoms.count(atom) != 0 );
+ }
+
+private:
+ std::set<ObjectFile::Atom*>& fAtoms;
+};
+
+
+
+//
+// LLVM bitcode file reader
+//
+class Reader : public ObjectFile::Reader
+{
+public:
+ static bool validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture);
+ static bool loaded() { return (::lto_get_version() != NULL); }
+ Reader(const uint8_t* fileContent, uint64_t fileLength,
+ const char* path, time_t modTime,
+ const ObjectFile::ReaderOptions&, cpu_type_t arch);
+ virtual ~Reader();
+
+ virtual std::vector<class ObjectFile::Atom*>& getAtoms() { return (std::vector<class ObjectFile::Atom*>&)(fAtoms); }
+ virtual std::vector<class ObjectFile::Atom*>* getJustInTimeAtomsFor(const char* name) { return NULL; }
+ virtual const char* getPath() { return fPath; }
+ virtual time_t getModificationTime() { return fModTime; }
+ virtual ObjectFile::Reader::DebugInfoKind getDebugInfoKind() { return kDebugInfoNone; }
+ virtual std::vector<Stab>* getStabs() { return NULL; }
+ virtual bool optimize(const std::vector<ObjectFile::Atom*>& allAtoms, std::vector<ObjectFile::Atom*>& newAtoms,
+ std::vector<const char*>& additionalUndefines, const std::set<ObjectFile::Atom*>&,
+ std::vector<ObjectFile::Atom*>& newDeadAtoms,
+ uint32_t nextInputOrdinal,
+ ObjectFile::Reader* writer, ObjectFile::Atom* entryPointAtom,
+ const std::vector<const char*>& llvmOptions,
+ bool allGlobalsAReDeadStripRoots,
+ int outputKind, bool verbose, bool saveTemps, const char* outputFilePath,
+ bool pie, bool allowTextRelocs);
+
+private:
+
+ class CStringEquals
+ {
+ public:
+ bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
+ };
+ typedef __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> CStringSet;
+ typedef __gnu_cxx::hash_map<const char*, Atom*, __gnu_cxx::hash<const char*>, CStringEquals> CStringToAtom;
+
+ ObjectFile::Reader* makeMachOReader(const uint8_t* p, size_t len, uint32_t nextInputOrdinal);
+ static const char* tripletPrefixForArch(cpu_type_t);
+
+ cpu_type_t fArchitecture;
+ const char* fPath;
+ time_t fModTime;
+ lto_module_t fModule;
+ std::vector<ObjectFile::Atom*> fAtoms;
+ InternalAtom fInternalAtom;
+ const ObjectFile::ReaderOptions& fReaderOptions;
+ static std::set<Reader*> fgReaders;
+ static bool fgOptimized;
+};
+
+bool Reader::fgOptimized = false;
+std::set<Reader*> Reader::fgReaders;
+
+
+Reader::~Reader()
+{
+ if ( fModule != NULL )
+ ::lto_module_dispose(fModule);
+}
+
+Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime,
+ const ObjectFile::ReaderOptions& options, cpu_type_t arch)
+ : fArchitecture(arch), fPath(strdup(path)), fModTime(modTime), fInternalAtom(*this), fReaderOptions(options)
+{
+ fgReaders.insert(this);
+
+ fModule = ::lto_module_create_from_memory(fileContent, fileLength);
+ if ( fModule == NULL )
+ throwf("could not parse object file %s: %s", path, lto_get_error_message());
+
+ fAtoms.push_back(&fInternalAtom);
+
+ uint32_t count = ::lto_module_get_num_symbols(fModule);
+ for (uint32_t i=0; i < count; ++i) {
+ const char* name = ::lto_module_get_symbol_name(fModule, i);
+ lto_symbol_attributes attr = lto_module_get_symbol_attribute(fModule, i);
+
+ // <rdar://problem/6378110> LTO doesn't like dtrace symbols
+ // ignore dtrace static probes for now
+ // later when codegen is done and a mach-o file is produces the probes will be processed
+ if ( (strncmp(name, "___dtrace_probe$", 16) == 0) || (strncmp(name, "___dtrace_isenabled$", 20) == 0) )
+ continue;
+
+ ObjectFile::Atom::DefinitionKind kind;
+ switch ( attr & LTO_SYMBOL_DEFINITION_MASK ) {
+ case LTO_SYMBOL_DEFINITION_REGULAR:
+ kind = ObjectFile::Atom::kRegularDefinition;
+ break;
+ case LTO_SYMBOL_DEFINITION_TENTATIVE:
+ kind = ObjectFile::Atom::kTentativeDefinition;
+ break;
+ case LTO_SYMBOL_DEFINITION_WEAK:
+ kind = ObjectFile::Atom::kWeakDefinition;
+ break;
+ case LTO_SYMBOL_DEFINITION_UNDEFINED:
+ case LTO_SYMBOL_DEFINITION_WEAKUNDEF:
+ kind = ObjectFile::Atom::kExternalDefinition;
+ break;
+ default:
+ throwf("unknown definition kind for symbol %s in bitcode file %s", name, path);
+ }
+
+ // make LLVM atoms for definitions and a reference for undefines
+ if ( kind != ObjectFile::Atom::kExternalDefinition ) {
+ ObjectFile::Atom::Scope scope;
+ switch ( attr & LTO_SYMBOL_SCOPE_MASK) {
+ case LTO_SYMBOL_SCOPE_INTERNAL:
+ scope = ObjectFile::Atom::scopeTranslationUnit;
+ break;
+ case LTO_SYMBOL_SCOPE_HIDDEN:
+ scope = ObjectFile::Atom::scopeLinkageUnit;
+ break;
+ case LTO_SYMBOL_SCOPE_DEFAULT:
+ scope = ObjectFile::Atom::scopeGlobal;
+ break;
+ default:
+ throwf("unknown scope for symbol %s in bitcode file %s", name, path);
+ }
+ // only make atoms for non-internal symbols
+ if ( scope == ObjectFile::Atom::scopeTranslationUnit )
+ continue;
+ uint8_t alignment = (attr & LTO_SYMBOL_ALIGNMENT_MASK);
+ // make Atom
+ fAtoms.push_back(new Atom(*this, name, scope, kind, alignment, fInternalAtom));
+ }
+ else {
+ // add to list of external references
+ fInternalAtom.addReference(name);
+ }
+ }
+}
+
+const char* Reader::tripletPrefixForArch(cpu_type_t arch)
+{
+ switch (arch) {
+ case CPU_TYPE_POWERPC:
+ return "powerpc-";
+ case CPU_TYPE_POWERPC64:
+ return "powerpc64-";
+ case CPU_TYPE_I386:
+ return "i386-";
+ case CPU_TYPE_X86_64:
+ return "x86_64-";
+ case CPU_TYPE_ARM:
+ return "arm";
+ }
+ return "";
+}
+
+bool Reader::validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture)
+{
+ return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, tripletPrefixForArch(architecture));
+}
+
+bool Reader::optimize(const std::vector<ObjectFile::Atom *>& allAtoms, std::vector<ObjectFile::Atom*>& newAtoms,
+ std::vector<const char*>& additionalUndefines, const std::set<ObjectFile::Atom*>& deadAtoms,
+ std::vector<ObjectFile::Atom*>& newlyDeadAtoms,
+ uint32_t nextInputOrdinal, ObjectFile::Reader* writer, ObjectFile::Atom* entryPointAtom,
+ const std::vector<const char*>& llvmOptions,
+ bool allGlobalsAReDeadStripRoots,
+ int okind, bool verbose, bool saveTemps, const char* outputFilePath,
+ bool pie, bool allowTextRelocs)
+{
+ // this method is call on all Readers. We want the first call to trigger optimization
+ // across all Readers and the subsequent calls to do nothing.
+ if ( fgOptimized )
+ return false;
+ fgOptimized = true;
+
+ Options::OutputKind outputKind = (Options::OutputKind)okind; // HACK to work around upward dependency
+
+ // print out LTO version string if -v was used
+ if ( verbose )
+ fprintf(stderr, "%s\n", lto_get_version());
+
+ // create optimizer and add each Reader
+ lto_code_gen_t generator = ::lto_codegen_create();
+ for (std::set<Reader*>::iterator it=fgReaders.begin(); it != fgReaders.end(); ++it) {
+ if ( ::lto_codegen_add_module(generator, (*it)->fModule) )
+ throwf("lto: could not merge in %s because %s", (*it)->fPath, ::lto_get_error_message());
+ }
+
+ // add any -mllvm command line options
+ for (std::vector<const char*>::const_iterator it=llvmOptions.begin(); it != llvmOptions.end(); ++it) {
+ ::lto_codegen_debug_options(generator, *it);
+ }
+
+ // The atom graph uses directed edges (references). Collect all references where
+ // originating atom is not part of any LTO Reader. This allows optimizer to optimize an
+ // external (i.e. not originated from same .o file) reference if all originating atoms are also
+ // defined in llvm bitcode file.
+ CStringSet nonLLVMRefs;
+ CStringToAtom llvmAtoms;
+ bool hasNonllvmAtoms = false;
+ for (std::vector<ObjectFile::Atom*>::const_iterator it = allAtoms.begin(); it != allAtoms.end(); ++it) {
+ ObjectFile::Atom* atom = *it;
+ // only look at references come from an atom that is not an llvm atom
+ if ( fgReaders.count((Reader*)(atom->getFile())) == 0 ) {
+ // remember if we've seen any atoms not from an llvm reader and not from the writer
+ if ( atom->getFile() != writer )
+ hasNonllvmAtoms = true;
+ std::vector<ObjectFile::Reference*>& refs = atom->getReferences();
+ for (std::vector<ObjectFile::Reference*>::iterator ri=refs.begin(), re=refs.end(); ri != re; ++ri) {
+ ObjectFile::Reference* ref = *ri;
+ // add target name to set if target is an llvm atom
+ if ( (ref->getTargetName() != NULL) && (fgReaders.count((Reader*)(ref->getTarget().getFile())) != 0) ) {
+ nonLLVMRefs.insert(ref->getTargetName());
+ }
+ }
+ }
+ else {
+ const char* name = atom->getName();
+ if ( name != NULL )
+ llvmAtoms[name] = (Atom*)atom;
+ }
+ }
+ // if entry point is in a llvm bitcode file, it must be preserved by LTO
+ if ( entryPointAtom != NULL ) {
+ if ( fgReaders.count((Reader*)(entryPointAtom->getFile())) != 0 )
+ nonLLVMRefs.insert(entryPointAtom->getName());
+ }
+
+ // deadAtoms are the atoms that the linker coalesced. For instance weak or tentative definitions
+ // overriden by another atom. If any of these deadAtoms are llvm atoms and they were replaced
+ // with a mach-o atom, we need to tell the lto engine to preserve (not optimize away) its dead
+ // atom so that the linker can replace it with the mach-o one later.
+ CStringToAtom deadllvmAtoms;
+ for (std::set<ObjectFile::Atom*>::iterator it = deadAtoms.begin(); it != deadAtoms.end(); ++it) {
+ ObjectFile::Atom* atom = *it;
+ if ( fgReaders.count((Reader*)(atom->getFile())) != 0 ) {
+ const char* name = atom->getName();
+ ::lto_codegen_add_must_preserve_symbol(generator, name);
+ deadllvmAtoms[name] = (Atom*)atom;
+ }
+ }
+
+
+ // tell code generator about symbols that must be preserved
+ for (CStringToAtom::iterator it = llvmAtoms.begin(); it != llvmAtoms.end(); ++it) {
+ const char* name = it->first;
+ Atom* atom = it->second;
+ // Include llvm Symbol in export list if it meets one of following two conditions
+ // 1 - atom scope is global (and not linkage unit).
+ // 2 - included in nonLLVMRefs set.
+ // If a symbol is not listed in exportList then LTO is free to optimize it away.
+ if ( (atom->getScope() == ObjectFile::Atom::scopeGlobal) )
+ ::lto_codegen_add_must_preserve_symbol(generator, name);
+ else if ( nonLLVMRefs.find(name) != nonLLVMRefs.end() )
+ ::lto_codegen_add_must_preserve_symbol(generator, name);
+ }
+
+ // special case running ld -r on all bitcode files to produce another bitcode file (instead of mach-o)
+ if ( (outputKind == Options::kObjectFile) && !hasNonllvmAtoms ) {
+ if ( ! ::lto_codegen_write_merged_modules(generator, outputFilePath) ) {
+ // HACK, no good way to tell linker we are all done, so just quit
+ exit(0);
+ }
+ warning("could not produce merged bitcode file");
+ }
+
+ // if requested, save off merged bitcode file
+ if ( saveTemps ) {
+ char tempBitcodePath[MAXPATHLEN];
+ strcpy(tempBitcodePath, outputFilePath);
+ strcat(tempBitcodePath, ".lto.bc");
+ ::lto_codegen_write_merged_modules(generator, tempBitcodePath);
+ }
+
+ // set code-gen model
+ lto_codegen_model model = LTO_CODEGEN_PIC_MODEL_DYNAMIC;
+ switch ( outputKind ) {
+ case Options::kDynamicExecutable:
+ case Options::kPreload:
+ if ( pie )
+ model = LTO_CODEGEN_PIC_MODEL_DYNAMIC;
+ else
+ model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC;
+ break;
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ case Options::kObjectFile: // ?? Is this appropriate ?
+ case Options::kDyld:
+ case Options::kKextBundle:
+ if ( allowTextRelocs )
+ model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC;
+ else
+ model = LTO_CODEGEN_PIC_MODEL_DYNAMIC;
+ break;
+ case Options::kStaticExecutable:
+ model = LTO_CODEGEN_PIC_MODEL_STATIC;
+ break;
+ }
+ if ( ::lto_codegen_set_pic_model(generator, model) )
+ throwf("could not create set codegen model: %s", lto_get_error_message());
+
+ // run code generator
+ size_t machOFileLen;
+ const uint8_t* machOFile = (uint8_t*)::lto_codegen_compile(generator, &machOFileLen);
+ if ( machOFile == NULL )
+ throwf("could not do LTO codegen: %s", ::lto_get_error_message());
+
+ // if requested, save off temp mach-o file
+ if ( saveTemps ) {
+ char tempMachoPath[MAXPATHLEN];
+ strcpy(tempMachoPath, outputFilePath);
+ strcat(tempMachoPath, ".lto.o");
+ int fd = ::open(tempMachoPath, O_CREAT | O_WRONLY | O_TRUNC, 0666);
+ if ( fd != -1) {
+ ::write(fd, machOFile, machOFileLen);
+ ::close(fd);
+ }
+ }
+
+ // parse generated mach-o file into a MachOReader
+ ObjectFile::Reader* machoReader = this->makeMachOReader(machOFile, machOFileLen, nextInputOrdinal);
+
+ // sync generated mach-o atoms with existing atoms ld knows about
+ std::vector<ObjectFile::Atom*> machoAtoms = machoReader->getAtoms();
+ for (std::vector<ObjectFile::Atom *>::iterator it = machoAtoms.begin(); it != machoAtoms.end(); ++it) {
+ ObjectFile::Atom* atom = *it;
+ const char* name = atom->getName();
+ if ( name != NULL ) {
+ CStringToAtom::iterator pos = llvmAtoms.find(name);
+ if ( pos != llvmAtoms.end() ) {
+ // turn Atom into a proxy for this mach-o atom
+ pos->second->setRealAtom(atom);
+ }
+ else {
+ // an atom of this name was not in the allAtoms list the linker gave us
+ if ( deadllvmAtoms.find(name) != deadllvmAtoms.end() ) {
+ // this corresponding to an atom that the linker coalesced away. Ignore it
+ // Make sure there any dependent atoms are also marked dead
+ std::vector<ObjectFile::Reference*>& refs = atom->getReferences();
+ for (std::vector<ObjectFile::Reference*>::iterator ri=refs.begin(), re=refs.end(); ri != re; ++ri) {
+ ObjectFile::Reference* ref = *ri;
+ if ( ref->getKind() == 2 /*kGroupSubordinate*/ ) { // FIX FIX
+ ObjectFile::Atom* targ = &ref->getTarget();
+ deadllvmAtoms[targ->getName()] = (Atom*)atom;
+ }
+ }
+ }
+ else
+ {
+ // this is something new that lto conjured up, tell ld its new
+ newAtoms.push_back(atom);
+ }
+ }
+ }
+ else {
+ // ld only knew about named atoms, so this one must be new
+ newAtoms.push_back(atom);
+ }
+ std::vector<class ObjectFile::Reference*>& references = atom->getReferences();
+ for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); ++rit) {
+ ObjectFile::Reference* ref = *rit;
+ const char* targetName = ref->getTargetName();
+ CStringToAtom::iterator pos;
+ if (targetName != NULL) {
+ switch ( ref->getTargetBinding() ) {
+ case ObjectFile::Reference::kUnboundByName:
+ // accumulate unbounded references so that ld can bound them.
+ additionalUndefines.push_back(targetName);
+ break;
+ case ObjectFile::Reference::kBoundDirectly:
+ case ObjectFile::Reference::kBoundByName:
+ // If mach-o atom is referencing another mach-o atom then
+ // reference is not going through Atom proxy. Fix it here to ensure that all
+ // llvm symbol references always go through Atom proxy.
+ pos = llvmAtoms.find(targetName);
+ if ( pos != llvmAtoms.end() )
+ ref->setTarget(*pos->second, ref->getTargetOffset());
+ break;
+ case ObjectFile::Reference::kDontBind:
+ break;
+ }
+ }
+ }
+ }
+
+ // Remove InternalAtoms from ld
+ for (std::set<Reader*>::iterator it=fgReaders.begin(); it != fgReaders.end(); ++it) {
+ newlyDeadAtoms.push_back(&((*it)->fInternalAtom));
+ }
+ // Remove Atoms from ld if code generator optimized them away
+ for (CStringToAtom::iterator li = llvmAtoms.begin(), le = llvmAtoms.end(); li != le; ++li) {
+ // check if setRealAtom() called on this Atom
+ if ( li->second->getRealAtom() == NULL )
+ newlyDeadAtoms.push_back(li->second);
+ }
+
+ return true;
+}
+
+
+ObjectFile::Reader* Reader::makeMachOReader(const uint8_t* p, size_t len, uint32_t nextInputOrdinal)
+{
+ switch ( fArchitecture ) {
+ case CPU_TYPE_POWERPC:
+ if ( mach_o::relocatable::Reader<ppc>::validFile(p) )
+ return new mach_o::relocatable::Reader<ppc>(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal);
+ break;
+ case CPU_TYPE_POWERPC64:
+ if ( mach_o::relocatable::Reader<ppc64>::validFile(p) )
+ return new mach_o::relocatable::Reader<ppc64>(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal);
+ break;
+ case CPU_TYPE_I386:
+ if ( mach_o::relocatable::Reader<x86>::validFile(p) )
+ return new mach_o::relocatable::Reader<x86>(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal);
+ break;
+ case CPU_TYPE_X86_64:
+ if ( mach_o::relocatable::Reader<x86_64>::validFile(p) )
+ return new mach_o::relocatable::Reader<x86_64>(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal);
+ break;
+ case CPU_TYPE_ARM:
+ if ( mach_o::relocatable::Reader<arm>::validFile(p) )
+ return new mach_o::relocatable::Reader<arm>(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal);
+ break;
+ }
+ throw "LLVM LTO, file is not of required architecture";
+}
+
+}; // namespace lto
+
+extern void printLTOVersion(Options &opts);
+
+void printLTOVersion(Options &opts) {
+ const char* vers = lto_get_version();
+ if ( vers != NULL )
+ fprintf(stderr, "%s\n", vers);
+}
+
+
+#endif
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2005-2007 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifndef __OBJECT_FILE_DYLIB_MACH_O__
+#define __OBJECT_FILE_DYLIB_MACH_O__
+
+#include <stdint.h>
+#include <math.h>
+#include <unistd.h>
+#include <sys/param.h>
+
+
+#include <vector>
+#include <set>
+#include <algorithm>
+#include <ext/hash_map>
+
+#include "MachOFileAbstraction.hpp"
+#include "MachOTrie.hpp"
+#include "ObjectFile.h"
+
+//
+//
+// To implement architecture xxx, you must write template specializations for the following method:
+// Reader<xxx>::validFile()
+//
+//
+
+
+
+
+namespace mach_o {
+namespace dylib {
+
+
+// forward reference
+template <typename A> class Reader;
+
+
+class Segment : public ObjectFile::Segment
+{
+public:
+ Segment(const char* name) { fName = name; }
+ virtual const char* getName() const { return fName; }
+ virtual bool isContentReadable() const { return true; }
+ virtual bool isContentWritable() const { return false; }
+ virtual bool isContentExecutable() const { return false; }
+private:
+ const char* fName;
+};
+
+
+//
+// An ExportAtom has no content. It exists so that the linker can track which imported
+// symbols came from which dynamic libraries.
+//
+template <typename A>
+class ExportAtom : public ObjectFile::Atom
+{
+public:
+ virtual ObjectFile::Reader* getFile() const { return &fOwner; }
+ virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; }
+ virtual const char* getName() const { return fName; }
+ virtual const char* getDisplayName() const { return fName; }
+ virtual Scope getScope() const { return ObjectFile::Atom::scopeGlobal; }
+ virtual DefinitionKind getDefinitionKind() const { return fWeakDefinition ? kExternalWeakDefinition : kExternalDefinition; }
+ virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableIn; }
+ virtual bool dontDeadStrip() const { return false; }
+ virtual bool isZeroFill() const { return false; }
+ virtual bool isThumb() const { return false; }
+ virtual uint64_t getSize() const { return 0; }
+ virtual std::vector<ObjectFile::Reference*>& getReferences() const { return fgEmptyReferenceList; }
+ virtual bool mustRemainInSection() const { return false; }
+ virtual const char* getSectionName() const { return "._imports"; }
+ virtual Segment& getSegment() const { return fgImportSegment; }
+ virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); }
+ virtual uint32_t getOrdinal() const { return fOrdinal; }
+ virtual std::vector<ObjectFile::LineInfo>* getLineInfo() const { return NULL; }
+ virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); }
+ virtual void copyRawContent(uint8_t buffer[]) const {}
+
+ virtual void setScope(Scope) { }
+
+protected:
+ friend class Reader<A>;
+ typedef typename A::P P;
+
+ ExportAtom(ObjectFile::Reader& owner, const char* name, bool weak, uint32_t ordinal)
+ : fOwner(owner), fName(name), fOrdinal(ordinal), fWeakDefinition(weak) {}
+ virtual ~ExportAtom() {}
+
+ ObjectFile::Reader& fOwner;
+ const char* fName;
+ uint32_t fOrdinal;
+ bool fWeakDefinition;
+
+ static std::vector<ObjectFile::Reference*> fgEmptyReferenceList;
+ static Segment fgImportSegment;
+};
+
+template <typename A>
+Segment ExportAtom<A>::fgImportSegment("__LINKEDIT");
+
+template <typename A>
+std::vector<ObjectFile::Reference*> ExportAtom<A>::fgEmptyReferenceList;
+
+
+
+class ImportReference : public ObjectFile::Reference
+{
+public:
+ ImportReference(const char* name)
+ : fTarget(NULL), fTargetName(strdup(name)) {}
+ virtual ~ImportReference() {}
+
+
+ virtual ObjectFile::Reference::TargetBinding getTargetBinding() const { return (fTarget==NULL) ? ObjectFile::Reference::kUnboundByName : ObjectFile::Reference::kBoundByName; }
+ virtual ObjectFile::Reference::TargetBinding getFromTargetBinding() const{ return ObjectFile::Reference::kDontBind; }
+ virtual uint8_t getKind() const { return 0; }
+ virtual uint64_t getFixUpOffset() const { return 0; }
+ virtual const char* getTargetName() const { return fTargetName; }
+ virtual ObjectFile::Atom& getTarget() const { return *((ObjectFile::Atom*)fTarget); }
+ virtual uint64_t getTargetOffset() const { return 0; }
+ virtual ObjectFile::Atom& getFromTarget() const { return *((ObjectFile::Atom*)NULL); }
+ virtual const char* getFromTargetName() const { return NULL; }
+ virtual uint64_t getFromTargetOffset() const { return 0; }
+ virtual void setTarget(ObjectFile::Atom& atom, uint64_t offset) { fTarget = &atom; }
+ virtual void setFromTarget(ObjectFile::Atom&) { throw "can't set from target"; }
+ virtual const char* getDescription() const { return "dylib import reference"; }
+
+private:
+ const ObjectFile::Atom* fTarget;
+ const char* fTargetName;
+};
+
+
+//
+// An ImportAtom has no content. It exists so that when linking a main executable flat-namespace
+// the imports of all flat dylibs are checked
+//
+template <typename A>
+class ImportAtom : public ObjectFile::Atom
+{
+public:
+ virtual ObjectFile::Reader* getFile() const { return &fOwner; }
+ virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; }
+ virtual const char* getName() const { return "flat-imports"; }
+ virtual const char* getDisplayName() const { return "flat_namespace undefines"; }
+ 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 false; }
+ virtual bool isZeroFill() const { return false; }
+ virtual bool isThumb() const { return false; }
+ virtual uint64_t getSize() const { return 0; }
+ virtual std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(fReferences); }
+ virtual bool mustRemainInSection() const { return false; }
+ virtual const char* getSectionName() const { return "._imports"; }
+ virtual Segment& getSegment() const { return fgImportSegment; }
+ virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); }
+ virtual uint32_t getOrdinal() const { return fOrdinal; }
+ virtual std::vector<ObjectFile::LineInfo>* getLineInfo() const { return NULL; }
+ virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); }
+ virtual void copyRawContent(uint8_t buffer[]) const {}
+
+ virtual void setScope(Scope) { }
+
+protected:
+ friend class Reader<A>;
+ typedef typename A::P P;
+
+ ImportAtom(ObjectFile::Reader& owner, uint32_t ordinal, std::vector<const char*>& imports)
+ : fOwner(owner), fOrdinal(ordinal) { makeReferences(imports); }
+ virtual ~ImportAtom() {}
+ void makeReferences(std::vector<const char*>& imports) {
+ for (std::vector<const char*>::iterator it=imports.begin(); it != imports.end(); ++it) {
+ fReferences.push_back(new ImportReference(*it));
+ }
+ }
+
+
+ ObjectFile::Reader& fOwner;
+ uint32_t fOrdinal;
+ std::vector<ObjectFile::Reference*> fReferences;
+
+ static Segment fgImportSegment;
+};
+
+template <typename A>
+Segment ImportAtom<A>::fgImportSegment("__LINKEDIT");
+
+
+
+
+//
+// The reader for a dylib extracts all exported symbols names from the memory-mapped
+// dylib, builds a hash table, then unmaps the file. This is an important memory
+// savings for large dylibs.
+//
+template <typename A>
+class Reader : public ObjectFile::Reader
+{
+public:
+ static bool validFile(const uint8_t* fileContent, bool executableOrDylib);
+ Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path,
+ const LibraryOptions& dylibOptions, const ObjectFile::ReaderOptions& options,
+ uint32_t ordinalBase);
+ virtual ~Reader() {}
+
+ virtual const char* getPath() { return fPath; }
+ virtual time_t getModificationTime() { return 0; }
+ virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; }
+ virtual std::vector<class ObjectFile::Atom*>& getAtoms();
+ virtual std::vector<class ObjectFile::Atom*>* getJustInTimeAtomsFor(const char* name);
+ virtual std::vector<Stab>* getStabs() { return NULL; }
+ virtual ObjectFile::Reader::ObjcConstraint getObjCConstraint() { return fObjcContraint; }
+ virtual const char* getInstallPath() { return fDylibInstallPath; }
+ virtual uint32_t getTimestamp() { return fDylibTimeStamp; }
+ virtual uint32_t getCurrentVersion() { return fDylibtCurrentVersion; }
+ virtual uint32_t getCompatibilityVersion() { return fDylibCompatibilityVersion; }
+ virtual void processIndirectLibraries(DylibHander* handler);
+ virtual void setExplicitlyLinked() { fExplicitlyLinked = true; }
+ virtual bool explicitlyLinked() { return fExplicitlyLinked; }
+ virtual bool implicitlyLinked() { return fImplicitlyLinked; }
+ virtual bool providedExportAtom() { return fProvidedAtom; }
+ virtual const char* parentUmbrella() { return fParentUmbrella; }
+ virtual std::vector<const char*>* getAllowableClients();
+ virtual bool hasWeakExternals() { return fHasWeakExports; }
+ virtual bool deadStrippable() { return fDeadStrippable; }
+ virtual bool isLazyLoadedDylib() { return fLazyLoaded; }
+
+ virtual void setImplicitlyLinked() { fImplicitlyLinked = true; }
+
+protected:
+
+ struct ReExportChain { ReExportChain* prev; Reader<A>* reader; };
+
+ void assertNoReExportCycles(ReExportChain*);
+
+private:
+ typedef typename A::P P;
+ typedef typename A::P::E E;
+
+ class CStringEquals
+ {
+ public:
+ bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
+ };
+ struct AtomAndWeak { ObjectFile::Atom* atom; bool weak; uint32_t ordinal; };
+ typedef __gnu_cxx::hash_map<const char*, AtomAndWeak, __gnu_cxx::hash<const char*>, CStringEquals> NameToAtomMap;
+ typedef __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> NameSet;
+ typedef typename NameToAtomMap::iterator NameToAtomMapIterator;
+
+ struct PathAndFlag { const char* path; bool reExport; };
+
+ bool isPublicLocation(const char* path);
+ void addSymbol(const char* name, bool weak);
+ void addDyldFastStub();
+ void buildExportHashTableFromExportInfo(const macho_dyld_info_command<P>* dyldInfo,
+ const uint8_t* fileContent);
+ void buildExportHashTableFromSymbolTable(const macho_dysymtab_command<P>* dynamicInfo,
+ const macho_nlist<P>* symbolTable, const char* strings,
+ const uint8_t* fileContent);
+
+ const char* fPath;
+ const char* fParentUmbrella;
+ std::vector<const char*> fAllowableClients;
+ const char* fDylibInstallPath;
+ uint32_t fDylibTimeStamp;
+ uint32_t fDylibtCurrentVersion;
+ uint32_t fDylibCompatibilityVersion;
+ uint32_t fReExportedOrdinal;
+ std::vector<PathAndFlag> fDependentLibraryPaths;
+ NameToAtomMap fAtoms;
+ NameSet fIgnoreExports;
+ bool fNoRexports;
+ bool fHasWeakExports;
+ bool fDeadStrippable;
+ const bool fLinkingFlat;
+ const bool fLinkingMainExecutable;
+ bool fExplictReExportFound;
+ bool fExplicitlyLinked;
+ bool fImplicitlyLinked;
+ bool fProvidedAtom;
+ bool fImplicitlyLinkPublicDylibs;
+ bool fLazyLoaded;
+ ObjectFile::Reader::ObjcConstraint fObjcContraint;
+ std::vector<ObjectFile::Reader*> fReExportedChildren;
+ const ObjectFile::ReaderOptions::MacVersionMin fDeploymentVersionMin;
+ std::vector<class ObjectFile::Atom*> fFlatImports;
+
+ static bool fgLogHashtable;
+ static std::vector<class ObjectFile::Atom*> fgEmptyAtomList;
+};
+
+template <typename A>
+std::vector<class ObjectFile::Atom*> Reader<A>::fgEmptyAtomList;
+template <typename A>
+bool Reader<A>::fgLogHashtable = false;
+
+
+template <typename A>
+Reader<A>::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path,
+ const LibraryOptions& dylibOptions,
+ const ObjectFile::ReaderOptions& options, uint32_t ordinalBase)
+ : fParentUmbrella(NULL), fDylibInstallPath(NULL), fDylibTimeStamp(0), fDylibtCurrentVersion(0),
+ fDylibCompatibilityVersion(0), fReExportedOrdinal(ordinalBase), fLinkingFlat(options.fFlatNamespace),
+ fLinkingMainExecutable(options.fLinkingMainExecutable), fExplictReExportFound(false),
+ fExplicitlyLinked(false), fImplicitlyLinked(false), fProvidedAtom(false),
+ fImplicitlyLinkPublicDylibs(options.fImplicitlyLinkPublicDylibs), fLazyLoaded(dylibOptions.fLazyLoad),
+ fObjcContraint(ObjectFile::Reader::kObjcNone),
+ fDeploymentVersionMin(options.fMacVersionMin)
+{
+ // sanity check
+ if ( ! validFile(fileContent, dylibOptions.fBundleLoader) )
+ throw "not a valid mach-o object file";
+
+ fPath = strdup(path);
+
+ const macho_header<P>* header = (const macho_header<P>*)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>* const cmdsEnd = (macho_load_command<P>*)((char*)header + sizeof(macho_header<P>) + header->sizeofcmds());
+
+ // write out path for -whatsloaded option
+ if ( options.fLogAllFiles )
+ printf("%s\n", path);
+
+ if ( options.fRootSafe && ((header->flags() & MH_ROOT_SAFE) == 0) )
+ warning("using -root_safe but linking against %s which is not root safe", path);
+
+ if ( options.fSetuidSafe && ((header->flags() & MH_SETUID_SAFE) == 0) )
+ warning("using -setuid_safe but linking against %s which is not setuid safe", path);
+
+ // a "blank" stub has zero load commands
+ if ( (header->filetype() == MH_DYLIB_STUB) && (cmd_count == 0) ) {
+ // no further processing needed
+ munmap((caddr_t)fileContent, fileLength);
+ return;
+ }
+
+
+ // optimize the case where we know there is no reason to look at indirect dylibs
+ fNoRexports = (header->flags() & MH_NO_REEXPORTED_DYLIBS);
+ fHasWeakExports = (header->flags() & MH_WEAK_DEFINES);
+ fDeadStrippable = (header->flags() & MH_DEAD_STRIPPABLE_DYLIB);
+ bool trackDependentLibraries = !fNoRexports || options.fFlatNamespace;
+
+ // pass 1 builds list of all dependent libraries
+ const macho_load_command<P>* cmd = cmds;
+ if ( trackDependentLibraries ) {
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ switch (cmd->cmd()) {
+ case LC_REEXPORT_DYLIB:
+ fExplictReExportFound = true;
+ // fall into next case
+ case LC_LOAD_DYLIB:
+ case LC_LOAD_WEAK_DYLIB:
+ PathAndFlag entry;
+ entry.path = strdup(((struct macho_dylib_command<P>*)cmd)->name());
+ entry.reExport = (cmd->cmd() == LC_REEXPORT_DYLIB);
+ fDependentLibraryPaths.push_back(entry);
+ break;
+ }
+ cmd = (const macho_load_command<P>*)(((char*)cmd)+cmd->cmdsize());
+ if ( cmd > cmdsEnd )
+ throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, path);
+ }
+ }
+
+ // pass 2 determines re-export info
+ const macho_dysymtab_command<P>* dynamicInfo = NULL;
+ const macho_dyld_info_command<P>* dyldInfo = NULL;
+ const macho_nlist<P>* symbolTable = NULL;
+ const char* strings = NULL;
+ cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ switch (cmd->cmd()) {
+ case LC_SYMTAB:
+ {
+ const macho_symtab_command<P>* symtab = (macho_symtab_command<P>*)cmd;
+ symbolTable = (const macho_nlist<P>*)((char*)header + symtab->symoff());
+ strings = (char*)header + symtab->stroff();
+ }
+ break;
+ case LC_DYSYMTAB:
+ dynamicInfo = (macho_dysymtab_command<P>*)cmd;
+ break;
+ case LC_DYLD_INFO:
+ case LC_DYLD_INFO_ONLY:
+ dyldInfo = (macho_dyld_info_command<P>*)cmd;
+ break;
+ case LC_ID_DYLIB:
+ {
+ macho_dylib_command<P>* dylibID = (macho_dylib_command<P>*)cmd;
+ fDylibInstallPath = strdup(dylibID->name());
+ fDylibTimeStamp = dylibID->timestamp();
+ fDylibtCurrentVersion = dylibID->current_version();
+ fDylibCompatibilityVersion = dylibID->compatibility_version();
+ }
+ break;
+ case LC_SUB_UMBRELLA:
+ if ( trackDependentLibraries ) {
+ const char* frameworkLeafName = ((macho_sub_umbrella_command<P>*)cmd)->sub_umbrella();
+ for (typename std::vector<PathAndFlag>::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) {
+ const char* dylibName = it->path;
+ const char* lastSlash = strrchr(dylibName, '/');
+ if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], frameworkLeafName) == 0) )
+ it->reExport = true;
+ }
+ }
+ break;
+ case LC_SUB_LIBRARY:
+ if ( trackDependentLibraries) {
+ const char* dylibBaseName = ((macho_sub_library_command<P>*)cmd)->sub_library();
+ for (typename std::vector<PathAndFlag>::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) {
+ const char* dylibName = it->path;
+ const char* lastSlash = strrchr(dylibName, '/');
+ const char* leafStart = &lastSlash[1];
+ if ( lastSlash == NULL )
+ leafStart = dylibName;
+ const char* firstDot = strchr(leafStart, '.');
+ int len = strlen(leafStart);
+ if ( firstDot != NULL )
+ len = firstDot - leafStart;
+ if ( strncmp(leafStart, dylibBaseName, len) == 0 )
+ it->reExport = true;
+ }
+ }
+ break;
+ case LC_SUB_FRAMEWORK:
+ fParentUmbrella = strdup(((macho_sub_framework_command<P>*)cmd)->umbrella());
+ break;
+ case macho_segment_command<P>::CMD:
+ // check for Objective-C info
+ if ( strcmp(((macho_segment_command<P>*)cmd)->segname(), "__OBJC") == 0 ) {
+ const macho_segment_command<P>* segment = (macho_segment_command<P>*)cmd;
+ const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segment + sizeof(macho_segment_command<P>));
+ const macho_section<P>* const sectionsEnd = §ionsStart[segment->nsects()];
+ for (const macho_section<P>* sect=sectionsStart; sect < sectionsEnd; ++sect) {
+ if ( strcmp(sect->sectname(), "__image_info") == 0 ) {
+ // struct objc_image_info {
+ // uint32_t version; // initially 0
+ // uint32_t flags;
+ // };
+ // #define OBJC_IMAGE_SUPPORTS_GC 2
+ // #define OBJC_IMAGE_GC_ONLY 4
+ //
+ const uint32_t* contents = (uint32_t*)(&fileContent[sect->offset()]);
+ if ( (sect->size() >= 8) && (contents[0] == 0) ) {
+ uint32_t flags = E::get32(contents[1]);
+ if ( (flags & 4) == 4 )
+ fObjcContraint = ObjectFile::Reader::kObjcGC;
+ else if ( (flags & 2) == 2 )
+ fObjcContraint = ObjectFile::Reader::kObjcRetainReleaseOrGC;
+ else
+ fObjcContraint = ObjectFile::Reader::kObjcRetainRelease;
+ }
+ else if ( sect->size() > 0 ) {
+ warning("can't parse __OBJC/__image_info section in %s", fPath);
+ }
+ }
+ }
+ }
+ }
+
+ cmd = (const macho_load_command<P>*)(((char*)cmd)+cmd->cmdsize());
+ if ( cmd > cmdsEnd )
+ throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, path);
+ }
+
+ // Process the rest of the commands here.
+ cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ switch (cmd->cmd()) {
+ case LC_SUB_CLIENT:
+ const char *temp = strdup(((macho_sub_client_command<P>*)cmd)->client());
+ fAllowableClients.push_back(temp);
+ break;
+ }
+ cmd = (const macho_load_command<P>*)(((char*)cmd)+cmd->cmdsize());
+ }
+
+ // validate minimal load commands
+ if ( (fDylibInstallPath == NULL) && ((header->filetype() == MH_DYLIB) || (header->filetype() == MH_DYLIB_STUB)) )
+ throwf("dylib %s missing LC_ID_DYLIB load command", path);
+ if ( symbolTable == NULL )
+ throw "binary missing LC_SYMTAB load command";
+ if ( dynamicInfo == NULL )
+ throw "binary missing LC_DYSYMTAB load command";
+
+ // if linking flat and this is a flat dylib, create one atom that references all imported symbols
+ if ( fLinkingFlat && fLinkingMainExecutable && ((header->flags() & MH_TWOLEVEL) == 0) ) {
+ std::vector<const char*> importNames;
+ importNames.reserve(dynamicInfo->nundefsym());
+ const macho_nlist<P>* start = &symbolTable[dynamicInfo->iundefsym()];
+ const macho_nlist<P>* end = &start[dynamicInfo->nundefsym()];
+ for (const macho_nlist<P>* sym=start; sym < end; ++sym) {
+ importNames.push_back(&strings[sym->n_strx()]);
+ }
+ fFlatImports.push_back(new ImportAtom<A>(*this, fReExportedOrdinal++, importNames));
+ }
+
+ // build hash table
+ if ( dyldInfo != NULL )
+ buildExportHashTableFromExportInfo(dyldInfo, fileContent);
+ else
+ buildExportHashTableFromSymbolTable(dynamicInfo, symbolTable, strings, fileContent);
+
+ // special case libSystem
+ if ( (fDylibInstallPath != NULL) && (strcmp(fDylibInstallPath, "/usr/lib/libSystem.B.dylib") == 0) )
+ addDyldFastStub();
+
+ // unmap file
+ munmap((caddr_t)fileContent, fileLength);
+}
+
+
+template <typename A>
+void Reader<A>::buildExportHashTableFromSymbolTable(const macho_dysymtab_command<P>* dynamicInfo,
+ const macho_nlist<P>* symbolTable, const char* strings,
+ const uint8_t* fileContent)
+{
+ if ( dynamicInfo->tocoff() == 0 ) {
+ if ( fgLogHashtable ) fprintf(stderr, "ld: building hashtable of %u toc entries for %s\n", dynamicInfo->nextdefsym(), this->getPath());
+ const macho_nlist<P>* start = &symbolTable[dynamicInfo->iextdefsym()];
+ const macho_nlist<P>* end = &start[dynamicInfo->nextdefsym()];
+ fAtoms.resize(dynamicInfo->nextdefsym()); // set initial bucket count
+ for (const macho_nlist<P>* sym=start; sym < end; ++sym) {
+ this->addSymbol(&strings[sym->n_strx()], (sym->n_desc() & N_WEAK_DEF) != 0);
+ }
+ }
+ else {
+ int32_t count = dynamicInfo->ntoc();
+ fAtoms.resize(count); // set initial bucket count
+ if ( fgLogHashtable ) fprintf(stderr, "ld: building hashtable of %u entries for %s\n", count, this->getPath());
+ const struct dylib_table_of_contents* toc = (dylib_table_of_contents*)(fileContent + dynamicInfo->tocoff());
+ for (int32_t i = 0; i < count; ++i) {
+ const uint32_t index = E::get32(toc[i].symbol_index);
+ const macho_nlist<P>* sym = &symbolTable[index];
+ this->addSymbol(&strings[sym->n_strx()], (sym->n_desc() & N_WEAK_DEF) != 0);
+ }
+ }
+}
+
+
+template <typename A>
+void Reader<A>::buildExportHashTableFromExportInfo(const macho_dyld_info_command<P>* dyldInfo,
+ const uint8_t* fileContent)
+{
+ if ( fgLogHashtable ) fprintf(stderr, "ld: building hashtable from export info in %s\n", this->getPath());
+ if ( dyldInfo->export_size() > 0 ) {
+ const uint8_t* start = fileContent + dyldInfo->export_off();
+ const uint8_t* end = &start[dyldInfo->export_size()];
+ std::vector<mach_o::trie::Entry> list;
+ parseTrie(start, end, list);
+ for (std::vector<mach_o::trie::Entry>::iterator it=list.begin(); it != list.end(); ++it)
+ this->addSymbol(it->name, it->flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION);
+ }
+}
+
+
+template <>
+void Reader<x86_64>::addDyldFastStub()
+{
+ addSymbol("dyld_stub_binder", false);
+}
+
+template <>
+void Reader<x86>::addDyldFastStub()
+{
+ addSymbol("dyld_stub_binder", false);
+}
+
+template <typename A>
+void Reader<A>::addDyldFastStub()
+{
+ // do nothing
+}
+
+template <typename A>
+void Reader<A>::addSymbol(const char* name, bool weakDef)
+{
+ //fprintf(stderr, "addSymbol() %s\n", name);
+ // symbols that start with $ld$ are meta-data to the static linker
+ // <rdar://problem/5182537> need way for ld and dyld to see different exported symbols in a dylib
+ if ( strncmp(name, "$ld$", 4) == 0 ) {
+ // $ld$ <action> $ <condition> $ <symbol-name>
+ const char* symAction = &name[4];
+ const char* symCond = strchr(symAction, '$');
+ if ( symCond != NULL ) {
+ ObjectFile::ReaderOptions::MacVersionMin symVersionCondition = ObjectFile::ReaderOptions::kMinMacVersionUnset;
+ if ( (strncmp(symCond, "$os10.", 6) == 0) && isdigit(symCond[6]) && (symCond[7] == '$') ) {
+ switch ( symCond[6] - '0' ) {
+ case 0:
+ case 1:
+ symVersionCondition = ObjectFile::ReaderOptions::k10_1;
+ break;
+ case 2:
+ symVersionCondition = ObjectFile::ReaderOptions::k10_2;
+ break;
+ case 3:
+ symVersionCondition = ObjectFile::ReaderOptions::k10_3;
+ break;
+ case 4:
+ symVersionCondition = ObjectFile::ReaderOptions::k10_4;
+ break;
+ case 5:
+ symVersionCondition = ObjectFile::ReaderOptions::k10_5;
+ break;
+ case 6:
+ symVersionCondition = ObjectFile::ReaderOptions::k10_6;
+ break;
+ }
+ const char* symName = strchr(&symCond[1], '$');
+ if ( symName != NULL ) {
+ ++symName;
+ if ( fDeploymentVersionMin == symVersionCondition ) {
+ if ( strncmp(symAction, "hide$", 5) == 0 ) {
+ if ( fgLogHashtable ) fprintf(stderr, " adding %s to ignore set for %s\n", symName, this->getPath());
+ fIgnoreExports.insert(strdup(symName));
+ return;
+ }
+ else if ( strncmp(symAction, "add$", 4) == 0 ) {
+ this->addSymbol(symName, weakDef);
+ return;
+ }
+ else {
+ warning("bad symbol action: %s in dylib %s", name, this->getPath());
+ }
+ }
+ }
+ else {
+ warning("bad symbol name: %s in dylib %s", name, this->getPath());
+ }
+ }
+ else {
+ warning("bad symbol version: %s in dylib %s", name, this->getPath());
+ }
+ }
+ else {
+ warning("bad symbol condition: %s in dylib %s", name, this->getPath());
+ }
+ }
+
+ // add symbol as possible export if we are not supposed to ignore it
+ if ( fIgnoreExports.count(name) == 0 ) {
+ AtomAndWeak bucket;
+ bucket.atom = NULL;
+ bucket.weak = weakDef;
+ bucket.ordinal = fReExportedOrdinal++;
+ if ( fgLogHashtable ) fprintf(stderr, " adding %s to hash table for %s\n", name, this->getPath());
+ fAtoms[strdup(name)] = bucket;
+ }
+}
+
+
+template <typename A>
+std::vector<class ObjectFile::Atom*>& Reader<A>::getAtoms()
+{
+ return fFlatImports;
+}
+
+
+template <typename A>
+std::vector<class ObjectFile::Atom*>* Reader<A>::getJustInTimeAtomsFor(const char* name)
+{
+ // if supposed to ignore this export, then pretend I don't have it
+ if ( fIgnoreExports.count(name) != 0 )
+ return NULL;
+
+ std::vector<class ObjectFile::Atom*>* atoms = NULL;
+ NameToAtomMapIterator pos = fAtoms.find(name);
+ if ( pos != fAtoms.end() ) {
+ if ( pos->second.atom == NULL ) {
+ // instantiate atom and update hash table
+ pos->second.atom = new ExportAtom<A>(*this, name, pos->second.weak, pos->second.ordinal);
+ fProvidedAtom = true;
+ if ( fgLogHashtable ) fprintf(stderr, "getJustInTimeAtomsFor: %s found in %s\n", name, this->getPath());
+ }
+ // return a vector of one atom
+ atoms = new std::vector<class ObjectFile::Atom*>;
+ atoms->push_back(pos->second.atom);
+ }
+ else {
+ if ( fgLogHashtable ) fprintf(stderr, "getJustInTimeAtomsFor: %s NOT found in %s\n", name, this->getPath());
+ // look in children that I re-export
+ for (std::vector<ObjectFile::Reader*>::iterator it = fReExportedChildren.begin(); it != fReExportedChildren.end(); it++) {
+ //fprintf(stderr, "getJustInTimeAtomsFor: %s NOT found in %s, looking in child %s\n", name, this->getPath(), (*it)->getInstallPath());
+ std::vector<class ObjectFile::Atom*>* childAtoms = (*it)->getJustInTimeAtomsFor(name);
+ if ( childAtoms != NULL ) {
+ // make a new atom that says this reader is the owner
+ bool isWeakDef = (childAtoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition);
+ // return a vector of one atom
+ ExportAtom<A>* newAtom = new ExportAtom<A>(*this, name, isWeakDef, fReExportedOrdinal++);
+ fProvidedAtom = true;
+ atoms = new std::vector<class ObjectFile::Atom*>;
+ atoms->push_back(newAtom);
+ delete childAtoms;
+ return atoms;
+ }
+ }
+ }
+ return atoms;
+}
+
+
+
+template <typename A>
+bool Reader<A>::isPublicLocation(const char* path)
+{
+ // -no_implicit_dylibs disables this optimization
+ if ( ! fImplicitlyLinkPublicDylibs )
+ return false;
+
+ // /usr/lib is a public location
+ if ( (strncmp(path, "/usr/lib/", 9) == 0) && (strchr(&path[9], '/') == NULL) )
+ return true;
+
+ // /System/Library/Frameworks/ is a public location
+ if ( strncmp(path, "/System/Library/Frameworks/", 27) == 0 ) {
+ const char* frameworkDot = strchr(&path[27], '.');
+ // but only top level framework
+ // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true
+ // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false
+ // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar ==> false
+ // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo ==> false
+ if ( frameworkDot != NULL ) {
+ int frameworkNameLen = frameworkDot - &path[27];
+ if ( strncmp(&path[strlen(path)-frameworkNameLen-1], &path[26], frameworkNameLen+1) == 0 )
+ return true;
+ }
+ }
+
+ return false;
+}
+
+template <typename A>
+void Reader<A>::processIndirectLibraries(DylibHander* handler)
+{
+ if ( fLinkingFlat ) {
+ for (typename std::vector<PathAndFlag>::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) {
+ handler->findDylib(it->path, this->getPath());
+ }
+ }
+ else if ( fNoRexports ) {
+ // MH_NO_REEXPORTED_DYLIBS bit set, then nothing to do
+ }
+ else {
+ // two-level, might have re-exports
+ for (typename std::vector<PathAndFlag>::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) {
+ if ( it->reExport ) {
+ //fprintf(stderr, "processIndirectLibraries() parent=%s, child=%s\n", this->getInstallPath(), it->path);
+ // a LC_REEXPORT_DYLIB, LC_SUB_UMBRELLA or LC_SUB_LIBRARY says we re-export this child
+ ObjectFile::Reader* child = handler->findDylib(it->path, this->getPath());
+ if ( isPublicLocation(child->getInstallPath()) ) {
+ // promote this child to be automatically added as a direct dependent if this already is
+ if ( this->explicitlyLinked() || this->implicitlyLinked() ) {
+ //fprintf(stderr, "processIndirectLibraries() implicitly linking %s\n", child->getInstallPath());
+ ((Reader<A>*)child)->setImplicitlyLinked();
+ }
+ else if ( child->explicitlyLinked() || child->implicitlyLinked() ) {
+ //fprintf(stderr, "processIndirectLibraries() parent is not directly linked, but child is, so no need to re-export child\n");
+ }
+ else {
+ fReExportedChildren.push_back(child);
+ //fprintf(stderr, "processIndirectLibraries() parent is not directly linked, so parent=%s will re-export child=%s\n", this->getInstallPath(), it->path);
+ }
+ }
+ else {
+ // add all child's symbols to me
+ fReExportedChildren.push_back(child);
+ //fprintf(stderr, "processIndirectLibraries() child is not public, so parent=%s will re-export child=%s\n", this->getInstallPath(), it->path);
+ }
+ }
+ else if ( !fExplictReExportFound ) {
+ // see if child contains LC_SUB_FRAMEWORK with my name
+ ObjectFile::Reader* child = handler->findDylib(it->path, this->getPath());
+ const char* parentUmbrellaName = ((Reader<A>*)child)->parentUmbrella();
+ if ( parentUmbrellaName != NULL ) {
+ const char* parentName = this->getPath();
+ const char* lastSlash = strrchr(parentName, '/');
+ if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], parentUmbrellaName) == 0) ) {
+ // add all child's symbols to me
+ fReExportedChildren.push_back(child);
+ //fprintf(stderr, "processIndirectLibraries() umbrella=%s will re-export child=%s\n", this->getInstallPath(), it->path);
+ }
+ }
+ }
+ }
+ }
+
+ // check for re-export cycles
+ ReExportChain chain;
+ chain.prev = NULL;
+ chain.reader = this;
+ this->assertNoReExportCycles(&chain);
+}
+
+template <typename A>
+void Reader<A>::assertNoReExportCycles(ReExportChain* prev)
+{
+ // recursively check my re-exported dylibs
+ ReExportChain chain;
+ chain.prev = prev;
+ chain.reader = this;
+ for (std::vector<ObjectFile::Reader*>::iterator it = fReExportedChildren.begin(); it != fReExportedChildren.end(); it++) {
+ ObjectFile::Reader* child = *it;
+ // check child is not already in chain
+ for (ReExportChain* p = prev; p != NULL; p = p->prev) {
+ if ( p->reader == child ) {
+ throwf("cycle in dylib re-exports with %s", child->getPath());
+ }
+ }
+ ((Reader<A>*)(*it))->assertNoReExportCycles(&chain);
+ }
+}
+
+
+template <typename A>
+std::vector<const char*>* Reader<A>::getAllowableClients()
+{
+ std::vector<const char*>* result = new std::vector<const char*>;
+ for (typename std::vector<const char*>::iterator it = fAllowableClients.begin();
+ it != fAllowableClients.end();
+ it++) {
+ result->push_back(*it);
+ }
+ return (fAllowableClients.size() != 0 ? result : NULL);
+}
+
+template <>
+bool Reader<ppc>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle)
+{
+ 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;
+ switch ( header->filetype() ) {
+ case MH_DYLIB:
+ case MH_DYLIB_STUB:
+ return true;
+ case MH_BUNDLE:
+ if ( executableOrDyliborBundle )
+ return true;
+ else
+ throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)";
+ case MH_EXECUTE:
+ if ( executableOrDyliborBundle )
+ return true;
+ else
+ throw "can't link with a main executable";
+ default:
+ return false;
+ }
+}
+
+template <>
+bool Reader<ppc64>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle)
+{
+ 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;
+ switch ( header->filetype() ) {
+ case MH_DYLIB:
+ case MH_DYLIB_STUB:
+ return true;
+ case MH_BUNDLE:
+ if ( executableOrDyliborBundle )
+ return true;
+ else
+ throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)";
+ case MH_EXECUTE:
+ if ( executableOrDyliborBundle )
+ return true;
+ else
+ throw "can't link with a main executable";
+ default:
+ return false;
+ }
+}
+
+template <>
+bool Reader<x86>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle)
+{
+ 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;
+ switch ( header->filetype() ) {
+ case MH_DYLIB:
+ case MH_DYLIB_STUB:
+ return true;
+ case MH_BUNDLE:
+ if ( executableOrDyliborBundle )
+ return true;
+ else
+ throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)";
+ case MH_EXECUTE:
+ if ( executableOrDyliborBundle )
+ return true;
+ else
+ throw "can't link with a main executable";
+ default:
+ return false;
+ }
+}
+
+template <>
+bool Reader<x86_64>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle)
+{
+ 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_BUNDLE:
+ if ( executableOrDyliborBundle )
+ return true;
+ else
+ throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)";
+ case MH_EXECUTE:
+ if ( executableOrDyliborBundle )
+ return true;
+ else
+ throw "can't link with a main executable";
+ default:
+ return false;
+ }
+}
+
+template <>
+bool Reader<arm>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle)
+{
+ const macho_header<P>* header = (const macho_header<P>*)fileContent;
+ if ( header->magic() != MH_MAGIC )
+ return false;
+ if ( header->cputype() != CPU_TYPE_ARM )
+ return false;
+ switch ( header->filetype() ) {
+ case MH_DYLIB:
+ case MH_DYLIB_STUB:
+ return true;
+ case MH_BUNDLE:
+ if ( executableOrDyliborBundle )
+ return true;
+ else
+ throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)";
+ case MH_EXECUTE:
+ if ( executableOrDyliborBundle )
+ return true;
+ else
+ throw "can't link with a main executable";
+ default:
+ return false;
+ }
+}
+
+}; // namespace dylib
+}; // namespace mach_o
+
+
+#endif // __OBJECT_FILE_DYLIB_MACH_O__
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2005-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifndef __OBJECT_FILE_MACH_O__
+#define __OBJECT_FILE_MACH_O__
+
+#include <stdint.h>
+#include <math.h>
+#include <unistd.h>
+#include <sys/param.h>
+
+#include <vector>
+#include <set>
+#include <algorithm>
+
+#include "MachOFileAbstraction.hpp"
+#include "Architectures.hpp"
+#include "ObjectFile.h"
+#include "dwarf2.h"
+#include "debugline.h"
+
+#include <libunwind/DwarfInstructions.hpp>
+#include <libunwind/AddressSpace.hpp>
+#include <libunwind/Registers.hpp>
+
+//
+//
+// To implement architecture xxx, you must write template specializations for the following six methods:
+// Reader<xxx>::validFile()
+// Reader<xxx>::validSectionType()
+// Reader<xxx>::addRelocReference()
+// Reference<xxx>::getDescription()
+//
+//
+
+
+
+extern __attribute__((noreturn)) void throwf(const char* format, ...);
+extern void warning(const char* format, ...);
+
+namespace mach_o {
+namespace relocatable {
+
+
+
+class ReferenceSorter
+{
+public:
+ bool operator()(const ObjectFile::Reference* left, const ObjectFile::Reference* right)
+ {
+ return ( left->getFixUpOffset() < right->getFixUpOffset() );
+ }
+};
+
+
+// forward reference
+template <typename A> class Reader;
+
+struct AtomAndOffset
+{
+ AtomAndOffset(ObjectFile::Atom* a=NULL) : atom(a), offset(0) {}
+ AtomAndOffset(ObjectFile::Atom* a, uint32_t off) : atom(a), offset(off) {}
+ ObjectFile::Atom* atom;
+ uint32_t offset;
+};
+
+
+template <typename A>
+class Reference : public ObjectFile::Reference
+{
+public:
+ typedef typename A::P P;
+ typedef typename A::P::uint_t pint_t;
+ typedef typename A::ReferenceKinds Kinds;
+
+ Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& toTarget);
+ Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& fromTarget, const AtomAndOffset& toTarget);
+ Reference(Kinds kind, const AtomAndOffset& at, const char* toName, uint32_t toOffset);
+
+ virtual ~Reference() {}
+
+
+ virtual ObjectFile::Reference::TargetBinding getTargetBinding() const;
+ virtual ObjectFile::Reference::TargetBinding getFromTargetBinding() const;
+ virtual uint8_t getKind() const { return (uint8_t)fKind; }
+ virtual uint64_t getFixUpOffset() const { return fFixUpOffsetInSrc; }
+ virtual const char* getTargetName() const { return (fToTarget.atom != NULL) ? fToTarget.atom->getName() : fToTargetName; }
+ virtual ObjectFile::Atom& getTarget() const { return *fToTarget.atom; }
+ virtual uint64_t getTargetOffset() const { return (int64_t)((int32_t)fToTarget.offset); }
+ virtual ObjectFile::Atom& getFromTarget() const { return *fFromTarget.atom; }
+ virtual const char* getFromTargetName() const { return (fFromTarget.atom != NULL) ? fFromTarget.atom->getName() : fFromTargetName; }
+ virtual void setTarget(ObjectFile::Atom& target, uint64_t offset) { fToTarget.atom = ⌖ fToTarget.offset = offset; }
+ virtual void setToTargetOffset(uint64_t offset) { fToTarget.offset = offset; }
+ virtual void setFromTarget(ObjectFile::Atom& target) { fFromTarget.atom = ⌖ }
+ virtual void setFromTargetName(const char* name) { fFromTargetName = name; }
+ virtual void setFromTargetOffset(uint64_t offset) { fFromTarget.offset = offset; }
+ virtual const char* getDescription() const;
+ virtual uint64_t getFromTargetOffset() const { return fFromTarget.offset; }
+ virtual bool isBranch() const;
+ virtual const char* getTargetDisplayName() const { return (fToTarget.atom != NULL) ? fToTarget.atom->getDisplayName() : fToTargetName; }
+ virtual const char* getFromTargetDisplayName() const { return (fFromTarget.atom != NULL) ? fFromTarget.atom->getDisplayName() : fFromTargetName; }
+
+ static bool fgForFinalLinkedImage;
+
+private:
+ pint_t fFixUpOffsetInSrc;
+ AtomAndOffset fToTarget;
+ AtomAndOffset fFromTarget;
+ const char* fToTargetName;
+ const char* fFromTargetName;
+ Kinds fKind;
+
+};
+
+
+template <typename A> bool Reference<A>::fgForFinalLinkedImage = true;
+
+template <typename A>
+Reference<A>::Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& toTarget)
+ : fFixUpOffsetInSrc(at.offset), fToTarget(toTarget), fToTargetName(NULL), fFromTargetName(NULL),
+ fKind(kind)
+{
+ // make reference a by-name unless:
+ // - the reference type is only used with direct references
+ // - the target is translation unit scoped
+ // - the target kind is not regular (is weak or tentative)
+ if ( (kind != A::kNoFixUp) && (kind != A::kFollowOn) && (kind != A::kGroupSubordinate)
+ && (toTarget.atom->getScope() != ObjectFile::Atom::scopeTranslationUnit)
+ && (toTarget.atom->getDefinitionKind() != ObjectFile::Atom::kRegularDefinition)
+ && (toTarget.atom != at.atom) ) {
+ fToTargetName = toTarget.atom->getName();
+ //fprintf(stderr, "Reference(): changing to by-name %p %s, target scope=%d, target section=%s\n", toTarget.atom, fToTargetName, toTarget.atom->getScope(), toTarget.atom->getSectionName());
+ fToTarget.atom = NULL;
+ }
+ ((class BaseAtom*)at.atom)->addReference(this);
+ //fprintf(stderr, "Reference(): %p fToTarget<%s, %08X>\n", this, (fToTarget.atom != NULL) ? fToTarget.atom->getDisplayName() : fToTargetName , fToTarget.offset);
+}
+
+template <typename A>
+Reference<A>::Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& fromTarget, const AtomAndOffset& toTarget)
+ : fFixUpOffsetInSrc(at.offset), fToTarget(toTarget), fFromTarget(fromTarget),
+ fToTargetName(NULL), fFromTargetName(NULL), fKind(kind)
+{
+ // make reference a by-name where needed
+ if ( (kind != A::kNoFixUp) && (kind != A::kFollowOn) && (kind != A::kGroupSubordinate)
+ && (toTarget.atom->getScope() != ObjectFile::Atom::scopeTranslationUnit)
+ && (toTarget.atom->getDefinitionKind() != ObjectFile::Atom::kRegularDefinition)
+ && (toTarget.atom != at.atom) ) {
+ fToTargetName = toTarget.atom->getName();
+ fToTarget.atom = NULL;
+ }
+ ((class BaseAtom*)at.atom)->addReference(this);
+ //fprintf(stderr, "Reference(): %p kind=%d, fToTarget<%s, %08X>, fromTarget<%s, %08X>\n", this, kind,
+ // this->getTargetName(), fToTarget.offset, this->getFromTargetName(), fromTarget.offset);
+}
+
+template <typename A>
+Reference<A>::Reference(Kinds kind, const AtomAndOffset& at, const char* toName, uint32_t toOffset)
+ : fFixUpOffsetInSrc(at.offset),
+ fToTargetName(toName), fFromTargetName(NULL), fKind(kind)
+{
+ fToTarget.offset = toOffset;
+ ((class BaseAtom*)at.atom)->addReference(this);
+}
+
+template <typename A>
+ObjectFile::Reference::TargetBinding Reference<A>::getTargetBinding() const
+{
+ if ( fgForFinalLinkedImage ) {
+ if ( (fKind == A::kDtraceProbe) || (fKind == A::kDtraceProbeSite) || (fKind == A::kDtraceIsEnabledSite) || (fKind == A::kDtraceTypeReference) )
+ return ObjectFile::Reference::kDontBind;
+ }
+ if ( fToTarget.atom == NULL )
+ return ObjectFile::Reference::kUnboundByName;
+ if ( fToTargetName == NULL )
+ return ObjectFile::Reference::kBoundDirectly;
+ else
+ return ObjectFile::Reference::kBoundByName;
+}
+
+template <typename A>
+ObjectFile::Reference::TargetBinding Reference<A>::getFromTargetBinding() const
+{
+ if ( fFromTarget.atom == NULL ) {
+ if ( fFromTargetName == NULL )
+ return ObjectFile::Reference::kDontBind;
+ else
+ return ObjectFile::Reference::kUnboundByName;
+ }
+ else {
+ if ( fFromTargetName == NULL )
+ return ObjectFile::Reference::kBoundDirectly;
+ else
+ return ObjectFile::Reference::kBoundByName;
+ }
+}
+
+
+
+template <typename A>
+class Segment : public ObjectFile::Segment
+{
+public:
+ Segment(const macho_section<typename A::P>* sect);
+ virtual const char* getName() const { return fSection->segname(); }
+ virtual bool isContentReadable() const { return true; }
+ virtual bool isContentWritable() const { return fWritable; }
+ virtual bool isContentExecutable() const { return fExecutable; }
+private:
+ const macho_section<typename A::P>* fSection;
+ bool fWritable;
+ bool fExecutable;
+};
+
+template <typename A>
+Segment<A>::Segment(const macho_section<typename A::P>* sect)
+ : fSection(sect), fWritable(true), fExecutable(false)
+{
+ if ( strcmp(fSection->segname(), "__TEXT") == 0 ) {
+ fWritable = false;
+ fExecutable = true;
+ }
+ else if ( strcmp(fSection->segname(), "__IMPORT") == 0 ) {
+ fWritable = true;
+ fExecutable = true;
+ }
+}
+
+
+class DataSegment : public ObjectFile::Segment
+{
+public:
+ virtual const char* getName() const { return "__DATA"; }
+ virtual bool isContentReadable() const { return true; }
+ virtual bool isContentWritable() const { return true; }
+ virtual bool isContentExecutable() const { return false; }
+
+ static DataSegment fgSingleton;
+};
+
+DataSegment DataSegment::fgSingleton;
+
+class LinkEditSegment : public ObjectFile::Segment
+{
+public:
+ virtual const char* getName() const { return "__LINKEDIT"; }
+ virtual bool isContentReadable() const { return true; }
+ virtual bool isContentWritable() const { return false; }
+ virtual bool isContentExecutable() const { return false; }
+
+ static LinkEditSegment fgSingleton;
+};
+
+LinkEditSegment LinkEditSegment::fgSingleton;
+
+class BaseAtom : public ObjectFile::Atom
+{
+public:
+ BaseAtom() : fStabsStartIndex(0), fStabsCount(0), fHasCompactUnwindInfo(false) {}
+
+ virtual void setSize(uint64_t size) = 0;
+ virtual void addReference(ObjectFile::Reference* ref) = 0;
+ virtual void sortReferences() = 0;
+ virtual void addLineInfo(const ObjectFile::LineInfo& info) = 0;
+ virtual const ObjectFile::ReaderOptions& getOptions() const = 0;
+ virtual uint64_t getObjectAddress() const = 0;
+ virtual uint32_t getOrdinal() const { return fOrdinal; }
+ virtual void setOrdinal(uint32_t value) { fOrdinal = value; }
+ virtual const void* getSectionRecord() const = 0;
+ virtual bool isAlias() const { return false; }
+ virtual uint8_t getLSDAReferenceKind() const { return 0; }
+ virtual uint8_t getPersonalityReferenceKind() const { return 0; }
+ virtual uint32_t getCompactUnwindEncoding(uint64_t ehAtomAddress) { return 0; }
+ virtual ObjectFile::UnwindInfo::iterator beginUnwind() { return fHasCompactUnwindInfo ? &fSingleUnwindInfo[0] : NULL; }
+ virtual ObjectFile::UnwindInfo::iterator endUnwind() { return fHasCompactUnwindInfo ? &fSingleUnwindInfo[1] : NULL; }
+ virtual ObjectFile::Reference* getLSDA();
+ virtual ObjectFile::Reference* getFDE();
+ virtual Atom* getPersonalityPointer();
+ virtual void setCompactUnwindEncoding(uint64_t ehAtomAddress);
+
+ uint32_t fStabsStartIndex;
+ uint32_t fStabsCount;
+ uint32_t fOrdinal;
+ ObjectFile::UnwindInfo fSingleUnwindInfo[1];
+ bool fHasCompactUnwindInfo;
+};
+
+
+ObjectFile::Reference* BaseAtom::getLSDA()
+{
+ const uint8_t groupKind = this->getLSDAReferenceKind();
+ const std::vector<ObjectFile::Reference*>& refs = this->getReferences();
+ for (std::vector<ObjectFile::Reference*>::const_iterator it=refs.begin(); it != refs.end(); it++) {
+ ObjectFile::Reference* ref = *it;
+ if ( (ref->getKind() == groupKind) && (ref->getTarget().getContentType() == ObjectFile::Atom::kLSDAType) ) {
+ return ref;
+ }
+ }
+ return NULL;
+}
+
+ObjectFile::Reference* BaseAtom::getFDE()
+{
+ const uint8_t groupKind = this->getLSDAReferenceKind();
+ const std::vector<ObjectFile::Reference*>& refs = this->getReferences();
+ for (std::vector<ObjectFile::Reference*>::const_iterator it=refs.begin(); it != refs.end(); it++) {
+ ObjectFile::Reference* ref = *it;
+ if ( (ref->getKind() == groupKind) && (ref->getTarget().getContentType() == ObjectFile::Atom::kCFIType) ) {
+ return ref;
+ }
+ }
+ return NULL;
+}
+
+ObjectFile::Atom* BaseAtom::getPersonalityPointer()
+{
+ const uint8_t personalityKind = this->getPersonalityReferenceKind();
+ const std::vector<ObjectFile::Reference*>& refs = this->getReferences();
+ for (std::vector<ObjectFile::Reference*>::const_iterator it=refs.begin(); it != refs.end(); it++) {
+ ObjectFile::Reference* ref = *it;
+ if ( ref->getKind() == personalityKind ) {
+ if ( strcmp(ref->getTarget().getSectionName(), "__nl_symbol_ptr") == 0 )
+ return &ref->getTarget();
+ if ( strcmp(ref->getTarget().getSectionName(), "__pointers") == 0 )
+ return &ref->getTarget();
+ }
+ }
+ return NULL;
+}
+
+
+void BaseAtom::setCompactUnwindEncoding(uint64_t ehAtomAddress)
+{
+ fSingleUnwindInfo[0].unwindInfo = this->getCompactUnwindEncoding(ehAtomAddress);
+ fHasCompactUnwindInfo = true;
+}
+
+
+class BaseAtomSorter
+{
+public:
+ bool operator()(const class BaseAtom* left, const class BaseAtom* right) {
+ if ( left == right )
+ return false;
+ uint64_t leftAddr = left->getObjectAddress();
+ uint64_t rightAddr = right->getObjectAddress();
+ if ( leftAddr < rightAddr ) {
+ return true;
+ }
+ else if ( leftAddr > rightAddr ) {
+ return false;
+ }
+ else {
+ // if they have same address, one might be the end of a section and the other the start of the next section
+ const void* leftSection = left->getSectionRecord();
+ const void* rightSection = right->getSectionRecord();
+ if ( leftSection != rightSection ) {
+ return ( leftSection < rightSection );
+ }
+ // if they have same address and section, one might be an alias
+ bool leftAlias = left->isAlias();
+ bool rightAlias = right->isAlias();
+ if ( leftAlias && rightAlias ) {
+ // sort multiple aliases for same address first by scope
+ ObjectFile::Atom::Scope leftScope = left->getScope();
+ ObjectFile::Atom::Scope rightScope = right->getScope();
+ if ( leftScope != rightScope ) {
+ return ( leftScope < rightScope );
+ }
+ // sort multiple aliases for same address then by name
+ return ( strcmp(left->getName(), right->getName()) < 0 );
+ }
+ else if ( leftAlias ) {
+ return true;
+ }
+ else if ( rightAlias ) {
+ return false;
+ }
+ else {
+ // they must be tentative defintions
+ switch ( left->getDefinitionKind() ) {
+ case ObjectFile::Atom::kTentativeDefinition:
+ // sort tentative definitions by name
+ return ( strcmp(left->getName(), right->getName()) < 0 );
+ case ObjectFile::Atom::kAbsoluteSymbol:
+ // sort absolute symbols with same address by name
+ return ( strcmp(left->getName(), right->getName()) < 0 );
+ default:
+ // hack for rdar://problem/5102873
+ if ( !left->isZeroFill() || !right->isZeroFill() )
+ warning("atom sorting error for %s and %s in %s", left->getDisplayName(), right->getDisplayName(), left->getFile()->getPath());
+ break;
+ }
+ }
+ }
+ return false;
+ }
+};
+
+
+//
+// A SymbolAtom represents a chunk of a mach-o object file that has a symbol table entry
+// pointing to it. A C function or global variable is represented by one of these atoms.
+//
+//
+template <typename A>
+class SymbolAtom : public BaseAtom
+{
+public:
+ virtual ObjectFile::Reader* getFile() const { return &fOwner; }
+ virtual bool getTranslationUnitSource(const char** dir, const char** name) const
+ { return fOwner.getTranslationUnitSource(dir, name); }
+ virtual const char* getName() const { return &fOwner.fStrings[fSymbol->n_strx()]; }
+ virtual const char* getDisplayName() const { return getName(); }
+ virtual ObjectFile::Atom::Scope getScope() const { return fScope; }
+ virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ((fSymbol->n_desc() & N_WEAK_DEF) != 0)
+ ? ObjectFile::Atom::kWeakDefinition : ObjectFile::Atom::kRegularDefinition; }
+ virtual ObjectFile::Atom::ContentType getContentType() const { return fType; }
+ virtual SymbolTableInclusion getSymbolTableInclusion() const { return fSymbolTableInclusion; }
+ virtual bool dontDeadStrip() const;
+ virtual bool isZeroFill() const { return ((fSection->flags() & SECTION_TYPE) == S_ZEROFILL); }
+ virtual bool isThumb() const { return ((fSymbol->n_desc() & N_ARM_THUMB_DEF) != 0); }
+ virtual uint64_t getSize() const { return fSize; }
+ virtual std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(fReferences); }
+ virtual bool mustRemainInSection() const { return true; }
+ virtual const char* getSectionName() const;
+ virtual Segment<A>& getSegment() const { return *fSegment; }
+ virtual ObjectFile::Atom& getFollowOnAtom() const;
+ virtual std::vector<ObjectFile::LineInfo>* getLineInfo() const { return (std::vector<ObjectFile::LineInfo>*)&fLineInfo; }
+ virtual ObjectFile::Alignment getAlignment() const { return fAlignment; }
+ virtual void copyRawContent(uint8_t buffer[]) const;
+ virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; }
+ virtual void setSize(uint64_t size);
+ virtual void addReference(ObjectFile::Reference* ref) { fReferences.push_back((Reference<A>*)ref); }
+ virtual void sortReferences() { std::sort(fReferences.begin(), fReferences.end(), ReferenceSorter()); }
+ virtual void addLineInfo(const ObjectFile::LineInfo& info) { fLineInfo.push_back(info); }
+ virtual const ObjectFile::ReaderOptions& getOptions() const { return fOwner.fOptions; }
+ virtual uint64_t getObjectAddress() const { return fAddress; }
+ virtual const void* getSectionRecord() const { return (const void*)fSection; }
+ virtual uint8_t getLSDAReferenceKind() const;
+ virtual uint8_t getPersonalityReferenceKind() const;
+ virtual uint32_t getCompactUnwindEncoding(uint64_t ehAtomAddress);
+
+protected:
+ typedef typename A::P P;
+ typedef typename A::P::E E;
+ typedef typename A::P::uint_t pint_t;
+ typedef typename A::ReferenceKinds Kinds;
+ typedef typename std::vector<Reference<A>*> ReferenceVector;
+ typedef typename ReferenceVector::iterator ReferenceVectorIterator; // seems to help C++ parser
+ typedef typename ReferenceVector::const_iterator ReferenceVectorConstIterator; // seems to help C++ parser
+ friend class Reader<A>;
+
+ SymbolAtom(Reader<A>&, const macho_nlist<P>*, const macho_section<P>*);
+ virtual ~SymbolAtom() {}
+
+ Reader<A>& fOwner;
+ const macho_nlist<P>* fSymbol;
+ pint_t fAddress;
+ pint_t fSize;
+ const macho_section<P>* fSection;
+ Segment<A>* fSegment;
+ ReferenceVector fReferences;
+ std::vector<ObjectFile::LineInfo> fLineInfo;
+ ObjectFile::Atom::Scope fScope;
+ SymbolTableInclusion fSymbolTableInclusion;
+ ObjectFile::Atom::ContentType fType;
+ ObjectFile::Alignment fAlignment;
+};
+
+
+template <typename A>
+SymbolAtom<A>::SymbolAtom(Reader<A>& owner, const macho_nlist<P>* symbol, const macho_section<P>* section)
+ : fOwner(owner), fSymbol(symbol), fAddress(0), fSize(0), fSection(section), fSegment(NULL), fType(ObjectFile::Atom::kUnclassifiedType), fAlignment(0)
+{
+ fSingleUnwindInfo[0].startOffset = 0;
+ fSingleUnwindInfo[0].unwindInfo = 0;
+ uint8_t type = symbol->n_type();
+ if ( (type & N_EXT) == 0 )
+ fScope = ObjectFile::Atom::scopeTranslationUnit;
+ else if ( (type & N_PEXT) != 0 )
+ fScope = ObjectFile::Atom::scopeLinkageUnit;
+ else
+ fScope = ObjectFile::Atom::scopeGlobal;
+ if ( (type & N_TYPE) == N_SECT ) {
+ // real definition
+ fSegment = new Segment<A>(fSection);
+ fAddress = fSymbol->n_value();
+ pint_t sectionStartAddr = section->addr();
+ pint_t sectionEndAddr = section->addr()+section->size();
+ if ( (fAddress < sectionStartAddr) || (fAddress > (sectionEndAddr)) ) {
+ throwf("malformed .o file, symbol %s with address 0x%0llX is not with section %d (%s,%s) address range of 0x%0llX to 0x%0llX",
+ this->getName(), (uint64_t)fAddress, fSymbol->n_sect(), section->segname(), section->sectname(),
+ (uint64_t)sectionStartAddr, (uint64_t)(sectionEndAddr) );
+ }
+ }
+ else {
+ warning("unknown symbol type: %d", type);
+ }
+
+ //fprintf(stderr, "SymbolAtom(%p) %s fAddress=0x%X\n", this, this->getDisplayName(), (uint32_t)fAddress);
+ // support for .o files built with old ld64
+ if ( (fSymbol->n_desc() & N_WEAK_DEF) && (strcmp(fSection->sectname(),"__picsymbolstub1__TEXT") == 0) ) {
+ const char* name = this->getName();
+ const int nameLen = strlen(name);
+ if ( (nameLen > 6) && strcmp(&name[nameLen-5], "$stub") == 0 ) {
+ // switch symbol to point at name that does not have trailing $stub
+ char correctName[nameLen];
+ strncpy(correctName, name, nameLen-5);
+ correctName[nameLen-5] = '\0';
+ const macho_nlist<P>* symbolsStart = fOwner.fSymbols;
+ const macho_nlist<P>* symbolsEnd = &symbolsStart[fOwner.fSymbolCount];
+ for(const macho_nlist<P>* s = symbolsStart; s < symbolsEnd; ++s) {
+ if ( strcmp(&fOwner.fStrings[s->n_strx()], correctName) == 0 ) {
+ fSymbol = s;
+ break;
+ }
+ }
+ }
+ }
+ // support for labeled stubs
+ switch ( section->flags() & SECTION_TYPE ) {
+ case S_SYMBOL_STUBS:
+ setSize(section->reserved2());
+ break;
+ case S_LAZY_SYMBOL_POINTERS:
+ case S_NON_LAZY_SYMBOL_POINTERS:
+ setSize(sizeof(pint_t));
+ break;
+ case S_4BYTE_LITERALS:
+ setSize(4);
+ break;
+ case S_8BYTE_LITERALS:
+ setSize(8);
+ break;
+ case S_16BYTE_LITERALS:
+ setSize(16);
+ break;
+ case S_CSTRING_LITERALS:
+ setSize(strlen((char*)(fOwner.fHeader) + section->offset() + fAddress - section->addr()) + 1);
+ fType = ObjectFile::Atom::kCStringType;
+ break;
+ case S_REGULAR:
+ case S_ZEROFILL:
+ case S_COALESCED:
+ // size calculate later after next atom is found
+ break;
+ }
+
+ // compute alignment
+ fAlignment = ObjectFile::Alignment(fSection->align(), fAddress % (1 << fSection->align()));
+
+ // 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 && !fOwner.fOptions.fForStatic && (fOwner.fStrings[fSymbol->n_strx()] == 'l') ) {
+ // labels beginning with a lowercase ell are automatically removed in final linked images <rdar://problem/4571042>
+ // xnu code base uses a lot of asesembly labels that start with 'l', don't strip those (static executable)
+ fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableNotIn;
+ }
+ else {
+ fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn;
+ }
+
+ // work around malformed icc generated .o files <rdar://problem/5349847>
+ // if section starts with a symbol and that symbol address does not match section alignment, then force it to
+ if ( (section->addr() == fAddress) && (fAlignment.modulus != 0) )
+ fAlignment.modulus = 0;
+}
+
+template <typename A>
+bool SymbolAtom<A>::dontDeadStrip() const
+{
+ // the symbol can have a no-dead-strip bit
+ if ( (fSymbol->n_desc() & (N_NO_DEAD_STRIP|REFERENCED_DYNAMICALLY)) != 0 )
+ return true;
+ // or the section can have a no-dead-strip bit
+ return ( fSection->flags() & S_ATTR_NO_DEAD_STRIP );
+}
+
+
+template <typename A>
+const char* SymbolAtom<A>::getSectionName() const
+{
+ if ( fOwner.fOptions.fForFinalLinkedImage ) {
+ if ( strcmp(fSection->sectname(), "__textcoal_nt") == 0 )
+ return "__text";
+ else if ( strcmp(fSection->sectname(), "__const_coal") == 0 )
+ return "__const";
+ else if ( strcmp(fSection->sectname(), "__datacoal_nt") == 0 )
+ return "__data";
+ else if ( fOwner.fOptions.fAutoOrderInitializers && (strcmp(fSection->sectname(), "__StaticInit") == 0) )
+ return "__text";
+ else {
+ switch ( fSection->flags() & SECTION_TYPE ) {
+ case S_4BYTE_LITERALS:
+ case S_8BYTE_LITERALS:
+ case S_16BYTE_LITERALS:
+ return "__const";
+ }
+ }
+ }
+
+ if ( strlen(fSection->sectname()) > 15 ) {
+ static char temp[18];
+ strncpy(temp, fSection->sectname(), 16);
+ temp[17] = '\0';
+ return temp;
+ }
+ return fSection->sectname();
+}
+
+template <typename A>
+ObjectFile::Atom& SymbolAtom<A>::getFollowOnAtom() const
+{
+ for (ReferenceVectorConstIterator it=fReferences.begin(); it != fReferences.end(); it++) {
+ Reference<A>* ref = *it;
+ if ( ref->getKind() == A::kFollowOn )
+ return ref->getTarget();
+ }
+ return *((ObjectFile::Atom*)NULL);
+}
+
+
+class Beyond
+{
+public:
+ Beyond(uint64_t offset) : fOffset(offset) {}
+ bool operator()(ObjectFile::Reference* ref) const {
+ return ( ref->getFixUpOffset() >= fOffset );
+ }
+private:
+ uint64_t fOffset;
+};
+
+
+template <typename A>
+void SymbolAtom<A>::setSize(uint64_t size)
+{
+ // when resizing, any references beyond the new size are tossed
+ if ( (fSize != 0) && (fReferences.size() > 0) )
+ fReferences.erase(std::remove_if(fReferences.begin(), fReferences.end(), Beyond(size)), fReferences.end());
+ // set new size
+ fSize = size;
+}
+
+template <typename A>
+void SymbolAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ // copy base bytes
+ if ( isZeroFill() )
+ bzero(buffer, fSize);
+ else {
+ uint32_t fileOffset = fSection->offset() - fSection->addr() + fAddress;
+ memcpy(buffer, (char*)(fOwner.fHeader)+fileOffset, fSize);
+ }
+}
+
+
+
+
+//
+// A SymbolAliasAtom represents an alternate name for a SymbolAtom
+//
+//
+template <typename A>
+class SymbolAliasAtom : public BaseAtom
+{
+public:
+ virtual ObjectFile::Reader* getFile() const { return fAliasOf.getFile(); }
+ virtual bool getTranslationUnitSource(const char** dir, const char** name) const
+ { return fAliasOf.getTranslationUnitSource(dir, name); }
+ virtual const char* getName() const { return fName; }
+ virtual const char* getDisplayName() const { return fName; }
+ virtual ObjectFile::Atom::Scope getScope() const { return fScope; }
+ virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return fAliasOf.getDefinitionKind(); }
+ virtual SymbolTableInclusion getSymbolTableInclusion() const { return fAliasOf.getSymbolTableInclusion(); }
+ virtual bool dontDeadStrip() const { return fDontDeadStrip; }
+ virtual bool isZeroFill() const { return fAliasOf.isZeroFill(); }
+ virtual bool isThumb() const { return fAliasOf.isThumb(); }
+ virtual uint64_t getSize() const { return 0; }
+ virtual std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(fReferences); }
+ virtual bool mustRemainInSection() const { return true; }
+ virtual const char* getSectionName() const { return fAliasOf.getSectionName(); }
+ virtual Segment<A>& getSegment() const { return (Segment<A>&)fAliasOf.getSegment(); }
+ virtual ObjectFile::Atom& getFollowOnAtom() const { return (ObjectFile::Atom&)fAliasOf; }
+ virtual std::vector<ObjectFile::LineInfo>* getLineInfo() const { return NULL; }
+ virtual ObjectFile::Alignment getAlignment() const { return fAliasOf.getAlignment(); }
+ virtual void copyRawContent(uint8_t buffer[]) const {}
+ virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; }
+ virtual void setSize(uint64_t size) { }
+ virtual void addReference(ObjectFile::Reference* ref) { fReferences.push_back((Reference<A>*)ref); }
+ virtual void sortReferences() { std::sort(fReferences.begin(), fReferences.end(), ReferenceSorter()); }
+ virtual void addLineInfo(const ObjectFile::LineInfo& info) { }
+ virtual const ObjectFile::ReaderOptions& getOptions() const { return fAliasOf.getOptions(); }
+ virtual uint64_t getObjectAddress() const { return fAliasOf.getObjectAddress(); }
+ virtual const void* getSectionRecord() const { return fAliasOf.getSectionRecord(); }
+ virtual bool isAlias() const { return true; }
+
+protected:
+ typedef typename A::P P;
+ typedef typename std::vector<Reference<A>*> ReferenceVector;
+ typedef typename ReferenceVector::iterator ReferenceVectorIterator; // seems to help C++ parser
+ typedef typename ReferenceVector::const_iterator ReferenceVectorConstIterator; // seems to help C++ parser
+ friend class Reader<A>;
+
+ SymbolAliasAtom(const char* name, const macho_nlist<P>*, const BaseAtom& );
+ virtual ~SymbolAliasAtom() {}
+
+ const char* fName;
+ const BaseAtom& fAliasOf;
+ ObjectFile::Atom::Scope fScope;
+ bool fDontDeadStrip;
+ ReferenceVector fReferences;
+};
+
+
+template <typename A>
+SymbolAliasAtom<A>::SymbolAliasAtom(const char* name, const macho_nlist<P>* symbol, const BaseAtom& aliasOf)
+ : fName(name), fAliasOf(aliasOf)
+{
+ //fprintf(stderr, "SymbolAliasAtom(%p) %s\n", this, name);
+ if ( symbol != NULL ) {
+ uint8_t type = symbol->n_type();
+ if ( (type & N_EXT) == 0 )
+ fScope = ObjectFile::Atom::scopeTranslationUnit;
+ else if ( (type & N_PEXT) != 0 )
+ fScope = ObjectFile::Atom::scopeLinkageUnit;
+ else
+ fScope = ObjectFile::Atom::scopeGlobal;
+ fDontDeadStrip = ((symbol->n_desc() & (N_NO_DEAD_STRIP|REFERENCED_DYNAMICALLY)) != 0);
+ }
+ else {
+ // aliases defined on the command line are initially global scope
+ fScope = ObjectFile::Atom::scopeGlobal;
+ fDontDeadStrip = false;
+ }
+ // add follow-on reference to real atom
+ new Reference<A>(A::kFollowOn, AtomAndOffset(this), AtomAndOffset((ObjectFile::Atom*)&aliasOf));
+}
+
+
+//
+// A TentativeAtom represents a C "common" or "tentative" defintion of data.
+// For instance, "int foo;" is neither a declaration or a definition and
+// is represented by a TentativeAtom.
+//
+template <typename A>
+class TentativeAtom : public BaseAtom
+{
+public:
+ virtual ObjectFile::Reader* getFile() const { return &fOwner; }
+ virtual bool getTranslationUnitSource(const char** dir, const char** name) const
+ { return fOwner.getTranslationUnitSource(dir, name); }
+ virtual const char* getName() const { return &fOwner.fStrings[fSymbol->n_strx()]; }
+ virtual const char* getDisplayName() const { return getName(); }
+ virtual ObjectFile::Atom::Scope getScope() const { return fScope; }
+ virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ObjectFile::Atom::kTentativeDefinition; }
+ virtual bool isZeroFill() const { return true; }
+ virtual bool isThumb() const { return false; }
+ 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 const char* getSectionName() const;
+ virtual ObjectFile::Segment& getSegment() const { return DataSegment::fgSingleton; }
+ virtual ObjectFile::Atom& getFollowOnAtom() const { return *(ObjectFile::Atom*)NULL; }
+ virtual std::vector<ObjectFile::LineInfo>* getLineInfo() const { return NULL; }
+ virtual ObjectFile::Alignment getAlignment() const;
+ virtual void copyRawContent(uint8_t buffer[]) const;
+ virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; }
+ virtual void setSize(uint64_t size) { }
+ virtual void addReference(ObjectFile::Reference* ref) { throw "ld: can't add references"; }
+ virtual void sortReferences() { }
+ virtual void addLineInfo(const ObjectFile::LineInfo& info) { throw "ld: can't add line info to tentative definition"; }
+ virtual const ObjectFile::ReaderOptions& getOptions() const { return fOwner.fOptions; }
+ virtual uint64_t getObjectAddress() const { return ULLONG_MAX; }
+ virtual const void* getSectionRecord() const { return NULL; }
+
+protected:
+ typedef typename A::P P;
+ typedef typename A::P::E E;
+ typedef typename A::P::uint_t pint_t;
+ typedef typename A::ReferenceKinds Kinds;
+ friend class Reader<A>;
+
+ TentativeAtom(Reader<A>&, const macho_nlist<P>*);
+ virtual ~TentativeAtom() {}
+
+ Reader<A>& fOwner;
+ const macho_nlist<P>* fSymbol;
+ ObjectFile::Atom::Scope fScope;
+ static std::vector<ObjectFile::Reference*> fgNoReferences;
+};
+
+template <typename A>
+std::vector<ObjectFile::Reference*> TentativeAtom<A>::fgNoReferences;
+
+template <typename A>
+TentativeAtom<A>::TentativeAtom(Reader<A>& owner, const macho_nlist<P>* symbol)
+ : fOwner(owner), fSymbol(symbol)
+{
+ uint8_t type = symbol->n_type();
+ if ( (type & N_EXT) == 0 )
+ fScope = ObjectFile::Atom::scopeTranslationUnit;
+ else if ( (type & N_PEXT) != 0 )
+ fScope = ObjectFile::Atom::scopeLinkageUnit;
+ else
+ fScope = ObjectFile::Atom::scopeGlobal;
+ if ( ((type & N_TYPE) == N_UNDF) && (symbol->n_value() != 0) ) {
+ // tentative definition
+ }
+ else {
+ warning("unknown symbol type: %d", type);
+ }
+ //fprintf(stderr, "TentativeAtom(%p) %s\n", this, this->getDisplayName());
+}
+
+
+template <typename A>
+ObjectFile::Alignment TentativeAtom<A>::getAlignment() const
+{
+ uint8_t alignment = GET_COMM_ALIGN(fSymbol->n_desc());
+ if ( alignment == 0 ) {
+ // common symbols align to their size
+ // that is, a 4-byte common aligns to 4-bytes
+ // if this size is not a power of two,
+ // then round up to the next power of two
+ uint64_t size = this->getSize();
+ alignment = 63 - (uint8_t)__builtin_clzll(size);
+ if ( size != (1ULL << alignment) )
+ ++alignment;
+ }
+ // limit alignment of extremely large commons to 2^15 bytes (8-page)
+ if ( alignment < 12 )
+ return ObjectFile::Alignment(alignment);
+ else
+ return ObjectFile::Alignment(12);
+}
+
+template <typename A>
+const char* TentativeAtom<A>::getSectionName() const
+{
+ if ( fOwner.fOptions.fForFinalLinkedImage || fOwner.fOptions.fMakeTentativeDefinitionsReal )
+ return "__common";
+ else
+ return "._tentdef";
+}
+
+
+template <typename A>
+void TentativeAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ bzero(buffer, getSize());
+}
+
+
+//
+// An AnonymousAtom represents compiler generated data that has no name.
+// For instance, a literal C-string or a 64-bit floating point constant
+// is represented by an AnonymousAtom.
+//
+template <typename A>
+class AnonymousAtom : public BaseAtom
+{
+public:
+ virtual ObjectFile::Reader* getFile() const { return &fOwner; }
+ virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; }
+ virtual const char* getName() const { return fSynthesizedName; }
+ virtual const char* getDisplayName() const;
+ virtual ObjectFile::Atom::Scope getScope() const;
+ virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return fKind; }
+ virtual ObjectFile::Atom::ContentType getContentType() const { return fType; }
+ virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return fSymbolTableInclusion; }
+ virtual bool dontDeadStrip() const { return fDontDeadStrip; }
+ virtual bool isZeroFill() const;
+ virtual bool isThumb() const { return false; }
+ virtual uint64_t getSize() const { return fSize; }
+ virtual std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(fReferences); }
+ virtual bool mustRemainInSection() const { return true; }
+ virtual const char* getSectionName() const;
+ virtual Segment<A>& getSegment() const { return *fSegment; }
+ virtual ObjectFile::Atom& getFollowOnAtom() const;
+ virtual std::vector<ObjectFile::LineInfo>* getLineInfo() const { return NULL; }
+ virtual ObjectFile::Alignment getAlignment() const;
+ virtual void copyRawContent(uint8_t buffer[]) const;
+ virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; }
+ virtual void setSize(uint64_t size) { fSize = size; }
+ virtual void addReference(ObjectFile::Reference* ref) { fReferences.push_back((Reference<A>*)ref); }
+ virtual void sortReferences() { std::sort(fReferences.begin(), fReferences.end(), ReferenceSorter()); }
+ virtual void addLineInfo(const ObjectFile::LineInfo& info);
+ virtual const ObjectFile::ReaderOptions& getOptions() const { return fOwner.fOptions; }
+ virtual uint64_t getObjectAddress() const { return fAddress; }
+ virtual const void* getSectionRecord() const { return (const void*)fSection; }
+ BaseAtom* redirectTo() { return fRedirect; }
+ bool isWeakImportStub() { return fWeakImportStub; }
+ void resolveName();
+ virtual uint8_t getLSDAReferenceKind() const;
+ virtual uint8_t getPersonalityReferenceKind() const;
+ virtual uint32_t getCompactUnwindEncoding(uint64_t ehAtomAddress);
+
+protected:
+ typedef typename A::P P;
+ typedef typename A::P::E E;
+ typedef typename A::P::uint_t pint_t;
+ typedef typename A::ReferenceKinds Kinds;
+ typedef typename std::vector<Reference<A>*> ReferenceVector;
+ typedef typename ReferenceVector::iterator ReferenceVectorIterator; // seems to help C++ parser
+ typedef typename ReferenceVector::const_iterator ReferenceVectorConstIterator; // seems to help C++ parser
+ friend class Reader<A>;
+
+ AnonymousAtom(Reader<A>&, const macho_section<P>*, pint_t addr, pint_t size);
+ virtual ~AnonymousAtom() {}
+ static bool cstringsHaveLabels();
+
+ Reader<A>& fOwner;
+ const char* fSynthesizedName;
+ const char* fDisplayName;
+ const macho_section<P>* fSection;
+ pint_t fAddress;
+ pint_t fSize;
+ Segment<A>* fSegment;
+ ReferenceVector fReferences;
+ BaseAtom* fRedirect;
+ bool fDontDeadStrip;
+ bool fWeakImportStub;
+ ObjectFile::Atom::SymbolTableInclusion fSymbolTableInclusion;
+ ObjectFile::Atom::Scope fScope;
+ ObjectFile::Atom::DefinitionKind fKind;
+ ObjectFile::Atom::ContentType fType;
+};
+
+template <typename A>
+AnonymousAtom<A>::AnonymousAtom(Reader<A>& owner, const macho_section<P>* section, pint_t addr, pint_t size)
+ : fOwner(owner), fSynthesizedName(NULL), fDisplayName(NULL), fSection(section), fAddress(addr), fSize(size),
+ fSegment(NULL), fDontDeadStrip(true), fWeakImportStub(false), fSymbolTableInclusion(ObjectFile::Atom::kSymbolTableNotIn),
+ fScope(ObjectFile::Atom::scopeTranslationUnit), fKind(ObjectFile::Atom::kRegularDefinition),
+ fType(ObjectFile::Atom::kUnclassifiedType)
+{
+ fSegment = new Segment<A>(fSection);
+ fRedirect = this;
+ uint8_t type = fSection->flags() & SECTION_TYPE;
+ //fprintf(stderr, "AnonymousAtom(%p) addr=0x%llX in %s from %s\n", this, (long long)addr, section->sectname(), owner.getPath());
+ switch ( type ) {
+ case S_ZEROFILL:
+ {
+ asprintf((char**)&fSynthesizedName, "zero-fill-at-0x%08X", addr);
+ }
+ break;
+ case S_COALESCED:
+ case S_REGULAR:
+ if ( section == owner.fehFrameSection ) {
+ if ( fSize == 1 ) {
+ // is CIE
+ fSize = 0;
+ fDontDeadStrip = false;
+ if ( fOwner.fOptions.fForFinalLinkedImage )
+ fSynthesizedName = "CIE";
+ else
+ fSynthesizedName = "EH_frame1";
+ }
+ else {
+ // is FDE
+ fSynthesizedName = ".eh_PENDING";
+ fDontDeadStrip = false;
+ owner.fAtomsPendingAName.push_back(this);
+ }
+ fType = ObjectFile::Atom::kCFIType;
+ // FDEs and CIEs don't need to be in symbol table of final linked images <rdar://problem/4180168>
+ if ( !fOwner.fOptions.fNoEHLabels )
+ fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn;
+ }
+ else 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
+ fSynthesizedName = ".objc_class_name_PENDING";
+ owner.fAtomsPendingAName.push_back(this);
+ owner.fSectionsWithAtomsPendingAName.insert(fSection);
+ 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);
+ }
+ else if ((strcmp(section->sectname(), "__cfstring") == 0) && (strcmp(section->segname(), "__DATA") == 0)) {
+ fSynthesizedName = "cfstring-pointer-name-PENDING";
+ fScope = ObjectFile::Atom::scopeLinkageUnit;
+ owner.fAtomsPendingAName.push_back(this);
+ owner.fSectionsWithAtomsPendingAName.insert(fSection);
+ fDontDeadStrip = false;
+ fKind = ObjectFile::Atom::kWeakDefinition;
+ }
+ else if ( (fSection->flags() & S_ATTR_SOME_INSTRUCTIONS) != 0 ) {
+ fDontDeadStrip = false;
+ asprintf((char**)&fSynthesizedName, "anon-func-0x%X", addr);
+ }
+ else if ( strncmp(fSection->sectname(), "__gcc_except_tab",16) == 0 ) {
+ fType = ObjectFile::Atom::kLSDAType;
+ fDontDeadStrip = false;
+ fSynthesizedName = ".lsda_PENDING";
+ owner.fAtomsPendingAName.push_back(this);
+ if ( !fOwner.fOptions.fNoEHLabels )
+ fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn;
+ }
+ break;
+ case S_CSTRING_LITERALS:
+ {
+ const char* str = (char*)(owner.fHeader) + section->offset() + addr - section->addr();
+ if ( (strcmp(fSection->sectname(), "__cstring") == 0) && (strcmp(section->segname(), "__TEXT") == 0) )
+ asprintf((char**)&fSynthesizedName, "cstring=%s", str);
+ else
+ asprintf((char**)&fSynthesizedName, "cstring%s%s=%s", fSection->segname(), fSection->sectname(), str);
+ fScope = ObjectFile::Atom::scopeLinkageUnit;
+ fKind = ObjectFile::Atom::kWeakDefinition;
+ fType = ObjectFile::Atom::kCStringType;
+ fDontDeadStrip = false;
+ if ( !fOwner.fOptions.fForFinalLinkedImage && cstringsHaveLabels() )
+ fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn;
+ }
+ 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;
+ fKind = ObjectFile::Atom::kWeakDefinition;
+ 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;
+ fKind = ObjectFile::Atom::kWeakDefinition;
+ 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;
+ fKind = ObjectFile::Atom::kWeakDefinition;
+ fDontDeadStrip = false;
+ }
+ break;
+ case S_LITERAL_POINTERS:
+ {
+ //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);
+ fSynthesizedName = "literal-pointer-name-PENDING";
+ fScope = ObjectFile::Atom::scopeLinkageUnit;
+ fKind = ObjectFile::Atom::kWeakDefinition;
+ fDontDeadStrip = false;
+ owner.fAtomsPendingAName.push_back(this);
+ owner.fSectionsWithAtomsPendingAName.insert(fSection);
+ }
+ break;
+ case S_MOD_INIT_FUNC_POINTERS:
+ asprintf((char**)&fSynthesizedName, "initializer$%d", (addr - (uint32_t)fSection->addr())/sizeof(pint_t));
+ break;
+ case S_MOD_TERM_FUNC_POINTERS:
+ asprintf((char**)&fSynthesizedName, "terminator$%d", (addr - (uint32_t)fSection->addr())/sizeof(pint_t));
+ break;
+ case S_SYMBOL_STUBS:
+ {
+ uint32_t index = (fAddress - fSection->addr()) / fSection->reserved2();
+ index += fSection->reserved1();
+ uint32_t symbolIndex = E::get32(fOwner.fIndirectTable[index]);
+ const macho_nlist<P>* sym = &fOwner.fSymbols[symbolIndex];
+ uint32_t strOffset = sym->n_strx();
+ // want name to not have $stub suffix, this is what automatic stub generation expects
+ fSynthesizedName = &fOwner.fStrings[strOffset];
+ // check for weak import
+ fWeakImportStub = fOwner.isWeakImportSymbol(sym);
+ // sometimes the compiler gets confused and generates a stub to a static function
+ // if so, we should redirect any call to the stub to be calls to the real static function atom
+ if ( ((sym->n_type() & N_TYPE) != N_UNDF) && ((sym->n_type() & N_EXT) == 0) ) {
+ BaseAtom* staticAtom = fOwner.findAtomByName(fSynthesizedName);
+ if ( staticAtom != NULL )
+ fRedirect = staticAtom;
+ }
+ fKind = ObjectFile::Atom::kWeakDefinition;
+ // might be a spurious stub for a static function, make stub static too
+ if ( (sym->n_type() & N_EXT) == 0 )
+ fScope = ObjectFile::Atom::scopeTranslationUnit;
+ else
+ fScope = ObjectFile::Atom::scopeLinkageUnit;
+ }
+ break;
+ case S_LAZY_SYMBOL_POINTERS:
+ case S_NON_LAZY_SYMBOL_POINTERS:
+ {
+ // transform i386 __IMPORT/__pointers to __DATA/__nl_symbol_ptr when
+ // generating the new compressed LINKEDIT format
+ if ( (type == S_NON_LAZY_SYMBOL_POINTERS) && fOwner.fOptions.fMakeCompressedDyldInfo && (strcmp(fSection->segname(),"__IMPORT") == 0) ) {
+ macho_section<P>* dummySection = new macho_section<P>(*fSection);
+ dummySection->set_segname("__DATA");
+ dummySection->set_sectname("__nl_symbol_ptr");
+ fSection = dummySection;
+ fSegment = new Segment<A>(fSection);
+ }
+
+ 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
+ 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>* closestSym = NULL;
+ 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_type() & N_STAB) == 0) ) {
+ if ( sym->n_value() == nonLazyPtrValue ) {
+ const char* name = &fOwner.fStrings[sym->n_strx()];
+ char* str = new char[strlen(name)+16];
+ strcpy(str, name);
+ strcat(str, "$non_lazy_ptr");
+ fSynthesizedName = str;
+ // add direct reference to target later, because its atom may not be constructed yet
+ fOwner.fLocalNonLazys.push_back(this);
+ fScope = ObjectFile::Atom::scopeTranslationUnit;
+ return;
+ }
+ else if ( (sym->n_value() < nonLazyPtrValue) && ((closestSym == NULL) || (sym->n_value() > closestSym->n_value())) ) {
+ closestSym = sym;
+ }
+ }
+ }
+ // add direct reference to target later, because its atom may not be constructed yet
+ if ( closestSym != NULL ) {
+ const char* name = &fOwner.fStrings[closestSym->n_strx()];
+ char* str;
+ asprintf(&str, "%s+%u$non_lazy_ptr", name, nonLazyPtrValue - closestSym->n_value());
+ fSynthesizedName = str;
+ }
+ else {
+ fSynthesizedName = "$interior$non_lazy_ptr";
+ }
+ fScope = ObjectFile::Atom::scopeTranslationUnit;
+ fOwner.fLocalNonLazys.push_back(this);
+ return;
+ }
+ const macho_nlist<P>* targetSymbol = &fOwner.fSymbols[symbolIndex];
+ const char* name = &fOwner.fStrings[targetSymbol->n_strx()];
+ char* str = new char[strlen(name)+16];
+ strcpy(str, name);
+ if ( type == S_LAZY_SYMBOL_POINTERS )
+ strcat(str, "$lazy_ptr");
+ else
+ strcat(str, "$non_lazy_ptr");
+ fSynthesizedName = str;
+
+ if ( type == S_NON_LAZY_SYMBOL_POINTERS )
+ fKind = ObjectFile::Atom::kWeakDefinition;
+
+ 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:
+ throwf("section type %d not supported with address=0x%08X", type, addr);
+ }
+ //fprintf(stderr, "AnonymousAtom(%p) %s \n", this, this->getDisplayName());
+}
+
+// x86_64 uses L labels on cstrings to allow relocs with addends
+template <> bool AnonymousAtom<x86_64>::cstringsHaveLabels() { return true; }
+template <typename A> bool AnonymousAtom<A>::cstringsHaveLabels() { return false; }
+
+template <typename A>
+void AnonymousAtom<A>::addLineInfo(const ObjectFile::LineInfo& info)
+{
+ // <rdar://problem/6545406> don't warn if line table has entries for stubs
+ if ( (fSection->flags() & SECTION_TYPE) != S_SYMBOL_STUBS )
+ warning("can't add line info to anonymous symbol %s from %s", this->getDisplayName(), this->getFile()->getPath());
+}
+
+template <typename A>
+void AnonymousAtom<A>::resolveName()
+{
+ if ( (strcmp(fSection->sectname(), "__class") == 0) && (strcmp(fSection->segname(), "__OBJC") == 0) ) {
+ std::vector<ObjectFile::Reference*>& references = this->getReferences();
+ // references are not yet sorted, so scan the vector
+ for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) {
+ if ( ((*rit)->getFixUpOffset() == sizeof(pint_t)) && ((*rit)->getKind() == A::kPointer) ) {
+ const char* superStr = (*rit)->getTargetName();
+ if ( strncmp(superStr, "cstring", 7) == 0 ) {
+ const char* superClassName;
+ asprintf((char**)&superClassName, ".objc_class_name_%s", &superStr[8]);
+ new Reference<A>(A::kNoFixUp, AtomAndOffset(this), superClassName, 0);
+ }
+ break;
+ }
+ }
+ for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) {
+ if ( ((*rit)->getFixUpOffset() == 2*sizeof(pint_t)) && ((*rit)->getKind() == A::kPointer) ) {
+ const char* classStr = (*rit)->getTargetName();
+ if ( strncmp(classStr, "cstring", 7) == 0 ) {
+ asprintf((char**)&fSynthesizedName, ".objc_class_name_%s", &classStr[8]);
+ }
+ break;
+ }
+ }
+ }
+ else if ( (fSection->flags() & SECTION_TYPE) == S_LITERAL_POINTERS) {
+ std::vector<ObjectFile::Reference*>& references = this->getReferences();
+ if ( references.size() < 1 )
+ throwf("S_LITERAL_POINTERS section %s,%s missing relocs", fSection->segname(), fSection->sectname());
+ ObjectFile::Reference* ref = references[0];
+ const char* str = ref->getTargetName();
+ if ( strncmp(str, "cstring", 7) == 0 ) {
+ asprintf((char**)&fSynthesizedName, "literal-pointer@%s@%s@%s", fSection->segname(), fSection->sectname(), &str[8]);
+ }
+ }
+ else if ( (strcmp(fSection->sectname(), "__cfstring") == 0) && (strcmp(fSection->segname(), "__DATA") == 0) ) {
+ // references are not yet sorted, so scan the vector
+ std::vector<ObjectFile::Reference*>& references = this->getReferences();
+ for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) {
+ if ( ((*rit)->getFixUpOffset() == 2*sizeof(pint_t)) && ((*rit)->getKind() == A::kPointer) ) {
+ const char* superStr = (*rit)->getTargetName();
+ if ( (superStr != NULL) && (strncmp(superStr, "cstring=", 8) == 0) ) {
+ asprintf((char**)&fSynthesizedName, "cfstring=%s", &superStr[8]);
+ }
+ else {
+ // compiled with -fwritable-strings or a non-ASCII string
+ fKind = ObjectFile::Atom::kRegularDefinition; // these are not coalescable
+ fScope = ObjectFile::Atom::scopeTranslationUnit;
+ fSynthesizedName = "cfstring-not-coalesable";
+ if ( (*rit)->getTargetOffset() != 0 )
+ warning("-fwritable-strings not compatible with literal CF/NSString in %s", fOwner.getPath());
+ }
+ break;
+ }
+ }
+ }
+ else if ( fSection == fOwner.fehFrameSection ) {
+ // give name to FDE
+ ObjectFile::Atom* funcAtom = fOwner.getFunctionAtomFromFDEAddress(fAddress);
+ if ( funcAtom != NULL )
+ asprintf((char**)&fSynthesizedName, "%s.eh", funcAtom->getDisplayName());
+ }
+ else if ( fOwner.fLSDAAtoms.count(this) != 0) {
+ // give name to LSDA
+ ObjectFile::Atom* funcAtom = fOwner.getFunctionAtomFromLSDAAddress(fAddress);
+ if ( funcAtom != NULL )
+ asprintf((char**)&fSynthesizedName, "%s.lsda", funcAtom->getDisplayName());
+ }
+}
+
+
+template <typename A>
+const char* AnonymousAtom<A>::getDisplayName() const
+{
+ if ( fSynthesizedName != NULL )
+ return fSynthesizedName;
+
+ if ( fDisplayName != NULL )
+ return fDisplayName;
+
+ if ( (fSection->flags() & SECTION_TYPE) == S_CSTRING_LITERALS ) {
+ uint32_t fileOffset = fSection->offset() - fSection->addr() + fAddress;
+ asprintf((char**)&fDisplayName, "atom string literal: \"%s\"", (char*)(fOwner.fHeader)+fileOffset);
+ }
+ else {
+ asprintf((char**)&fDisplayName, "%s@%d", fSection->sectname(), fAddress - (uint32_t)fSection->addr() );
+ }
+ return fDisplayName;
+}
+
+
+template <typename A>
+ObjectFile::Atom::Scope AnonymousAtom<A>::getScope() const
+{
+ return fScope;
+}
+
+
+template <typename A>
+bool AnonymousAtom<A>::isZeroFill() const
+{
+ return ( (fSection->flags() & SECTION_TYPE) == S_ZEROFILL );
+}
+
+
+template <typename A>
+const char* AnonymousAtom<A>::getSectionName() const
+{
+ if ( fOwner.fOptions.fForFinalLinkedImage ) {
+ switch ( fSection->flags() & SECTION_TYPE ) {
+ case S_4BYTE_LITERALS:
+ case S_8BYTE_LITERALS:
+ case S_16BYTE_LITERALS:
+ return "__const";
+ }
+ }
+
+ if ( strlen(fSection->sectname()) > 15 ) {
+ static char temp[18];
+ strncpy(temp, fSection->sectname(), 16);
+ temp[17] = '\0';
+ return temp;
+ }
+ return fSection->sectname();
+}
+
+template <typename A>
+ObjectFile::Alignment AnonymousAtom<A>::getAlignment() const
+{
+ // FDEs and CIEs are always packed together in a final linked image, so ignore section alignment
+ if ( fType == ObjectFile::Atom::kCFIType )
+ return ObjectFile::Alignment(0);
+
+ switch ( fSection->flags() & SECTION_TYPE ) {
+ case S_4BYTE_LITERALS:
+ return ObjectFile::Alignment(2);
+ case S_8BYTE_LITERALS:
+ return ObjectFile::Alignment(3);
+ case S_16BYTE_LITERALS:
+ return ObjectFile::Alignment(4);
+ case S_NON_LAZY_SYMBOL_POINTERS:
+ return ObjectFile::Alignment((uint8_t)log2(sizeof(pint_t)));
+ case S_CSTRING_LITERALS:
+ if ( ! fOwner.fOptions.fForFinalLinkedImage )
+ return ObjectFile::Alignment(fSection->align());
+ default:
+ return ObjectFile::Alignment(fSection->align(), fAddress % (1 << fSection->align()));
+ }
+}
+
+
+template <typename A>
+ObjectFile::Atom& AnonymousAtom<A>::getFollowOnAtom() const
+{
+ for (ReferenceVectorConstIterator it=fReferences.begin(); it != fReferences.end(); it++) {
+ Reference<A>* ref = *it;
+ if ( ref->getKind() == A::kFollowOn )
+ return ref->getTarget();
+ }
+ return *((ObjectFile::Atom*)NULL);
+}
+
+template <typename A>
+void AnonymousAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ // copy base bytes
+ if ( isZeroFill() )
+ bzero(buffer, fSize);
+ else {
+ uint32_t fileOffset = fSection->offset() - fSection->addr() + fAddress;
+ memcpy(buffer, (char*)(fOwner.fHeader)+fileOffset, fSize);
+ }
+}
+
+//
+// An AbsoluteAtom represents an N_ABS symbol which can only be created in
+// assembly language and usable by static executables such as the kernel/
+//
+template <typename A>
+class AbsoluteAtom : public BaseAtom
+{
+public:
+ virtual ObjectFile::Reader* getFile() const { return &fOwner; }
+ virtual bool getTranslationUnitSource(const char** dir, const char** name) const
+ { return fOwner.getTranslationUnitSource(dir, name); }
+ virtual const char* getName() const { return &fOwner.fStrings[fSymbol->n_strx()]; }
+ virtual const char* getDisplayName() const { return getName(); }
+ virtual ObjectFile::Atom::Scope getScope() const { return fScope; }
+ virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ObjectFile::Atom::kAbsoluteSymbol; }
+ virtual bool isZeroFill() const { return false; }
+ virtual bool isThumb() const { return ((fSymbol->n_desc() & N_ARM_THUMB_DEF) != 0); }
+ virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableInAsAbsolute; }
+ virtual bool dontDeadStrip() const { return false; }
+ virtual uint64_t getSize() const { return 0; }
+ virtual std::vector<ObjectFile::Reference*>& getReferences() const { return fgNoReferences; }
+ virtual bool mustRemainInSection() const { return true; }
+ virtual const char* getSectionName() const { return "._absolute"; }
+ virtual ObjectFile::Segment& getSegment() const { return LinkEditSegment::fgSingleton; }
+ virtual ObjectFile::Atom& getFollowOnAtom() const { return *(ObjectFile::Atom*)NULL; }
+ virtual std::vector<ObjectFile::LineInfo>* getLineInfo() const { return NULL; }
+ virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); }
+ 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 "ld: can't add references"; }
+ virtual void sortReferences() { }
+ virtual void addLineInfo(const ObjectFile::LineInfo& info) { throw "ld: can't add line info to tentative definition"; }
+ virtual const ObjectFile::ReaderOptions& getOptions() const { return fOwner.fOptions; }
+ virtual uint64_t getObjectAddress() const { return fSymbol->n_value(); }
+ virtual void setSectionOffset(uint64_t offset) { /* don't let fSectionOffset be altered*/ }
+ virtual const void* getSectionRecord() const { return NULL; }
+
+protected:
+ typedef typename A::P P;
+ typedef typename A::P::E E;
+ typedef typename A::P::uint_t pint_t;
+ typedef typename A::ReferenceKinds Kinds;
+ friend class Reader<A>;
+
+ AbsoluteAtom(Reader<A>&, const macho_nlist<P>*);
+ virtual ~AbsoluteAtom() {}
+
+ Reader<A>& fOwner;
+ const macho_nlist<P>* fSymbol;
+ ObjectFile::Atom::Scope fScope;
+ static std::vector<ObjectFile::Reference*> fgNoReferences;
+};
+
+template <typename A>
+std::vector<ObjectFile::Reference*> AbsoluteAtom<A>::fgNoReferences;
+
+template <typename A>
+AbsoluteAtom<A>::AbsoluteAtom(Reader<A>& owner, const macho_nlist<P>* symbol)
+ : fOwner(owner), fSymbol(symbol)
+{
+ // store absolute adress in fSectionOffset
+ fSectionOffset = symbol->n_value();
+ // compute scope
+ uint8_t type = symbol->n_type();
+ if ( (type & N_EXT) == 0 )
+ fScope = ObjectFile::Atom::scopeTranslationUnit;
+ else if ( (type & N_PEXT) != 0 )
+ fScope = ObjectFile::Atom::scopeLinkageUnit;
+ else
+ fScope = ObjectFile::Atom::scopeGlobal;
+ //fprintf(stderr, "AbsoluteAtom(%p) %s\n", this, this->getDisplayName());
+}
+
+
+
+
+///
+/// ObjectFileAddressSpace is used as a template parameter to UnwindCursor for parsing
+/// dwarf CFI information in an object file.
+///
+template <typename A>
+class ObjectFileAddressSpace
+{
+public:
+ ObjectFileAddressSpace(Reader<A>& reader);
+
+ typedef typename A::P::uint_t pint_t;
+ typedef typename A::P P;
+ typedef typename A::P::uint_t sint_t;
+
+ uint8_t get8(pint_t addr);
+ uint16_t get16(pint_t addr);
+ uint32_t get32(pint_t addr);
+ uint64_t get64(pint_t addr);
+ pint_t getP(pint_t addr);
+ uint64_t getULEB128(pint_t& addr, pint_t end);
+ int64_t getSLEB128(pint_t& addr, pint_t end);
+ pint_t getEncodedP(pint_t& addr, pint_t end, uint8_t encoding);
+private:
+ const void* mappedAddress(pint_t addr, pint_t* relocTarget=NULL);
+ pint_t relocated(uint32_t sectOffset, uint32_t relocsOffset, uint32_t relocsCount);
+ void buildRelocatedMap(const macho_section<P>* sect, std::map<uint32_t,uint64_t>& map);
+
+ Reader<A>& fReader;
+ const uint8_t* fMappingStart;
+ const macho_section<P>* fSectionsStart;
+ const macho_section<P>* fSectionsEnd;
+ std::map<uint32_t,uint64_t> fEHFrameOffsetToTargetMap;
+};
+
+
+template <typename A>
+ObjectFileAddressSpace<A>::ObjectFileAddressSpace(Reader<A>& reader)
+ : fReader(reader), fMappingStart(NULL), fSectionsStart(NULL), fSectionsEnd(NULL)
+{
+}
+
+
+
+template <typename A>
+const void* ObjectFileAddressSpace<A>::mappedAddress(pint_t addr, pint_t* relocTarget)
+{
+ if ( fMappingStart == NULL ) {
+ // delay initialization until now when fReader.fSegment is set up
+ fMappingStart = (uint8_t*)fReader.fHeader;
+ fSectionsStart = (macho_section<P>*)((char*)fReader.fSegment + sizeof(macho_segment_command<P>));
+ fSectionsEnd = &fSectionsStart[fReader.fSegment->nsects()];
+ // find __eh_frame section and build map of relocations for performance
+ buildRelocatedMap(fReader.fehFrameSection, fEHFrameOffsetToTargetMap);
+ }
+ // special case lookups in __eh_frame section to be fast
+ const macho_section<P>* ehSect = fReader.fehFrameSection;
+ if ( (ehSect->addr() <= addr) && (addr < (ehSect->addr()+ehSect->size())) ) {
+ pint_t offsetOfAddrInSection = addr - ehSect->addr();
+ if ( relocTarget != NULL ) {
+ std::map<uint32_t,uint64_t>::iterator pos = fEHFrameOffsetToTargetMap.find(offsetOfAddrInSection);
+ if ( pos != fEHFrameOffsetToTargetMap.end() )
+ *relocTarget = pos->second;
+ else
+ *relocTarget = 0;
+ }
+ return fMappingStart + ehSect->offset() + offsetOfAddrInSection;
+ }
+ else {
+ for (const macho_section<P>* sect=fSectionsStart; sect < fSectionsEnd; ++sect) {
+ if ( (sect->addr() <= addr) && (addr < (sect->addr()+sect->size())) ) {
+ pint_t offsetOfAddrInSection = addr - sect->addr();
+ if ( (sect->flags() & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS ) {
+ const uint32_t indirectTableOffset = sect->reserved1();
+ const uint32_t sectionIndex = offsetOfAddrInSection/sizeof(pint_t);
+ const uint32_t symbolIndex = A::P::E::get32(fReader.fIndirectTable[indirectTableOffset+sectionIndex]);
+ // return pointer to symbol name which this non-lazy-pointer will point to
+ if ( relocTarget != NULL )
+ *relocTarget = (uintptr_t)&fReader.fStrings[fReader.fSymbols[symbolIndex].n_strx()];
+ }
+ else {
+ if ( relocTarget != NULL )
+ *relocTarget = relocated(offsetOfAddrInSection, sect->reloff(), sect->nreloc());
+ }
+ return fMappingStart + sect->offset() + offsetOfAddrInSection;
+ }
+ }
+ throwf("ObjectFileAddressSpace::mappedAddress(0x%0lX) not in any section", (long)addr);
+ }
+}
+
+
+
+
+template <typename A>
+uint8_t ObjectFileAddressSpace<A>::get8(pint_t logicalAddr)
+{
+ return *((uint8_t*)mappedAddress(logicalAddr));
+}
+
+template <typename A>
+uint16_t ObjectFileAddressSpace<A>::get16(pint_t logicalAddr)
+{
+ return P::E::get16(*((uint16_t*)mappedAddress(logicalAddr)));
+}
+
+template <typename A>
+uint32_t ObjectFileAddressSpace<A>::get32(pint_t logicalAddr)
+{
+ pint_t relocTarget;
+ return P::E::get32(*((uint32_t*)mappedAddress(logicalAddr, &relocTarget))) + relocTarget;
+}
+
+template <typename A>
+uint64_t ObjectFileAddressSpace<A>::get64(pint_t logicalAddr)
+{
+ pint_t relocTarget;
+ return P::E::get64(*((uint64_t*)mappedAddress(logicalAddr, &relocTarget))) + relocTarget;
+}
+
+template <typename A>
+typename A::P::uint_t ObjectFileAddressSpace<A>::getP(pint_t logicalAddr)
+{
+ pint_t relocTarget;
+ return P::getP(*((pint_t*)mappedAddress(logicalAddr, &relocTarget))) + relocTarget;
+}
+
+template <typename A>
+uint64_t ObjectFileAddressSpace<A>::getULEB128(pint_t& logicalAddr, pint_t end)
+{
+ uintptr_t size = (end - logicalAddr);
+ libunwind::LocalAddressSpace::pint_t laddr = (libunwind::LocalAddressSpace::pint_t)mappedAddress(logicalAddr);
+ libunwind::LocalAddressSpace::pint_t sladdr = laddr;
+ uint64_t result = libunwind::LocalAddressSpace::getULEB128(laddr, laddr+size);
+ logicalAddr += (laddr-sladdr);
+ return result;
+}
+
+template <typename A>
+int64_t ObjectFileAddressSpace<A>::getSLEB128(pint_t& logicalAddr, pint_t end)
+{
+ uintptr_t size = (end - logicalAddr);
+ libunwind::LocalAddressSpace::pint_t laddr = (libunwind::LocalAddressSpace::pint_t)mappedAddress(logicalAddr);
+ libunwind::LocalAddressSpace::pint_t sladdr = laddr;
+ int64_t result = libunwind::LocalAddressSpace::getSLEB128(laddr, laddr+size);
+ logicalAddr += (laddr-sladdr);
+ return result;
+}
+
+
+
+
+
+
+template <typename A>
+class Reader : public ObjectFile::Reader
+{
+public:
+ static bool validFile(const uint8_t* fileContent, bool subtypeMustMatch=false, cpu_subtype_t subtype=0);
+ Reader(const uint8_t* fileContent, const char* path, time_t modTime,
+ const ObjectFile::ReaderOptions& options, uint32_t ordinalBase);
+ virtual ~Reader() {}
+
+ virtual const char* getPath() { return fPath; }
+ virtual time_t getModificationTime() { return fModTime; }
+ virtual ObjectFile::Reader::DebugInfoKind getDebugInfoKind() { return fDebugInfo; }
+ virtual std::vector<class ObjectFile::Atom*>& getAtoms() { return (std::vector<class ObjectFile::Atom*>&)(fAtoms); }
+ virtual std::vector<class ObjectFile::Atom*>* getJustInTimeAtomsFor(const char* name) { return NULL; }
+ virtual std::vector<Stab>* getStabs() { return &fStabs; }
+ virtual ObjectFile::Reader::ObjcConstraint getObjCConstraint() { return fObjConstraint; }
+ virtual uint32_t updateCpuConstraint(uint32_t current);
+ virtual bool canScatterAtoms() { return (fHeader->flags() & MH_SUBSECTIONS_VIA_SYMBOLS); }
+ virtual bool objcReplacementClasses(){ return fReplacementClasses; }
+ virtual bool hasLongBranchStubs() { return fHasLongBranchStubs; }
+
+ bool getTranslationUnitSource(const char** dir, const char** name) const;
+
+private:
+ typedef typename A::P P;
+ typedef typename A::P::E E;
+ typedef typename A::P::uint_t pint_t;
+ //typedef typename std::vector<Atom<A>*> AtomVector;
+ //typedef typename AtomVector::iterator AtomVectorIterator; // seems to help C++ parser
+ typedef typename A::ReferenceKinds Kinds;
+ typedef typename libunwind::CFI_Parser<ObjectFileAddressSpace<A> >::FDE_Atom_Info FDE_Atom_Info;
+ typedef typename libunwind::CFI_Parser<ObjectFileAddressSpace<A> >::CIE_Atom_Info CIE_Atom_Info;
+ typedef class ObjectFileAddressSpace<A> OAS;
+ friend class ObjectFileAddressSpace<A>;
+ friend class AnonymousAtom<A>;
+ friend class TentativeAtom<A>;
+ friend class AbsoluteAtom<A>;
+ friend class SymbolAtom<A>;
+ typedef std::map<pint_t, BaseAtom*> AddrToAtomMap;
+
+ void addReferencesForSection(const macho_section<P>* sect);
+ bool addRelocReference(const macho_section<P>* sect, const macho_relocation_info<P>* reloc);
+ bool addRelocReference_powerpc(const macho_section<P>* sect, const macho_relocation_info<P>* reloc);
+ const char* getDwarfString(uint64_t form, const uint8_t* p);
+ bool read_comp_unit(const char ** name, const char ** comp_dir, uint64_t *stmt_list);
+ static bool isWeakImportSymbol(const macho_nlist<P>* sym);
+ static bool skip_form(const uint8_t ** offset, const uint8_t * end, uint64_t form, uint8_t addr_size, bool dwarf64);
+ static const char* assureFullPath(const char* path);
+ AtomAndOffset findAtomAndOffset(pint_t addr);
+ AtomAndOffset findAtomAndOffset(pint_t baseAddr, pint_t realAddr);
+ Reference<A>* makeReference(Kinds kind, pint_t atAddr, pint_t toAddr);
+ Reference<A>* makeReference(Kinds kind, pint_t atAddr, pint_t fromAddr, pint_t toAddr);
+ Reference<A>* makeReferenceWithToBase(Kinds kind, pint_t atAddr, pint_t toAddr, pint_t toBaseAddr);
+ Reference<A>* makeReferenceWithToBase(Kinds kind, pint_t atAddr, pint_t fromAddr, pint_t toAddr, pint_t toBaseAddr);
+ Reference<A>* makeByNameReference(Kinds kind, pint_t atAddr, const char* toName, uint32_t toOffset);
+ BaseAtom* makeReferenceToEH(const char* ehName, pint_t ehAtomAddress, const macho_section<P>* ehSect);
+ Reference<A>* makeReferenceToSymbol(Kinds kind, pint_t atAddr, const macho_nlist<P>* toSymbol, pint_t toOffset);
+ void validSectionType(uint8_t type);
+ void addDtraceExtraInfos(uint32_t probeAddr, const char* providerName);
+ void setCpuConstraint(uint32_t cpusubtype);
+ const macho_section<P>* getSectionForAddress(pint_t);
+ ObjectFile::Atom* getFunctionAtomFromFDEAddress(pint_t);
+ ObjectFile::Atom* getFunctionAtomFromLSDAAddress(pint_t);
+ void addFdeReference(uint8_t encoding, AtomAndOffset inFDE, AtomAndOffset target);
+ void addCiePersonalityReference(BaseAtom* cieAtom, uint32_t offsetInCIE, uint8_t encoding);
+ bool isSectDiffReloc(uint8_t r_type);
+
+
+ BaseAtom* findAtomByName(const char*);
+
+ const char* fPath;
+ time_t fModTime;
+ uint32_t fOrdinalBase;
+ const ObjectFile::ReaderOptions& fOptions;
+ const macho_header<P>* fHeader;
+ const char* fStrings;
+ const macho_nlist<P>* fSymbols;
+ uint32_t fSymbolCount;
+ const macho_segment_command<P>* fSegment;
+ const uint32_t* fIndirectTable;
+ std::vector<BaseAtom*> fAtoms;
+ AddrToAtomMap fAddrToAtom;
+ AddrToAtomMap fAddrToAbsoluteAtom;
+ std::vector<class AnonymousAtom<A>*> fLocalNonLazys;
+ std::vector<class AnonymousAtom<A>*> fAtomsPendingAName;
+ std::set<const macho_section<P>*> fSectionsWithAtomsPendingAName;
+ std::vector<const char*> fDtraceProviderInfo;
+ ObjectFile::Reader::DebugInfoKind fDebugInfo;
+ bool fHasUUID;
+ const macho_section<P>* fehFrameSection;
+ std::set<BaseAtom*> fLSDAAtoms;
+ const macho_section<P>* fDwarfDebugInfoSect;
+ const macho_section<P>* fDwarfDebugAbbrevSect;
+ const macho_section<P>* fDwarfDebugLineSect;
+ const macho_section<P>* fDwarfDebugStringSect;
+ const char* fDwarfTranslationUnitDir;
+ const char* fDwarfTranslationUnitFile;
+ std::map<uint32_t,const char*> fDwarfIndexToFile;
+ std::vector<Stab> fStabs;
+ std::vector<FDE_Atom_Info> fFDEInfos;
+ std::vector<CIE_Atom_Info> fCIEInfos;
+ bool fAppleObjc;
+ bool fHasDTraceProbes;
+ bool fHaveIndirectSymbols;
+ bool fReplacementClasses;
+ bool fHasLongBranchStubs;
+ ObjectFile::Reader::ObjcConstraint fObjConstraint;
+ uint32_t fCpuConstraint;
+ const macho_section<P>* fSectionsStart;
+ const macho_section<P>* fSectionsEnd;
+ OAS fObjectAddressSpace;
+};
+
+template <typename A>
+Reader<A>::Reader(const uint8_t* fileContent, const char* path, time_t modTime, const ObjectFile::ReaderOptions& options, uint32_t ordinalBase)
+ : fPath(strdup(path)), fModTime(modTime), fOrdinalBase(ordinalBase), fOptions(options), fHeader((const macho_header<P>*)fileContent),
+ fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fSegment(NULL), fIndirectTable(NULL),
+ fDebugInfo(kDebugInfoNone), fHasUUID(false), fehFrameSection(NULL),
+ fDwarfDebugInfoSect(NULL), fDwarfDebugAbbrevSect(NULL), fDwarfDebugLineSect(NULL),
+ fDwarfTranslationUnitDir(NULL), fDwarfTranslationUnitFile(NULL), fAppleObjc(false), fHasDTraceProbes(false),
+ fHaveIndirectSymbols(false), fReplacementClasses(false), fHasLongBranchStubs(false),
+ fObjConstraint(ObjectFile::Reader::kObjcNone), fCpuConstraint(ObjectFile::Reader::kCpuAny),
+ fSectionsStart(NULL), fSectionsEnd(NULL), fObjectAddressSpace(*this)
+{
+ // sanity check
+ if ( ! validFile(fileContent, false, 0) )
+ throw "not a valid mach-o object file";
+
+ Reference<A>::fgForFinalLinkedImage = options.fForFinalLinkedImage;
+
+ // write out path for -t or -whatsloaded option
+ if ( options.fLogObjectFiles || options.fLogAllFiles )
+ printf("%s\n", path);
+
+ // cache intersting pointers
+ const macho_header<P>* header = (const macho_header<P>*)fileContent;
+ this->setCpuConstraint(header->cpusubtype());
+ 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>* const cmdsEnd = (macho_load_command<P>*)((char*)header + sizeof(macho_header<P>) + header->sizeofcmds());
+ 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_symtab_command<P>* symtab = (macho_symtab_command<P>*)cmd;
+ fSymbolCount = symtab->nsyms();
+ fSymbols = (const macho_nlist<P>*)((char*)header + symtab->symoff());
+ fStrings = (char*)header + symtab->stroff();
+ if ( undefinedEndIndex == 0 ) {
+ undefinedStartIndex = 0;
+ undefinedEndIndex = symtab->nsyms();
+ }
+ }
+ break;
+ case LC_DYSYMTAB:
+ {
+ 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:
+ fHasUUID = true;
+ break;
+
+ default:
+ if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
+ fSegment = (macho_segment_command<P>*)cmd;
+ }
+ break;
+ }
+ cmd = (const macho_load_command<P>*)(((char*)cmd)+cmd->cmdsize());
+ if ( cmd > cmdsEnd )
+ throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, path);
+ }
+
+ // if there are no load commands, then this file has no content, so no atoms
+ if ( header->ncmds() < 1 )
+ return;
+
+ fSectionsStart = (macho_section<P>*)((char*)fSegment + sizeof(macho_segment_command<P>));
+ fSectionsEnd = &fSectionsStart[fSegment->nsects()];
+
+ // inital guess for number of atoms
+ fAtoms.reserve(fSymbolCount);
+
+ // if there is an __eh_frame section, decode it into chunks to get atoms in that
+ // section as well as division points for functions in __text
+ for (const macho_section<P>* sect=fSectionsStart; sect < fSectionsEnd; ++sect) {
+ if ( (strcmp(sect->sectname(), "__eh_frame") == 0) && (strcmp(sect->segname(), "__TEXT") == 0) ) {
+ fehFrameSection = sect;
+ const char* msg = libunwind::CFI_Parser<ObjectFileAddressSpace<A> >::getCFIs(fObjectAddressSpace, sect->addr(),
+ sect->size(), fFDEInfos, fCIEInfos);
+ if ( msg != NULL ) {
+ throwf("malformed __eh_frame section: %s", msg);
+ }
+ else {
+ //fprintf(stderr, "%lu CIEs, %lu FDEs\n", fCIEInfos.size(), fFDEInfos.size());
+ // add anonymous atoms for each CIE
+ for (typename std::vector<CIE_Atom_Info>::const_iterator it = fCIEInfos.begin(); it != fCIEInfos.end(); ++it) {
+ AnonymousAtom<A>* cieAtom = new AnonymousAtom<A>(*this, sect, it->cieAddress, 1);
+ fAtoms.push_back(cieAtom);
+ fAddrToAtom[it->cieAddress] = cieAtom;
+ }
+ // add anonymous atoms for each FDE and LSDA
+ for (typename std::vector<FDE_Atom_Info>::const_iterator it = fFDEInfos.begin(); it != fFDEInfos.end(); ++it) {
+ //fprintf(stderr, "fdeAddress=0x%08llX, lsdaAddr=0x%08llX, funcAddr=0x%08llX\n", (uint64_t)it->fdeAddress, (uint64_t)it->lsda.address, (uint64_t)it->function.address);
+ AnonymousAtom<A>* fdeAtom = new AnonymousAtom<A>(*this, sect, it->fdeAddress, 0);
+ fAtoms.push_back(fdeAtom);
+ fAddrToAtom[it->fdeAddress] = fdeAtom;
+ if ( it->lsda.address != 0 ) {
+ AnonymousAtom<A>* lsdaAtom = new AnonymousAtom<A>(*this, getSectionForAddress(it->lsda.address), it->lsda.address, 0);
+ fAtoms.push_back(lsdaAtom);
+ fAddrToAtom[it->lsda.address] = lsdaAtom;
+ fLSDAAtoms.insert(lsdaAtom);
+ }
+ }
+ }
+ }
+ }
+
+
+ // add all atoms that have entries in symbol table
+ BaseAtom* sectionEndAtoms[fSegment->nsects()];
+ for (unsigned int i=0; i < fSegment->nsects(); ++i)
+ sectionEndAtoms[i] = NULL;
+ for (int i=fSymbolCount-1; i >= 0 ; --i) {
+ // walk backwards through symbol table so globals are see before locals, otherwise a local alias would beome the real name
+ const macho_nlist<P>& sym = fSymbols[i];
+ if ( (sym.n_type() & N_STAB) == 0 ) {
+ uint8_t type = (sym.n_type() & N_TYPE);
+ if ( type == N_SECT ) {
+ const macho_section<P>* section = &fSectionsStart[sym.n_sect()-1];
+ const pint_t sectionStartAddr = section->addr();
+ const pint_t sectionEndAddr = sectionStartAddr + section->size();
+ bool suppress = false;
+ // ignore atoms in debugger sections
+ if ( (section->flags() & S_ATTR_DEBUG) == 0 ) {
+ if ( strncmp(&fStrings[sym.n_strx()], "__dtrace_probe$", 15) == 0 ) {
+ // ignore dtrace probe labels
+ fHasDTraceProbes = true;
+ }
+ else if ( fStrings[sym.n_strx()] == 'L' ) {
+ // ignore L labels, <rdar://problem/3962731>
+ }
+ else if ( section == fehFrameSection ) {
+ // ignore labels in __eh_frame section
+ }
+ else {
+ // ignore labels for atoms in other sections
+ switch ( section->flags() & SECTION_TYPE ) {
+ case S_REGULAR:
+ if ( (sym.n_desc() & N_WEAK_DEF) && strcmp(section->sectname(), "__picsymbolstub1__TEXT") == 0 )
+ suppress = true; // ignore stubs in crt1.o built by old ld64 that was missing S_SYMBOL_STUBS
+ case S_ZEROFILL:
+ case S_COALESCED:
+ case S_4BYTE_LITERALS:
+ case S_8BYTE_LITERALS:
+ case S_16BYTE_LITERALS:
+ case S_CSTRING_LITERALS:
+ {
+ BaseAtom* newAtom;
+ typename AddrToAtomMap::iterator pos = fAddrToAtom.find(sym.n_value());
+ if ( (pos != fAddrToAtom.end()) && (pos->second->getSectionRecord() == section) ) {
+ if ( fLSDAAtoms.count(pos->second) != 0 ) {
+ // already have LSDA atom from for this address, ignore compiler's label
+ suppress = true;
+ break;
+ }
+ else {
+ // another label to an existing address in the same section, make this an alias
+ newAtom = new SymbolAliasAtom<A>(&fStrings[sym.n_strx()], &sym, *pos->second);
+ }
+ }
+ else {
+ if ( sym.n_value() == sectionEndAddr ) {
+ // Symbol address is at end of section. This can interfere
+ // with a symbol at the start of the next section, so don't
+ // add to fAddrToAtom. But do track in sectionEndAtoms so we
+ // still make aliases if there are duplicates.
+ if ( sectionEndAtoms[sym.n_sect()-1] == NULL ) {
+ newAtom = new SymbolAtom<A>(*this, &sym, section);
+ sectionEndAtoms[sym.n_sect()-1] = newAtom;
+ // if this is a zero length section, so add to fAddrToAtom
+ if ( sym.n_value() == sectionStartAddr )
+ fAddrToAtom[newAtom->getObjectAddress()] = newAtom;
+ }
+ else {
+ newAtom = new SymbolAliasAtom<A>(&fStrings[sym.n_strx()], &sym, *sectionEndAtoms[sym.n_sect()-1]);
+ }
+ }
+ else {
+ // make SymbolAtom atom for this address
+ newAtom = new SymbolAtom<A>(*this, &sym, section);
+ fAddrToAtom[newAtom->getObjectAddress()] = newAtom;
+ }
+ }
+ if ( ! suppress )
+ fAtoms.push_back(newAtom);
+ }
+ break;
+ case S_SYMBOL_STUBS:
+ case S_LAZY_SYMBOL_POINTERS:
+ case S_NON_LAZY_SYMBOL_POINTERS:
+ // ignore symboled stubs produces by old ld64
+ break;
+ default:
+ warning("symbol %s found in unsupported section in %s",
+ &fStrings[sym.n_strx()], this->getPath());
+ }
+ }
+ }
+ }
+ else if ( (type == N_UNDF) && (sym.n_value() != 0) ) {
+ fAtoms.push_back(new TentativeAtom<A>(*this, &sym));
+ }
+ else if ( type == N_ABS ) {
+ const char* symName = &fStrings[sym.n_strx()];
+ if ( strncmp(symName, ".objc_class_name_", 17) == 0 ) {
+ // ignore .objc_class_name_* symbols
+ fAppleObjc = true;
+ }
+ else if ( strcmp(&symName[strlen(symName)-3], ".eh") == 0 ) {
+ // ignore empty *.eh symbols
+ }
+ else {
+ BaseAtom* abAtom = new AbsoluteAtom<A>(*this, &sym);
+ fAtoms.push_back(abAtom);
+ fAddrToAbsoluteAtom[sym.n_value()] = abAtom;
+ }
+ }
+ else if ( type == N_INDR ) {
+ fHaveIndirectSymbols = true;
+ }
+ }
+ }
+
+ // add anonymous atoms for any functions (as determined by dwarf unwind) have no symbol names
+ if ( fehFrameSection != NULL ) {
+ for (typename std::vector<FDE_Atom_Info>::const_iterator it = fFDEInfos.begin(); it != fFDEInfos.end(); ++it) {
+ // add if not already an atom at that address
+ if ( fAddrToAtom.find(it->function.address) == fAddrToAtom.end() ) {
+ AnonymousAtom<A>* funcAtom = new AnonymousAtom<A>(*this, getSectionForAddress(it->function.address), it->function.address, 0);
+ fAtoms.push_back(funcAtom);
+ fAddrToAtom[it->function.address] = funcAtom;
+ // even though we've made a new atom, be conservative and make sure they lay out together
+ if ( canScatterAtoms() ) {
+ AtomAndOffset prev = findAtomAndOffset(it->function.address-1);
+ if ( prev.atom != NULL ) {
+ if ( ((BaseAtom*)(prev.atom))->getSectionRecord() == funcAtom->getSectionRecord() )
+ new Reference<A>(A::kFollowOn, prev, AtomAndOffset(funcAtom));
+ }
+ }
+ }
+ }
+ }
+
+
+ // add all fixed size anonymous atoms from special sections
+ for (const macho_section<P>* sect=fSectionsStart; sect < fSectionsEnd; ++sect) {
+ pint_t atomSize = 0;
+ uint8_t type (sect->flags() & SECTION_TYPE);
+ validSectionType(type);
+ bool suppress = false;
+ switch ( type ) {
+ case S_SYMBOL_STUBS:
+ suppress = true;
+ atomSize = sect->reserved2();
+ break;
+ case S_LAZY_SYMBOL_POINTERS:
+ suppress = true;
+ atomSize = sizeof(pint_t);
+ break;
+ case S_NON_LAZY_SYMBOL_POINTERS:
+ case S_LITERAL_POINTERS:
+ case S_MOD_INIT_FUNC_POINTERS:
+ case S_MOD_TERM_FUNC_POINTERS:
+ atomSize = sizeof(pint_t);
+ break;
+ case S_INTERPOSING:
+ atomSize = sizeof(pint_t)*2;
+ break;
+ case S_4BYTE_LITERALS:
+ atomSize = 4;
+ break;
+ case S_8BYTE_LITERALS:
+ atomSize = 8;
+ break;
+ 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);
+ }
+ // get objc Garbage Collection info
+ else if ( ((strcmp(sect->sectname(), "__image_info") == 0) && (strcmp(sect->segname(), "__OBJC") == 0))
+ || ((strncmp(sect->sectname(), "__objc_imageinfo", 16) == 0) && (strcmp(sect->segname(), "__DATA") == 0)) ) {
+ // struct objc_image_info {
+ // uint32_t version; // initially 0
+ // uint32_t flags;
+ // };
+ // #define OBJC_IMAGE_SUPPORTS_GC 2
+ // #define OBJC_IMAGE_GC_ONLY 4
+ //
+ const uint32_t* contents = (uint32_t*)(((char*)fHeader) + sect->offset());
+ if ( (sect->size() >= 8) && (contents[0] == 0) ) {
+ uint32_t flags = E::get32(contents[1]);
+ if ( (flags & 4) == 4 )
+ fObjConstraint = ObjectFile::Reader::kObjcGC;
+ else if ( (flags & 2) == 2 )
+ fObjConstraint = ObjectFile::Reader::kObjcRetainReleaseOrGC;
+ else
+ fObjConstraint = ObjectFile::Reader::kObjcRetainRelease;
+ if ( (flags & 1) == 1 )
+ fReplacementClasses = true;
+ // don't make atom for this section
+ atomSize = sect->size();
+ suppress = true;
+ }
+ else {
+ warning("can't parse __OBJC/__image_info section in %s", fPath);
+ }
+ }
+ // special case constant NS/CFString literals and make an atom out of each one
+ else if ((strcmp(sect->sectname(), "__cfstring") == 0) && (strcmp(sect->segname(), "__DATA") == 0)) {
+ atomSize = 4 * sizeof(pint_t);
+ }
+ break;
+ }
+ if ( atomSize != 0 ) {
+ for(pint_t sectOffset=0; sectOffset < sect->size(); sectOffset += atomSize) {
+ pint_t atomAddr = sect->addr() + sectOffset;
+ // add if not already an atom at that address
+ if ( fAddrToAtom.find(atomAddr) == fAddrToAtom.end() ) {
+ AnonymousAtom<A>* newAtom = new AnonymousAtom<A>(*this, sect, atomAddr, atomSize);
+ if ( !suppress )
+ fAtoms.push_back(newAtom);
+ fAddrToAtom[atomAddr] = newAtom->redirectTo();
+ }
+ }
+ }
+ }
+
+ // add all c-string anonymous atoms
+ for (const macho_section<P>* sect=fSectionsStart; sect < fSectionsEnd; ++sect) {
+ if ( ((sect->flags() & SECTION_TYPE) == S_CSTRING_LITERALS) || strcmp(sect->sectname(), "__cstring") == 0 ) {
+ uint32_t stringLen;
+ pint_t stringAddr;
+ BaseAtom* mostAlignedEmptyString = NULL;
+ uint32_t mostAlignedEmptyStringTrailingZeros = 0;
+ std::vector<std::pair<pint_t,BaseAtom*> > emptyStrings;
+ for(pint_t sectOffset=0; sectOffset < sect->size(); sectOffset += stringLen) {
+ stringAddr = sect->addr() + sectOffset;
+ stringLen = strlen((char*)(fHeader) + sect->offset() + sectOffset) + 1;
+ // add if not already an atom at that address
+ if ( fAddrToAtom.find(stringAddr) == fAddrToAtom.end() ) {
+ BaseAtom* newAtom = new AnonymousAtom<A>(*this, sect, stringAddr, stringLen);
+ if ( stringLen == 1 ) {
+ // because of padding it may look like there are lots of empty strings, keep track of all
+ emptyStrings.push_back(std::make_pair<pint_t,BaseAtom*>(stringAddr, newAtom));
+ // record empty string with greatest alignment requirement
+ uint32_t stringAddrTrailingZeros = (stringAddr==0) ? sect->align() : __builtin_ctz(stringAddr);
+ if ( (mostAlignedEmptyString == NULL)
+ || ( stringAddrTrailingZeros > mostAlignedEmptyStringTrailingZeros) ) {
+ mostAlignedEmptyString = newAtom;
+ mostAlignedEmptyStringTrailingZeros = stringAddrTrailingZeros;
+ }
+ }
+ else {
+ fAtoms.push_back(newAtom);
+ fAddrToAtom[stringAddr] = newAtom;
+ }
+ }
+ }
+ // map all uses of empty strings to the most aligned one
+ if ( mostAlignedEmptyString != NULL ) {
+ // make most aligned atom a real atom
+ fAtoms.push_back(mostAlignedEmptyString);
+ // map all other empty atoms to this one
+ for (typename std::vector<std::pair<pint_t,BaseAtom*> >::iterator it=emptyStrings.begin(); it != emptyStrings.end(); it++) {
+ fAddrToAtom[it->first] = mostAlignedEmptyString;
+ }
+ }
+ }
+ }
+
+ // sort all atoms so far by address and section
+ std::sort(fAtoms.begin(), fAtoms.end(), BaseAtomSorter());
+
+ //fprintf(stderr, "sorted atoms:\n");
+ //for (std::vector<BaseAtom*>::iterator it=fAtoms.begin(); it != fAtoms.end(); it++)
+ // fprintf(stderr, "0x%08llX %s\n", (*it)->getObjectAddress(), (*it)->getDisplayName());
+
+ // create atoms to cover any non-debug ranges not handled above
+ for (const macho_section<P>* sect=fSectionsStart; sect < fSectionsEnd; ++sect) {
+ pint_t sectionStartAddr = sect->addr();
+ pint_t sectionEndAddr = sect->addr() + sect->size();
+ // don't set follow-on atoms in __eh_frame section
+ const bool setFollowOnAtom = !canScatterAtoms() && (sect != fehFrameSection);
+ if ( sect->size() != 0 ) {
+ // ignore dwarf sections. If ld every supports processing dwarf, this logic will need to change
+ if ( (sect->flags() & S_ATTR_DEBUG) != 0 ) {
+ fDebugInfo = kDebugInfoDwarf;
+ if ( strcmp(sect->sectname(), "__debug_info") == 0 )
+ fDwarfDebugInfoSect = sect;
+ else if ( strcmp(sect->sectname(), "__debug_abbrev") == 0 )
+ fDwarfDebugAbbrevSect = sect;
+ else if ( strcmp(sect->sectname(), "__debug_line") == 0 )
+ fDwarfDebugLineSect = sect;
+ else if ( strcmp(sect->sectname(), "__debug_str") == 0 )
+ fDwarfDebugStringSect = sect;
+ }
+ else {
+ if ( strcmp(sect->segname(), "__DWARFA") == 0 ) {
+ throw "object file contains old DWARF debug info - rebuild with newer compiler";
+ }
+ uint8_t type (sect->flags() & SECTION_TYPE);
+ switch ( type ) {
+ case S_REGULAR:
+ case S_ZEROFILL:
+ case S_COALESCED:
+ // if there is not an atom already at the start of this section, add an anonymous one
+ pint_t previousAtomAddr = 0;
+ BaseAtom* previousAtom = NULL;
+ if ( fAddrToAtom.find(sectionStartAddr) == fAddrToAtom.end() ) {
+ BaseAtom* newAtom = new AnonymousAtom<A>(*this, sect, sect->addr(), 0);
+ fAddrToAtom[sect->addr()] = newAtom;
+ fAtoms.push_back(newAtom);
+ previousAtomAddr = sectionStartAddr;
+ previousAtom = newAtom;
+ std::sort(fAtoms.begin(), fAtoms.end(), BaseAtomSorter());
+ }
+ // calculate size of all atoms in this section and add follow-on references
+ for (std::vector<BaseAtom*>::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) {
+ BaseAtom* atom = (BaseAtom*)(*it);
+ pint_t atomAddr = atom->getObjectAddress();
+ if ( atom->getSectionRecord() == sect ) {
+ //fprintf(stderr, "addr=0x%08llX, atom=%s\n", (uint64_t)atomAddr, atom->getDisplayName());
+ if ( (previousAtom != NULL) && (previousAtomAddr != atomAddr) ) {
+ previousAtom->setSize(atomAddr - previousAtomAddr);
+ if ( setFollowOnAtom && (atom != previousAtom) )
+ new Reference<A>(A::kFollowOn, AtomAndOffset(previousAtom), AtomAndOffset(atom));
+ }
+ previousAtomAddr = atomAddr;
+ previousAtom = atom;
+ }
+ }
+ if ( previousAtom != NULL ) {
+ // set last atom in section
+ previousAtom->setSize(sectionEndAddr - previousAtomAddr);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ // check for object file that defines no objc classes, but uses objc classes
+ // check for dtrace provider info
+ 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 ) {
+ const char* undefinedName = &fStrings[sym.n_strx()];
+ if ( !fAppleObjc && (strncmp(undefinedName, ".objc_class_name_", 17) == 0) ) {
+ fAppleObjc = true;
+ }
+ else if ( strncmp(undefinedName, "___dtrace_", 10) == 0 ) {
+ if ( strchr(undefinedName, '$') != NULL ) {
+ if ( (strncmp(&undefinedName[10], "probe$", 6) != 0) && (strncmp(&undefinedName[10], "isenabled$", 10) != 0) ) {
+ // any undefined starting with __dtrace_*$ that is not ___dtrace_probe$* or ___dtrace_isenabled$*
+ // is extra provider info
+ fDtraceProviderInfo.push_back(undefinedName);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // add relocation based references to sections that have atoms with pending names
+ for (const macho_section<P>* sect=fSectionsStart; sect < fSectionsEnd; ++sect) {
+ if ( fSectionsWithAtomsPendingAName.count(sect) != 0 )
+ addReferencesForSection(sect);
+ }
+
+ // update any anonymous atoms that need references built in order to name themselves
+ for (typename std::vector<AnonymousAtom<A>*>::iterator it=fAtomsPendingAName.begin(); it != fAtomsPendingAName.end(); it++) {
+ (*it)->resolveName();
+ }
+
+ // add relocation based references to other sections
+ for (const macho_section<P>* sect=fSectionsStart; sect < fSectionsEnd; ++sect) {
+ if ( fSectionsWithAtomsPendingAName.count(sect) == 0 )
+ addReferencesForSection(sect);
+ }
+
+ // add objective-c references
+ if ( fAppleObjc ) {
+ for (const macho_section<P>* sect=fSectionsStart; sect < fSectionsEnd; ++sect) {
+ if ( (strcmp(sect->sectname(), "__cls_refs") == 0) && (strcmp(sect->segname(), "__OBJC") == 0) ) {
+ for (uint32_t offset = 0; offset < sect->size(); offset += sizeof(pint_t)) {
+ AtomAndOffset ao = this->findAtomAndOffset(sect->addr()+offset);
+ ObjectFile::Reference* classRef = ao.atom->getReferences()[0];
+ if ( classRef->getFixUpOffset() == 0 ) {
+ const char* classStr = classRef->getTargetName();
+ if ( strncmp(classStr, "cstring=", 8) == 0 ) {
+ const char* className;
+ asprintf((char**)&className, ".objc_class_name_%s", &classStr[8]);
+ new Reference<A>(A::kNoFixUp, ao, 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;
+ uint32_t fileOffset = localNonLazy->fSection->offset() - localNonLazy->fSection->addr() + localNonLazy->fAddress;
+ pint_t nonLazyPtrValue = P::getP(*((pint_t*)((char*)(fHeader)+fileOffset)));
+ makeReference(A::kPointer, localNonLazy->fAddress, nonLazyPtrValue);
+ }
+
+
+ // add personality references to CIEs
+ for (typename std::vector<CIE_Atom_Info>::const_iterator it = fCIEInfos.begin(); it != fCIEInfos.end(); ++it) {
+ if ( it->personality.offsetInFDE != 0 )
+ addCiePersonalityReference(fAddrToAtom[it->cieAddress], it->personality.offsetInFDE, it->personality.encodingOfAddress);
+ }
+
+ // add all references for FDEs, including implicit group references
+ for (typename std::vector<FDE_Atom_Info>::const_iterator it = fFDEInfos.begin(); it != fFDEInfos.end(); ++it) {
+ AtomAndOffset funcAO = this->findAtomAndOffset(it->function.address);
+ if ( funcAO.offset != 0 )
+ warning("FDE does not point to start of function %s\n", funcAO.atom->getDisplayName());
+ AtomAndOffset fdeAO = this->findAtomAndOffset(it->fdeAddress);
+ if ( fdeAO.offset != 0 )
+ warning("FDE does start its own atom %s\n", funcAO.atom->getDisplayName());
+ AtomAndOffset cieAO = this->findAtomAndOffset(it->cie.address);
+ if ( cieAO.offset != 0 )
+ warning("CIE does start its own atom %s\n", cieAO.atom->getDisplayName());
+ AtomAndOffset lsdaAO;
+ if ( it->lsda.address != 0 ) {
+ lsdaAO = this->findAtomAndOffset(it->lsda.address);
+ if ( lsdaAO.offset != 0 )
+ warning("LSDA does start its own atom %s\n", lsdaAO.atom->getDisplayName());
+ }
+
+ // add reference from FDE to CIE
+ AtomAndOffset cieInfdeAO = AtomAndOffset(fdeAO.atom, it->cie.offsetInFDE);
+ new Reference<A>(A::kPointerDiff32, cieInfdeAO, cieAO, cieInfdeAO);
+
+ // add reference from FDE to function
+ addFdeReference(it->function.encodingOfAddress, AtomAndOffset(fdeAO.atom, it->function.offsetInFDE), funcAO);
+
+ // add reference from FDE to LSDA
+ if ( it->lsda.address != 0 ) {
+ addFdeReference(it->lsda.encodingOfAddress, AtomAndOffset(fdeAO.atom, it->lsda.offsetInFDE), lsdaAO);
+ }
+
+ // FDE is in group lead by function atom
+ new Reference<A>(A::kGroupSubordinate, funcAO, fdeAO);
+
+ // LSDA is in group lead by function atom
+ if ( it->lsda.address != 0 ) {
+ new Reference<A>(A::kGroupSubordinate, funcAO, lsdaAO);
+ // add back reference from LSDA to owning function
+ new Reference<A>(A::kNoFixUp, lsdaAO, funcAO);
+ }
+
+ // compute compact encoding for this FDE
+ if ( fOptions.fAddCompactUnwindEncoding ) {
+ ((BaseAtom*)(funcAO.atom))->setCompactUnwindEncoding(it->fdeAddress);
+ // add reference from function atom to personality function
+ // the only reference a CIE can have is the reference to the personality function
+ std::vector<class ObjectFile::Reference*>& cieRefs = cieAO.atom->getReferences();
+ if ( cieRefs.size() == 1 ) {
+ new Reference<A>((typename A::ReferenceKinds)((BaseAtom*)(funcAO.atom))->getPersonalityReferenceKind(),
+ funcAO, cieRefs[0]->getTargetName(), 0);
+ }
+ }
+ }
+
+ // add command line aliases
+ for(std::vector<ObjectFile::ReaderOptions::AliasPair>::const_iterator it = fOptions.fAliases.begin(); it != fOptions.fAliases.end(); ++it) {
+ BaseAtom* target = this->findAtomByName(it->realName);
+ if ( (target != NULL) && target->getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn )
+ fAtoms.push_back(new SymbolAliasAtom<A>(it->alias, NULL, *target));
+ }
+
+ // add dtrace probe locations
+ if ( fHasDTraceProbes ) {
+ for (uint32_t i=0; i < fSymbolCount; ++i) {
+ const macho_nlist<P>& sym = fSymbols[i];
+ if ( (sym.n_type() & N_STAB) == 0 ) {
+ if ( (sym.n_type() & N_TYPE) == N_SECT ) {
+ const char* symbolName = &fStrings[sym.n_strx()];
+ if ( strncmp(symbolName, "__dtrace_probe$", 15) == 0 ) {
+ //fprintf(stderr, "adding dtrace probe at 0x%08llX %s\n", sym.n_value(), symbolName);
+ makeByNameReference(A::kDtraceProbe, sym.n_value(), symbolName, 0);
+ }
+ }
+ }
+ }
+ }
+
+ // turn indirect symbols into SymbolAliasAtom
+ if ( fHaveIndirectSymbols ) {
+ for (uint32_t i=0; i < fSymbolCount; ++i) {
+ const macho_nlist<P>& sym = fSymbols[i];
+ if ( (sym.n_type() & N_STAB) == 0 ) {
+ if ( (sym.n_type() & N_TYPE) == N_INDR ) {
+ const char* aliasName = &fStrings[sym.n_strx()];
+ const char* targetName = &fStrings[sym.n_value()];
+ //fprintf(stderr, "found alias %s for %s\n", aliasName, targetName);
+ BaseAtom* target = this->findAtomByName(targetName);
+ // only currently support N_INDR based aliases to something in the same .o file
+ if ( target != NULL ) {
+ fAtoms.push_back(new SymbolAliasAtom<A>(aliasName, &sym, *target));
+ //fprintf(stderr, "creating alias %s for %s\n", aliasName, targetName);
+ }
+ }
+ }
+ }
+ }
+
+ //for (typename AddrToAtomMap::iterator it=fAddrToAtom.begin(); it != fAddrToAtom.end(); it++) {
+ // fprintf(stderr, "[0x%0X -> 0x%0llX) : %s\n", it->first, it->first+it->second->getSize(), it->second->getDisplayName());
+ //}
+
+ // add translation unit info from dwarf
+ uint64_t stmtList;
+ if ( (fDebugInfo == kDebugInfoDwarf) && (fOptions.fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone) ) {
+ // 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;
+ warning("can't parse dwarf compilation unit info in %s", this->getPath());
+ fDebugInfo = kDebugInfoNone;
+ }
+ }
+ }
+
+ // add line number info to atoms from dwarf
+ if ( (fDebugInfo == kDebugInfoDwarf) && (fOptions.fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone) ) {
+ // file with just data will have no __debug_line info
+ if ( (fDwarfDebugLineSect != NULL) && (fDwarfDebugLineSect->size() != 0) && (fAddrToAtom.size() != 0)
+ && (fDwarfDebugInfoSect != NULL) && (fDwarfDebugInfoSect->size() != 0) ) {
+ // validate stmt_list
+ if ( (stmtList != (uint64_t)-1) && (stmtList < fDwarfDebugLineSect->size()) ) {
+ const uint8_t* debug_line = (uint8_t*)(fHeader) + fDwarfDebugLineSect->offset();
+ if ( debug_line != NULL ) {
+ struct line_reader_data* lines = line_open(&debug_line[stmtList],
+ fDwarfDebugLineSect->size() - stmtList, E::little_endian);
+ struct line_info result;
+ ObjectFile::Atom* curAtom = NULL;
+ uint32_t curAtomOffset = 0;
+ uint32_t curAtomAddress = 0;
+ uint32_t curAtomSize = 0;
+ if ( lines != NULL ) {
+ while ( line_next (lines, &result, line_stop_pc) ) {
+ //fprintf(stderr, "curAtom=%p, result.pc=0x%llX, result.line=%llu, result.end_of_sequence=%d, curAtomAddress=0x%X, curAtomSize=0x%X\n",
+ // curAtom, result.pc, result.line, result.end_of_sequence, curAtomAddress, curAtomSize);
+ // work around weird debug line table compiler generates if no functions in __text section
+ if ( (curAtom == NULL) && (result.pc == 0) && result.end_of_sequence && (result.file == 1))
+ continue;
+ // for performance, see if in next pc is in current atom
+ 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 {
+ // do slow look up of atom by address
+ AtomAndOffset ao = this->findAtomAndOffset(result.pc);
+ curAtom = ao.atom;
+ if ( curAtom == NULL )
+ break; // file has line info but no functions
+ if ( result.end_of_sequence && (curAtomAddress+curAtomSize < result.pc) ) {
+ // a one line function can be returned by line_next() as one entry with pc at end of blob
+ // look for alt atom starting at end of previous atom
+ uint32_t previousEnd = curAtomAddress+curAtomSize;
+ AtomAndOffset alt = this->findAtomAndOffset(previousEnd);
+ if ( result.pc <= previousEnd - alt.offset + alt.atom->getSize() ) {
+ curAtom = alt.atom;
+ curAtomOffset = alt.offset;
+ curAtomAddress = previousEnd - alt.offset;
+ curAtomSize = curAtom->getSize();
+ }
+ else {
+ curAtomOffset = ao.offset;
+ curAtomAddress = result.pc - ao.offset;
+ curAtomSize = curAtom->getSize();
+ }
+ }
+ else {
+ curAtomOffset = ao.offset;
+ curAtomAddress = result.pc - ao.offset;
+ curAtomSize = curAtom->getSize();
+ }
+ }
+ const char* filename;
+ std::map<uint32_t,const char*>::iterator pos = fDwarfIndexToFile.find(result.file);
+ if ( pos == fDwarfIndexToFile.end() ) {
+ filename = line_file(lines, result.file);
+ fDwarfIndexToFile[result.file] = filename;
+ }
+ else {
+ filename = pos->second;
+ }
+ ObjectFile::LineInfo info;
+ info.atomOffset = curAtomOffset;
+ info.fileName = filename;
+ info.lineNumber = result.line;
+ //fprintf(stderr, "addr=0x%08llX, line=%lld, file=%s, 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);
+ }
+ }
+ else {
+ warning("could not parse dwarf line number info in %s", this->getPath());
+ }
+ }
+ }
+ }
+
+ // if no dwarf, try processing stabs debugging info
+ if ( (fDebugInfo == kDebugInfoNone) && (fOptions.fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone) ) {
+ // scan symbol table for stabs entries
+ fStabs.reserve(fSymbolCount); // reduce re-allocations
+ BaseAtom* currentAtom = NULL;
+ pint_t currentAtomAddress = 0;
+ enum { start, inBeginEnd, inFun } state = start;
+ for (uint32_t symbolIndex = 0; symbolIndex < fSymbolCount; ++symbolIndex ) {
+ const macho_nlist<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 = (fHasUUID ? kDebugInfoStabsUUID : kDebugInfoStabs);
+ Stab stab;
+ stab.atom = NULL;
+ stab.type = type;
+ stab.other = sym->n_sect();
+ stab.desc = sym->n_desc();
+ stab.value = sym->n_value();
+ stab.string = NULL;
+ switch (state) {
+ case start:
+ switch (type) {
+ case N_BNSYM:
+ // beginning of function block
+ state = inBeginEnd;
+ // fall into case to lookup atom by addresss
+ case N_LCSYM:
+ case N_STSYM:
+ currentAtomAddress = sym->n_value();
+ currentAtom = (BaseAtom*)this->findAtomAndOffset(currentAtomAddress).atom;
+ if ( currentAtom != NULL ) {
+ stab.atom = currentAtom;
+ stab.string = symString;
+ }
+ else {
+ fprintf(stderr, "can't find atom for stabs BNSYM at %08llX in %s",
+ (uint64_t)sym->n_value(), path);
+ }
+ break;
+ case N_SO:
+ case N_OSO:
+ case N_OPT:
+ case N_LSYM:
+ case N_RSYM:
+ case N_PSYM:
+ // not associated with an atom, just copy
+ stab.string = symString;
+ break;
+ case N_GSYM:
+ {
+ // n_value field is NOT atom address ;-(
+ // need to find atom by name match
+ const char* colon = strchr(symString, ':');
+ if ( colon != NULL ) {
+ // build underscore leading name
+ int nameLen = colon - symString;
+ char symName[nameLen+2];
+ strlcpy(&symName[1], symString, nameLen+1);
+ symName[0] = '_';
+ symName[nameLen+1] = '\0';
+ currentAtom = findAtomByName(symName);
+ if ( currentAtom != NULL ) {
+ stab.atom = currentAtom;
+ stab.string = symString;
+ }
+ }
+ else {
+ // might be a debug-note without trailing :G()
+ currentAtom = findAtomByName(symString);
+ if ( currentAtom != NULL ) {
+ stab.atom = currentAtom;
+ stab.string = symString;
+ }
+ }
+ if ( stab.atom == NULL ) {
+ // ld_classic added bogus GSYM stabs for old style dtrace probes
+ if ( (strncmp(symString, "__dtrace_probe$", 15) != 0) )
+ warning("can't find atom for N_GSYM stabs %s in %s", symString, path);
+ useStab = false;
+ }
+ break;
+ }
+ case N_FUN:
+ // old style stabs without BNSYM
+ state = inFun;
+ currentAtomAddress = sym->n_value();
+ currentAtom = (BaseAtom*)this->findAtomAndOffset(currentAtomAddress).atom;
+ if ( currentAtom != NULL ) {
+ stab.atom = currentAtom;
+ stab.string = symString;
+ }
+ else {
+ warning("can't find atom for stabs FUN at %08llX in %s",
+ (uint64_t)currentAtomAddress, path);
+ }
+ break;
+ case N_SOL:
+ case N_SLINE:
+ stab.string = symString;
+ // old stabs
+ break;
+ case N_BINCL:
+ case N_EINCL:
+ case N_EXCL:
+ stab.string = symString;
+ // -gfull built .o file
+ break;
+ default:
+ warning("unknown stabs type 0x%X in %s", type, path);
+ }
+ break;
+ case inBeginEnd:
+ stab.atom = currentAtom;
+ switch (type) {
+ case N_ENSYM:
+ state = start;
+ currentAtom = NULL;
+ break;
+ case N_LCSYM:
+ case N_STSYM:
+ {
+ BaseAtom* nestedAtom = (BaseAtom*)this->findAtomAndOffset(sym->n_value()).atom;
+ if ( nestedAtom != NULL ) {
+ stab.atom = nestedAtom;
+ stab.string = symString;
+ }
+ else {
+ warning("can't find atom for stabs 0x%X at %08llX in %s",
+ type, (uint64_t)sym->n_value(), path);
+ }
+ break;
+ }
+ case N_LBRAC:
+ case N_RBRAC:
+ case N_SLINE:
+ // adjust value to be offset in atom
+ stab.value -= currentAtomAddress;
+ default:
+ stab.string = symString;
+ break;
+ }
+ break;
+ case inFun:
+ switch (type) {
+ case N_FUN:
+ if ( sym->n_sect() != 0 ) {
+ // found another start stab, must be really old stabs...
+ currentAtomAddress = sym->n_value();
+ currentAtom = (BaseAtom*)this->findAtomAndOffset(currentAtomAddress).atom;
+ if ( currentAtom != NULL ) {
+ stab.atom = currentAtom;
+ stab.string = symString;
+ }
+ else {
+ warning("can't find atom for stabs FUN at %08llX in %s",
+ (uint64_t)currentAtomAddress, path);
+ }
+ }
+ else {
+ // found ending stab, switch back to start state
+ stab.string = symString;
+ stab.atom = currentAtom;
+ state = start;
+ currentAtom = NULL;
+ }
+ break;
+ case N_LBRAC:
+ case N_RBRAC:
+ case N_SLINE:
+ // adjust value to be offset in atom
+ stab.value -= currentAtomAddress;
+ stab.atom = currentAtom;
+ break;
+ case N_SO:
+ stab.string = symString;
+ state = start;
+ break;
+ default:
+ stab.atom = currentAtom;
+ stab.string = symString;
+ break;
+ }
+ break;
+ }
+ // add to list of stabs for this .o file
+ if ( useStab )
+ fStabs.push_back(stab);
+ }
+ }
+ }
+
+#if 0
+ // special case precompiled header .o file (which has no content) to have one empty atom
+ if ( fAtoms.size() == 0 ) {
+ int pathLen = strlen(path);
+ if ( (pathLen > 6) && (strcmp(&path[pathLen-6], ".gch.o")==0) ) {
+ ObjectFile::Atom* phony = new AnonymousAtom<A>(*this, (uint32_t)0);
+ //phony->fSynthesizedName = ".gch.o";
+ fAtoms.push_back(phony);
+ }
+ }
+#endif
+
+ // sort all atoms by address
+ std::sort(fAtoms.begin(), fAtoms.end(), BaseAtomSorter());
+
+ // set ordinal and sort references in each atom
+ uint32_t index = fOrdinalBase;
+ for (std::vector<BaseAtom*>::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) {
+ BaseAtom* atom = (BaseAtom*)(*it);
+ atom->setOrdinal(index++);
+ atom->sortReferences();
+ }
+
+}
+
+template <typename A>
+const macho_section<typename A::P>* Reader<A>::getSectionForAddress(pint_t addr)
+{
+ for (const macho_section<P>* sect=fSectionsStart; sect < fSectionsEnd; ++sect) {
+ if ( (sect->addr() <= addr) && (addr < (sect->addr()+sect->size())) )
+ return sect;
+ }
+ throwf("section not found for address 0x%08llX", (uint64_t)addr);
+}
+
+template <typename A>
+ObjectFile::Atom* Reader<A>::getFunctionAtomFromFDEAddress(pint_t addr)
+{
+ for (typename std::vector<FDE_Atom_Info>::const_iterator it = fFDEInfos.begin(); it != fFDEInfos.end(); ++it) {
+ if ( it->fdeAddress == addr ) {
+ return findAtomAndOffset(it->function.address).atom;
+ }
+ }
+ // CIEs won't be in fFDEInfos
+ return NULL;
+}
+
+template <typename A>
+ObjectFile::Atom* Reader<A>::getFunctionAtomFromLSDAAddress(pint_t addr)
+{
+ for (typename std::vector<FDE_Atom_Info>::const_iterator it = fFDEInfos.begin(); it != fFDEInfos.end(); ++it) {
+ if ( it->lsda.address == addr ) {
+ return findAtomAndOffset(it->function.address).atom;
+ }
+ }
+ return NULL;
+}
+
+
+template <>
+void ObjectFileAddressSpace<x86_64>::buildRelocatedMap(const macho_section<P>* sect, std::map<uint32_t,uint64_t>& map)
+{
+ // mach-o x86_64 is different, the content of a section with a relocation is the addend
+ const macho_relocation_info<P>* relocs = (macho_relocation_info<P>*)((char*)(fReader.fHeader) + sect->reloff());
+ const macho_relocation_info<P>* relocsEnd = &relocs[sect->nreloc()];
+ for (const macho_relocation_info<P>* reloc = relocs; reloc < relocsEnd; ++reloc) {
+ std::map<uint32_t,uint64_t>::iterator pos;
+ switch ( reloc->r_type() ) {
+ case X86_64_RELOC_UNSIGNED:
+ pos = map.find(reloc->r_address());
+ if ( pos != map.end() )
+ pos->second += fReader.fSymbols[reloc->r_symbolnum()].n_value();
+ else
+ map[reloc->r_address()] = fReader.fSymbols[reloc->r_symbolnum()].n_value();
+ break;
+ case X86_64_RELOC_SUBTRACTOR:
+ map[reloc->r_address()] = -fReader.fSymbols[reloc->r_symbolnum()].n_value();
+ break;
+ case X86_64_RELOC_GOT:
+ // there is no good address to return here.
+ // GOT slots are synthsized by the linker
+ // this is used for the reference to the personality function in CIEs
+ map[reloc->r_address()] = 0;
+ break;
+ default:
+ fprintf(stderr, "ObjectFileAddressSpace::buildRelocatedMap() unexpected relocation at r_address=0x%08X\n", reloc->r_address());
+ break;
+ }
+ }
+}
+
+template <typename A>
+void ObjectFileAddressSpace<A>::buildRelocatedMap(const macho_section<P>* sect, std::map<uint32_t,uint64_t>& map)
+{
+ // in all architectures except x86_64, the section contents are already fixed up to point
+ // to content in the same object file.
+}
+
+template <>
+uint64_t ObjectFileAddressSpace<x86_64>::relocated(uint32_t sectOffset, uint32_t relocsOffset, uint32_t relocsCount)
+{
+ // mach-o x86_64 is different, the content of a section with a relocation is the addend
+ uint64_t result = 0;
+ const macho_relocation_info<P>* relocs = (macho_relocation_info<P>*)((char*)(fReader.fHeader) + relocsOffset);
+ const macho_relocation_info<P>* relocsEnd = &relocs[relocsCount];
+ for (const macho_relocation_info<P>* reloc = relocs; reloc < relocsEnd; ++reloc) {
+ //fprintf(stderr, "ObjectFileAddressSpace::relocated(0x%08X), r_address=0x%08X\n", sectOffset, reloc->r_address());
+ if ( reloc->r_address() == sectOffset ) {
+ switch ( reloc->r_type() ) {
+ case X86_64_RELOC_UNSIGNED:
+ result += fReader.fSymbols[reloc->r_symbolnum()].n_value();
+ break;
+ case X86_64_RELOC_SUBTRACTOR:
+ result -= fReader.fSymbols[reloc->r_symbolnum()].n_value();
+ break;
+ case X86_64_RELOC_GOT:
+ // there is no good address to return here.
+ // GOT slots are synthsized by the linker
+ // this is used for the reference to the personality function in CIEs
+ result = 0;
+ break;
+ default:
+ fprintf(stderr, "ObjectFileAddressSpace::relocated(0x%08X) => type=%d, value=0x%08X\n", sectOffset, reloc->r_type(), reloc->r_symbolnum());
+ break;
+ }
+ }
+ }
+ //fprintf(stderr, "ObjectFileAddressSpace::relocated(0x%08X) => 0x%0llX\n", sectOffset, result);
+ return result;
+}
+
+template <typename A>
+typename A::P::uint_t ObjectFileAddressSpace<A>::relocated(uint32_t sectOffset, uint32_t relocsOffset, uint32_t relocsCount)
+{
+ // in all architectures except x86_64, the section contents are already fixed up to point
+ // to content in the same object file.
+ return 0;
+}
+
+
+
+// FSF exception handling Pointer-Encoding constants
+// Used in CFI augmentation by gcc compiler
+enum {
+ DW_EH_PE_ptr = 0x00,
+ DW_EH_PE_uleb128 = 0x01,
+ DW_EH_PE_udata2 = 0x02,
+ DW_EH_PE_udata4 = 0x03,
+ DW_EH_PE_udata8 = 0x04,
+ DW_EH_PE_signed = 0x08,
+ DW_EH_PE_sleb128 = 0x09,
+ DW_EH_PE_sdata2 = 0x0A,
+ DW_EH_PE_sdata4 = 0x0B,
+ DW_EH_PE_sdata8 = 0x0C,
+ DW_EH_PE_absptr = 0x00,
+ DW_EH_PE_pcrel = 0x10,
+ DW_EH_PE_textrel = 0x20,
+ DW_EH_PE_datarel = 0x30,
+ DW_EH_PE_funcrel = 0x40,
+ DW_EH_PE_aligned = 0x50,
+ DW_EH_PE_indirect = 0x80,
+ DW_EH_PE_omit = 0xFF
+};
+
+template <>
+void Reader<x86_64>::addCiePersonalityReference(BaseAtom* cieAtom, uint32_t offsetInCIE, uint8_t encoding)
+{
+ if ( encoding != (DW_EH_PE_indirect|DW_EH_PE_pcrel|DW_EH_PE_sdata4) )
+ throw "unexpected personality encoding in CIE";
+
+ // walk relocs looking for reloc in this CIE
+ uint32_t sectOffset = (cieAtom->getObjectAddress() + offsetInCIE) - fehFrameSection->addr();
+ const macho_relocation_info<P>* relocs = (macho_relocation_info<P>*)((char*)(fHeader) + fehFrameSection->reloff());
+ const macho_relocation_info<P>* relocsEnd = &relocs[fehFrameSection->nreloc()];
+ for (const macho_relocation_info<P>* reloc = relocs; reloc < relocsEnd; ++reloc) {
+ if ( reloc->r_address() == sectOffset ) {
+ switch ( reloc->r_type() ) {
+ case X86_64_RELOC_GOT:
+ if ( !reloc->r_extern() )
+ throw "GOT reloc not extern for personality function";
+ new Reference<x86_64>(x86_64::kPCRel32GOT, AtomAndOffset(cieAtom, offsetInCIE), &fStrings[fSymbols[reloc->r_symbolnum()].n_strx()], 4);
+ return;
+ default:
+ throw "expected GOT reloc for personality function";
+ }
+ }
+ }
+ throw "personality function not found for CIE";
+}
+
+template <>
+bool Reader<ppc>::isSectDiffReloc(uint8_t r_type)
+{
+ switch ( r_type ) {
+ case PPC_RELOC_LOCAL_SECTDIFF:
+ case PPC_RELOC_SECTDIFF:
+ return true;
+ }
+ return false;
+}
+
+template <>
+bool Reader<ppc64>::isSectDiffReloc(uint8_t r_type)
+{
+ switch ( r_type ) {
+ case PPC_RELOC_LOCAL_SECTDIFF:
+ case PPC_RELOC_SECTDIFF:
+ return true;
+ }
+ return false;
+}
+
+template <>
+bool Reader<x86>::isSectDiffReloc(uint8_t r_type)
+{
+ switch ( r_type ) {
+ case GENERIC_RELOC_LOCAL_SECTDIFF:
+ case GENERIC_RELOC_SECTDIFF:
+ return true;
+ }
+ return false;
+}
+
+template <>
+bool Reader<arm>::isSectDiffReloc(uint8_t r_type)
+{
+ switch ( r_type ) {
+ case ARM_RELOC_LOCAL_SECTDIFF:
+ case ARM_RELOC_SECTDIFF:
+ return true;
+ }
+ return false;
+}
+
+template <typename A>
+void Reader<A>::addCiePersonalityReference(BaseAtom* cieAtom, uint32_t offsetInCIE, uint8_t encoding)
+{
+ if ( (encoding != (DW_EH_PE_indirect|DW_EH_PE_pcrel|DW_EH_PE_sdata4)) && (encoding != (DW_EH_PE_indirect|DW_EH_PE_pcrel)) )
+ throw "unexpected personality encoding in CIE";
+
+ // walk relocs looking for personality reloc in this CIE
+ uint32_t sectOffset = (cieAtom->getObjectAddress() + offsetInCIE) - fehFrameSection->addr();
+ const macho_relocation_info<P>* relocs = (macho_relocation_info<P>*)((char*)(fHeader) + fehFrameSection->reloff());
+ const macho_relocation_info<P>* relocsEnd = &relocs[fehFrameSection->nreloc()];
+ for (const macho_relocation_info<P>* reloc = relocs; reloc < relocsEnd; ++reloc) {
+ if ( (reloc->r_address() & R_SCATTERED) == 0 ) {
+ // ignore
+ }
+ else {
+ const macho_scattered_relocation_info<P>* sreloc = (macho_scattered_relocation_info<P>*)reloc;
+ if ( sreloc->r_address() == sectOffset ) {
+ if ( isSectDiffReloc(sreloc->r_type()) ) {
+ // r_value is address of non-lazy-pointer to personality function
+ new Reference<A>(A::kPointerDiff32, AtomAndOffset(cieAtom, offsetInCIE), AtomAndOffset(cieAtom, offsetInCIE),
+ findAtomAndOffset(sreloc->r_value()));
+ return;
+ }
+ }
+ }
+ }
+ throw "can't find relocation for personality in CIE";
+}
+
+template <typename A>
+void Reader<A>::addFdeReference(uint8_t encoding, AtomAndOffset inFDE, AtomAndOffset target)
+{
+ if ( (encoding & 0xF0) != DW_EH_PE_pcrel )
+ throw "unsupported encoding in FDE";
+ Kinds kind = A::kNoFixUp;
+ switch ( encoding & 0xF ) {
+ case DW_EH_PE_ptr:
+ kind = A::kPointerDiff;
+ break;
+ case DW_EH_PE_sdata4:
+ kind = A::kPointerDiff32;
+ break;
+ default:
+ throw "unsupported encoding in FDE";
+ }
+ new Reference<A>(kind, inFDE, inFDE, target);
+}
+
+template <typename A>
+typename A::P::uint_t ObjectFileAddressSpace<A>::getEncodedP(pint_t& addr, pint_t end, uint8_t encoding)
+{
+ pint_t startAddr = addr;
+ pint_t p = addr;
+ pint_t result;
+
+ // first get value
+ switch (encoding & 0x0F) {
+ case DW_EH_PE_ptr:
+ result = getP(addr);
+ p += sizeof(pint_t);
+ addr = (pint_t)p;
+ break;
+ case DW_EH_PE_uleb128:
+ result = getULEB128(addr, end);
+ break;
+ case DW_EH_PE_udata2:
+ result = get16(addr);
+ p += 2;
+ addr = (pint_t)p;
+ break;
+ case DW_EH_PE_udata4:
+ result = get32(addr);
+ p += 4;
+ addr = (pint_t)p;
+ break;
+ case DW_EH_PE_udata8:
+ result = get64(addr);
+ p += 8;
+ addr = (pint_t)p;
+ break;
+ case DW_EH_PE_sleb128:
+ result = getSLEB128(addr, end);
+ break;
+ case DW_EH_PE_sdata2:
+ result = (int16_t)get16(addr);
+ p += 2;
+ addr = (pint_t)p;
+ break;
+ case DW_EH_PE_sdata4:
+ result = (int32_t)get32(addr);
+ p += 4;
+ addr = (pint_t)p;
+ break;
+ case DW_EH_PE_sdata8:
+ result = get64(addr);
+ p += 8;
+ addr = (pint_t)p;
+ break;
+ default:
+ throwf("ObjectFileAddressSpace<A>::getEncodedP() encoding 0x%08X not supported", encoding);
+ }
+
+ // then add relative offset
+ switch ( encoding & 0x70 ) {
+ case DW_EH_PE_absptr:
+ // do nothing
+ break;
+ case DW_EH_PE_pcrel:
+ result += startAddr;
+ break;
+ case DW_EH_PE_textrel:
+ throw "DW_EH_PE_textrel pointer encoding not supported";
+ break;
+ case DW_EH_PE_datarel:
+ throw "DW_EH_PE_datarel pointer encoding not supported";
+ break;
+ case DW_EH_PE_funcrel:
+ throw "DW_EH_PE_funcrel pointer encoding not supported";
+ break;
+ case DW_EH_PE_aligned:
+ throw "DW_EH_PE_aligned pointer encoding not supported";
+ break;
+ default:
+ throwf("ObjectFileAddressSpace<A>::getEncodedP() encoding 0x%08X not supported", encoding);
+ break;
+ }
+
+ if ( encoding & DW_EH_PE_indirect )
+ result = getP(result);
+
+ return result;
+}
+
+template <>
+uint32_t SymbolAtom<x86>::getCompactUnwindEncoding(uint64_t ehAtomAddress)
+{
+ pint_t lsda;
+ pint_t personality;
+ char warningBuffer[1024];
+ uint32_t result = libunwind::DwarfInstructions<class ObjectFileAddressSpace<x86>, libunwind::Registers_x86>::createCompactEncodingFromFDE(
+ fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality, warningBuffer);
+ if ( (result & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_DWARF ) {
+ //if ( fOwner.fOptions.fForDyld )
+ // throwf("can't make compact unwind encoding from dwarf for %s", this->getDisplayName());
+ //else
+ if ( fOwner.fOptions.fWarnCompactUnwind )
+ warning("can't make compact unwind encoding from dwarf for %s in %s because %s", this->getDisplayName(), fOwner.getPath(), warningBuffer);
+ }
+ return result;
+}
+
+template <>
+uint32_t SymbolAtom<x86_64>::getCompactUnwindEncoding(uint64_t ehAtomAddress)
+{
+ pint_t lsda;
+ pint_t personality;
+ char warningBuffer[1024];
+ uint32_t result = libunwind::DwarfInstructions<class ObjectFileAddressSpace<x86_64>, libunwind::Registers_x86_64>::createCompactEncodingFromFDE(
+ fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality, warningBuffer);
+ if ( (result & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF ) {
+ //if ( fOwner.fOptions.fForDyld )
+ // throwf("can't make compact unwind encoding from dwarf for %s", this->getDisplayName());
+ //else
+ if ( fOwner.fOptions.fWarnCompactUnwind )
+ warning("can't make compact unwind encoding from dwarf for %s in %s because %s", this->getDisplayName(), fOwner.getPath(), warningBuffer);
+ }
+ return result;
+}
+
+template <>
+uint32_t SymbolAtom<ppc>::getCompactUnwindEncoding(uint64_t ehAtomAddress)
+{
+ // compact encoding not supported for ppc
+ return 0;
+}
+
+template <>
+uint32_t SymbolAtom<ppc64>::getCompactUnwindEncoding(uint64_t ehAtomAddress)
+{
+ // compact encoding not supported for ppc64
+ return 0;
+}
+
+template <>
+uint32_t SymbolAtom<arm>::getCompactUnwindEncoding(uint64_t ehAtomAddress)
+{
+ // compact encoding not supported for arm
+ return 0;
+}
+
+
+template <typename A>
+uint8_t SymbolAtom<A>::getLSDAReferenceKind() const
+{
+ return A::kGroupSubordinate;
+}
+
+template <>
+uint8_t SymbolAtom<x86_64>::getPersonalityReferenceKind() const
+{
+ return x86_64::kGOTNoFixUp;
+}
+
+template <>
+uint8_t SymbolAtom<x86>::getPersonalityReferenceKind() const
+{
+ return x86::kNoFixUp;
+}
+
+template <typename A>
+uint8_t SymbolAtom<A>::getPersonalityReferenceKind() const
+{
+ // only used with architectures that support compact unwinding
+ return 0;
+}
+
+
+template <>
+uint32_t AnonymousAtom<x86>::getCompactUnwindEncoding(uint64_t ehAtomAddress)
+{
+ pint_t lsda;
+ pint_t personality;
+ char warningBuffer[1024];
+ uint32_t result = libunwind::DwarfInstructions<class ObjectFileAddressSpace<x86>, libunwind::Registers_x86>::createCompactEncodingFromFDE(
+ fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality, warningBuffer);
+ if ( (result & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_DWARF ) {
+ //if ( fOwner.fOptions.fForDyld )
+ // throwf("can't make compact unwind encoding from dwarf for %s", this->getDisplayName());
+ //else
+ if ( fOwner.fOptions.fWarnCompactUnwind )
+ warning("can't make compact unwind encoding from dwarf for %s in %s", this->getDisplayName(), fOwner.getPath());
+ }
+ return result;
+}
+
+template <>
+uint32_t AnonymousAtom<x86_64>::getCompactUnwindEncoding(uint64_t ehAtomAddress)
+{
+ pint_t lsda;
+ pint_t personality;
+ char warningBuffer[1024];
+ uint32_t result = libunwind::DwarfInstructions<class ObjectFileAddressSpace<x86_64>, libunwind::Registers_x86_64>::createCompactEncodingFromFDE(
+ fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality, warningBuffer);
+ if ( (result & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF ) {
+ //if ( fOwner.fOptions.fForDyld )
+ // throwf("can't make compact unwind encoding from dwarf for %s", this->getDisplayName());
+ //else
+ if ( fOwner.fOptions.fWarnCompactUnwind )
+ warning("can't make compact unwind encoding from dwarf for %s in %s", this->getDisplayName(), fOwner.getPath());
+ }
+ return result;
+}
+
+template <>
+uint32_t AnonymousAtom<ppc>::getCompactUnwindEncoding(uint64_t ehAtomAddress)
+{
+ // compact encoding not supported for ppc
+ return 0;
+}
+
+template <>
+uint32_t AnonymousAtom<ppc64>::getCompactUnwindEncoding(uint64_t ehAtomAddress)
+{
+ // compact encoding not supported for ppc64
+ return 0;
+}
+
+template <>
+uint32_t AnonymousAtom<arm>::getCompactUnwindEncoding(uint64_t ehAtomAddress)
+{
+ // compact encoding not supported for arm
+ return 0;
+}
+
+
+template <typename A>
+uint8_t AnonymousAtom<A>::getLSDAReferenceKind() const
+{
+ return A::kGroupSubordinate;
+}
+
+template <>
+uint8_t AnonymousAtom<x86_64>::getPersonalityReferenceKind() const
+{
+ return x86_64::kGOTNoFixUp;
+}
+
+template <>
+uint8_t AnonymousAtom<x86>::getPersonalityReferenceKind() const
+{
+ return x86::kNoFixUp;
+}
+
+template <typename A>
+uint8_t AnonymousAtom<A>::getPersonalityReferenceKind() const
+{
+ // only used with architectures that support compact unwinding
+ return 0;
+}
+
+
+
+
+
+
+
+template <>
+void Reader<ppc>::setCpuConstraint(uint32_t cpusubtype)
+{
+ switch (cpusubtype) {
+ case CPU_SUBTYPE_POWERPC_ALL:
+ case CPU_SUBTYPE_POWERPC_750:
+ case CPU_SUBTYPE_POWERPC_7400:
+ case CPU_SUBTYPE_POWERPC_7450:
+ case CPU_SUBTYPE_POWERPC_970:
+ fCpuConstraint = cpusubtype;
+ break;
+ default:
+ warning("unknown ppc subtype 0x%08X in %s, defaulting to ALL", cpusubtype, fPath);
+ fCpuConstraint = CPU_SUBTYPE_POWERPC_ALL;
+ break;
+ }
+}
+
+template <>
+void Reader<arm>::setCpuConstraint(uint32_t cpusubtype)
+{
+ switch (cpusubtype) {
+ case CPU_SUBTYPE_ARM_ALL:
+ case CPU_SUBTYPE_ARM_V4T:
+ case CPU_SUBTYPE_ARM_V5TEJ:
+ case CPU_SUBTYPE_ARM_V6:
+ case CPU_SUBTYPE_ARM_XSCALE:
+ case CPU_SUBTYPE_ARM_V7:
+ fCpuConstraint = cpusubtype;
+ break;
+ default:
+ warning("unknown arm subtype 0x%08X in %s, defaulting to ALL", cpusubtype, fPath);
+ fCpuConstraint = CPU_SUBTYPE_ARM_ALL;
+ break;
+ }
+}
+
+template <typename A>
+void Reader<A>::setCpuConstraint(uint32_t cpusubtype)
+{
+ // no cpu sub types for this architecture
+}
+
+template <>
+uint32_t Reader<ppc>::updateCpuConstraint(uint32_t previous)
+{
+ switch ( previous ) {
+ case CPU_SUBTYPE_POWERPC_ALL:
+ return fCpuConstraint;
+ break;
+ case CPU_SUBTYPE_POWERPC_750:
+ if ( fCpuConstraint == CPU_SUBTYPE_POWERPC_7400 ||
+ fCpuConstraint == CPU_SUBTYPE_POWERPC_7450 ||
+ fCpuConstraint == CPU_SUBTYPE_POWERPC_970 )
+ return fCpuConstraint;
+ break;
+ case CPU_SUBTYPE_POWERPC_7400:
+ case CPU_SUBTYPE_POWERPC_7450:
+ if ( fCpuConstraint == CPU_SUBTYPE_POWERPC_970 )
+ return fCpuConstraint;
+ break;
+ case CPU_SUBTYPE_POWERPC_970:
+ // G5 can run everything
+ break;
+ default:
+ throw "Unhandled PPC cpu subtype!";
+ break;
+ }
+ return previous;
+}
+
+
+
+template <>
+uint32_t Reader<arm>::updateCpuConstraint(uint32_t previous)
+{
+ switch (previous) {
+ case CPU_SUBTYPE_ARM_ALL:
+ return fCpuConstraint;
+ break;
+ case CPU_SUBTYPE_ARM_V5TEJ:
+ // v6, v7, and xscale are more constrained than previous file (v5), so use it
+ if ( (fCpuConstraint == CPU_SUBTYPE_ARM_V6)
+ || (fCpuConstraint == CPU_SUBTYPE_ARM_V7)
+ || (fCpuConstraint == CPU_SUBTYPE_ARM_XSCALE) )
+ return fCpuConstraint;
+ break;
+ case CPU_SUBTYPE_ARM_V4T:
+ // v5, v6, v7, and xscale are more constrained than previous file (v4t), so use it
+ if ( (fCpuConstraint == CPU_SUBTYPE_ARM_V7)
+ || (fCpuConstraint == CPU_SUBTYPE_ARM_V6)
+ || (fCpuConstraint == CPU_SUBTYPE_ARM_V5TEJ)
+ || (fCpuConstraint == CPU_SUBTYPE_ARM_XSCALE) )
+ return fCpuConstraint;
+ break;
+ case CPU_SUBTYPE_ARM_V6:
+ // v6 can run everything except xscale and v7
+ if ( fCpuConstraint == CPU_SUBTYPE_ARM_XSCALE )
+ throw "can't mix xscale and v6 code";
+ if ( fCpuConstraint == CPU_SUBTYPE_ARM_V7 )
+ return fCpuConstraint;
+ break;
+ case CPU_SUBTYPE_ARM_XSCALE:
+ // xscale can run everything except v6 and v7
+ if ( fCpuConstraint == CPU_SUBTYPE_ARM_V6 )
+ throw "can't mix xscale and v6 code";
+ if ( fCpuConstraint == CPU_SUBTYPE_ARM_V7 )
+ throw "can't mix xscale and v7 code";
+ break;
+ case CPU_SUBTYPE_ARM_V7:
+ // v7 can run everything except xscale
+ if ( fCpuConstraint == CPU_SUBTYPE_ARM_XSCALE )
+ throw "can't mix xscale and v7 code";
+ break;
+ default:
+ throw "Unhandled ARM cpu subtype!";
+ }
+ return previous;
+}
+
+template <typename A>
+uint32_t Reader<A>::updateCpuConstraint(uint32_t current)
+{
+ // no cpu sub types for this architecture
+ return current;
+}
+
+template <typename A>
+void Reader<A>::addDtraceExtraInfos(uint32_t probeAddr, const char* providerName)
+{
+ // for every ___dtrace_stability$* and ___dtrace_typedefs$* undefine with
+ // a matching provider name, add a by-name kDtraceTypeReference at probe site
+ const char* dollar = strchr(providerName, '$');
+ if ( dollar != NULL ) {
+ int providerNameLen = dollar-providerName+1;
+ for ( std::vector<const char*>::iterator it = fDtraceProviderInfo.begin(); it != fDtraceProviderInfo.end(); ++it) {
+ const char* typeDollar = strchr(*it, '$');
+ if ( typeDollar != NULL ) {
+ if ( strncmp(typeDollar+1, providerName, providerNameLen) == 0 ) {
+ makeByNameReference(A::kDtraceTypeReference, probeAddr, *it, 0);
+ }
+ }
+ }
+ }
+}
+
+
+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)
+{
+}
+
+template <typename A>
+bool Reader<A>::getTranslationUnitSource(const char** dir, const char** name) const
+{
+ if ( fDebugInfo == kDebugInfoDwarf ) {
+ *dir = fDwarfTranslationUnitDir;
+ *name = fDwarfTranslationUnitFile;
+ return (fDwarfTranslationUnitFile != NULL);
+ }
+ return false;
+}
+
+template <typename A>
+BaseAtom* Reader<A>::findAtomByName(const char* name)
+{
+ // first search the more important atoms
+ for (typename AddrToAtomMap::iterator it=fAddrToAtom.begin(); it != fAddrToAtom.end(); it++) {
+ const char* atomName = it->second->getName();
+ if ( (atomName != NULL) && (strcmp(atomName, name) == 0) ) {
+ return it->second;
+ }
+ }
+ // try all atoms, because this might have been a tentative definition
+ for (std::vector<BaseAtom*>::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) {
+ BaseAtom* atom = (BaseAtom*)(*it);
+ const char* atomName = atom->getName();
+ if ( (atomName != NULL) && (strcmp(atomName, name) == 0) ) {
+ return atom;
+ }
+ }
+ return NULL;
+}
+
+template <typename A>
+Reference<A>* Reader<A>::makeReference(Kinds kind, pint_t atAddr, pint_t toAddr)
+{
+ return new Reference<A>(kind, findAtomAndOffset(atAddr), findAtomAndOffset(toAddr));
+}
+
+template <typename A>
+Reference<A>* Reader<A>::makeReference(Kinds kind, pint_t atAddr, pint_t fromAddr, pint_t toAddr)
+{
+ return new Reference<A>(kind, findAtomAndOffset(atAddr), findAtomAndOffset(fromAddr), findAtomAndOffset(toAddr));
+}
+
+template <typename A>
+Reference<A>* Reader<A>::makeReferenceWithToBase(Kinds kind, pint_t atAddr, pint_t toAddr, pint_t toBaseAddr)
+{
+ return new Reference<A>(kind, findAtomAndOffset(atAddr), findAtomAndOffset(toBaseAddr, toAddr));
+}
+
+template <typename A>
+Reference<A>* Reader<A>::makeReferenceWithToBase(Kinds kind, pint_t atAddr, pint_t fromAddr, pint_t toAddr, pint_t toBaseAddr)
+{
+ return new Reference<A>(kind, findAtomAndOffset(atAddr), findAtomAndOffset(fromAddr), findAtomAndOffset(toBaseAddr, toAddr));
+}
+
+template <typename A>
+Reference<A>* Reader<A>::makeByNameReference(Kinds kind, pint_t atAddr, const char* toName, uint32_t toOffset)
+{
+ return new Reference<A>(kind, findAtomAndOffset(atAddr), toName, toOffset);
+}
+
+template <typename A>
+BaseAtom* Reader<A>::makeReferenceToEH(const char* ehName, pint_t ehAtomAddress, const macho_section<P>* ehSect)
+{
+ // add a group subordinate reference from function atom to its eh frame atom
+ const uint8_t* ehContent = (const uint8_t*)(fHeader) + ehAtomAddress - ehSect->addr() + ehSect->offset();
+ int32_t deltaMinus8 = P::getP(*(pint_t*)(&ehContent[8])); // offset 8 in eh info is delta to function
+ pint_t funcAddr = ehAtomAddress + deltaMinus8 + 8;
+ ObjectFile::Atom* funcAtom = findAtomAndOffset(funcAddr).atom;
+ ObjectFile::Atom* ehAtom = findAtomAndOffset(ehAtomAddress).atom;
+ new Reference<A>(A::kGroupSubordinate, funcAtom, ehAtom);
+ return (BaseAtom*)funcAtom;
+}
+
+
+template <>
+Reference<x86_64>* Reader<x86_64>::makeByNameReference(Kinds kind, pint_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, pint_t atAddr, const macho_nlist<P>* toSymbol, pint_t toOffset)
+{
+ // x86_64 uses external relocations everywhere, so external relocations do not imply by-name references
+ // instead check scope of target
+ const char* symbolName = &fStrings[toSymbol->n_strx()];
+ if ( ((toSymbol->n_type() & N_TYPE) == N_SECT) && (((toSymbol->n_type() & N_EXT) == 0) || (symbolName[0] == 'L')) )
+ 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), symbolName, toOffset);
+}
+
+
+template <>
+BaseAtom* Reader<x86_64>::makeReferenceToEH(const char* ehName, pint_t ehAtomAddress, const macho_section<P>* ehSect)
+{
+ // add a group subordinate 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) ) {
+ pint_t funcAddr = fSymbols[reloc->r_symbolnum()].n_value();
+ ObjectFile::Atom* funcAtom = findAtomAndOffset(funcAddr).atom;
+ ObjectFile::Atom* ehAtom = findAtomAndOffset(ehAtomAddress).atom;
+ new Reference<x86_64>(x86_64::kGroupSubordinate, funcAtom, ehAtom);
+ return (BaseAtom*)funcAtom;
+ }
+ }
+ warning("can't find matching function for eh symbol %s", ehName);
+ return NULL;
+}
+
+
+template <typename A>
+AtomAndOffset Reader<A>::findAtomAndOffset(pint_t addr)
+{
+ // STL has no built-in for "find largest key that is same or less than"
+ typename AddrToAtomMap::iterator it = fAddrToAtom.upper_bound(addr);
+ // if no atoms up to this address return none found
+ if ( it == fAddrToAtom.begin() )
+ return AtomAndOffset(NULL);
+ // otherwise upper_bound gets us next key, so we back up one
+ --it;
+ AtomAndOffset result;
+ result.atom = it->second;
+ result.offset = addr - it->first;
+ //fprintf(stderr, "findAtomAndOffset(0x%0llX) ==> %s (0x%0llX -> 0x%0llX)\n",
+ // (uint64_t)addr, result.atom->getDisplayName(), (uint64_t)it->first, it->first+result.atom->getSize());
+ return result;
+}
+
+// "scattered" relocations enable you to offset into an atom past the end of it
+// baseAddr is the address of the target atom,
+// realAddr is the points into it
+template <typename A>
+AtomAndOffset Reader<A>::findAtomAndOffset(pint_t baseAddr, pint_t realAddr)
+{
+ typename AddrToAtomMap::iterator it = fAddrToAtom.find(baseAddr);
+ if ( it != fAddrToAtom.end() ) {
+ AtomAndOffset result;
+ result.atom = it->second;
+ result.offset = realAddr - it->first;
+ if ( result.atom->isThumb() )
+ result.offset &= -2;
+ //fprintf(stderr, "findAtomAndOffset(0x%08X, 0x%08X) => %s + 0x%08X\n", baseAddr, realAddr, result.atom->getDisplayName(), result.offset);
+ return result;
+ }
+ // getting here means we have a scattered relocation to an address without a label
+ // so, find the atom that contains the baseAddr, and offset from that to the readAddr
+ AtomAndOffset result = findAtomAndOffset(baseAddr);
+ result.offset += (realAddr-baseAddr);
+ return result;
+}
+
+
+/* Skip over a LEB128 value (signed or unsigned). */
+static void
+skip_leb128 (const uint8_t ** offset, const uint8_t * end)
+{
+ while (*offset != end && **offset >= 0x80)
+ (*offset)++;
+ if (*offset != end)
+ (*offset)++;
+}
+
+/* Read a ULEB128 into a 64-bit word. Return (uint64_t)-1 on overflow
+ or error. On overflow, skip past the rest of the uleb128. */
+static uint64_t
+read_uleb128 (const uint8_t ** offset, const uint8_t * end)
+{
+ uint64_t result = 0;
+ int bit = 0;
+
+ do {
+ uint64_t b;
+
+ if (*offset == end)
+ return (uint64_t) -1;
+
+ b = **offset & 0x7f;
+
+ if (bit >= 64 || b << bit >> bit != b)
+ result = (uint64_t) -1;
+ else
+ result |= b << bit, bit += 7;
+ } while (*(*offset)++ >= 0x80);
+ return result;
+}
+
+
+/* Skip over a DWARF attribute of form FORM. */
+template <typename A>
+bool Reader<A>::skip_form(const uint8_t ** offset, const uint8_t * end, uint64_t form,
+ uint8_t addr_size, bool dwarf64)
+{
+ int64_t sz=0;
+
+ switch (form)
+ {
+ case DW_FORM_addr:
+ sz = addr_size;
+ break;
+
+ case DW_FORM_block2:
+ if (end - *offset < 2)
+ return false;
+ sz = 2 + A::P::E::get16(*(uint16_t*)offset);
+ break;
+
+ case DW_FORM_block4:
+ if (end - *offset < 4)
+ return false;
+ sz = 2 + A::P::E::get32(*(uint32_t*)offset);
+ break;
+
+ case DW_FORM_data2:
+ case DW_FORM_ref2:
+ sz = 2;
+ break;
+
+ case DW_FORM_data4:
+ case DW_FORM_ref4:
+ sz = 4;
+ break;
+
+ case DW_FORM_data8:
+ case DW_FORM_ref8:
+ sz = 8;
+ break;
+
+ case DW_FORM_string:
+ while (*offset != end && **offset)
+ ++*offset;
+ case DW_FORM_data1:
+ case DW_FORM_flag:
+ case DW_FORM_ref1:
+ sz = 1;
+ break;
+
+ case DW_FORM_block:
+ sz = read_uleb128 (offset, end);
+ break;
+
+ case DW_FORM_block1:
+ if (*offset == end)
+ return false;
+ sz = 1 + **offset;
+ break;
+
+ case DW_FORM_sdata:
+ case DW_FORM_udata:
+ case DW_FORM_ref_udata:
+ skip_leb128 (offset, end);
+ return true;
+
+ case DW_FORM_strp:
+ case DW_FORM_ref_addr:
+ sz = 4;
+ break;
+
+ default:
+ return false;
+ }
+ if (end - *offset < sz)
+ return false;
+ *offset += sz;
+ return true;
+}
+
+template <typename A>
+const char* Reader<A>::getDwarfString(uint64_t form, const uint8_t* p)
+{
+ if ( form == DW_FORM_string )
+ return (const char*)p;
+ else if ( form == DW_FORM_strp ) {
+ uint32_t offset = E::get32(*((uint32_t*)p));
+ const char* dwarfStrings = (char*)(fHeader) + fDwarfDebugStringSect->offset();
+ if ( offset > fDwarfDebugStringSect->size() ) {
+ warning("unknown dwarf DW_FORM_strp (offset=0x%08X) is too big in %s\n", offset, this->getPath());
+ return NULL;
+ }
+ return &dwarfStrings[offset];
+ }
+ warning("unknown dwarf string encoding (form=%lld) in %s\n", form, this->getPath());
+ return NULL;
+}
+
+
+// Look at the compilation unit DIE and determine
+// its NAME, compilation directory (in COMP_DIR) and its
+// line number information offset (in STMT_LIST). NAME and COMP_DIR
+// may be NULL (especially COMP_DIR) if they are not in the .o file;
+// STMT_LIST will be (uint64_t) -1.
+//
+// At present this assumes that there's only one compilation unit DIE.
+//
+template <typename A>
+bool Reader<A>::read_comp_unit(const char ** name, const char ** comp_dir,
+ uint64_t *stmt_list)
+{
+ const uint8_t * debug_info;
+ const uint8_t * debug_abbrev;
+ const uint8_t * di;
+ const uint8_t * da;
+ const uint8_t * end;
+ const uint8_t * enda;
+ uint64_t sz;
+ uint16_t vers;
+ uint64_t abbrev_base;
+ uint64_t abbrev;
+ uint8_t address_size;
+ bool dwarf64;
+
+ *name = NULL;
+ *comp_dir = NULL;
+ *stmt_list = (uint64_t) -1;
+
+ if ( (fDwarfDebugInfoSect == NULL) || (fDwarfDebugAbbrevSect == NULL) )
+ return false;
+
+ debug_info = (uint8_t*)(fHeader) + fDwarfDebugInfoSect->offset();
+ debug_abbrev = (uint8_t*)(fHeader) + fDwarfDebugAbbrevSect->offset();
+ di = debug_info;
+
+ if (fDwarfDebugInfoSect->size() < 12)
+ /* Too small to be a real debug_info section. */
+ return false;
+ sz = A::P::E::get32(*(uint32_t*)di);
+ di += 4;
+ dwarf64 = sz == 0xffffffff;
+ if (dwarf64)
+ sz = A::P::E::get64(*(uint64_t*)di), di += 8;
+ else if (sz > 0xffffff00)
+ /* Unknown dwarf format. */
+ return false;
+
+ /* Verify claimed size. */
+ if (sz + (di - debug_info) > fDwarfDebugInfoSect->size() || sz <= (dwarf64 ? 23 : 11))
+ return false;
+
+ vers = A::P::E::get16(*(uint16_t*)di);
+ if (vers < 2 || vers > 3)
+ /* DWARF version wrong for this code.
+ Chances are we could continue anyway, but we don't know for sure. */
+ return false;
+ di += 2;
+
+ /* Find the debug_abbrev section. */
+ abbrev_base = dwarf64 ? A::P::E::get64(*(uint64_t*)di) : A::P::E::get32(*(uint32_t*)di);
+ di += dwarf64 ? 8 : 4;
+
+ if (abbrev_base > fDwarfDebugAbbrevSect->size())
+ return false;
+ da = debug_abbrev + abbrev_base;
+ enda = debug_abbrev + fDwarfDebugAbbrevSect->size();
+
+ address_size = *di++;
+
+ /* Find the abbrev number we're looking for. */
+ end = di + sz;
+ abbrev = read_uleb128 (&di, end);
+ if (abbrev == (uint64_t) -1)
+ return false;
+
+ /* Skip through the debug_abbrev section looking for that abbrev. */
+ for (;;)
+ {
+ uint64_t this_abbrev = read_uleb128 (&da, enda);
+ uint64_t attr;
+
+ if (this_abbrev == abbrev)
+ /* This is almost always taken. */
+ break;
+ skip_leb128 (&da, enda); /* Skip the tag. */
+ if (da == enda)
+ return false;
+ da++; /* Skip the DW_CHILDREN_* value. */
+
+ do {
+ attr = read_uleb128 (&da, enda);
+ skip_leb128 (&da, enda);
+ } while (attr != 0 && attr != (uint64_t) -1);
+ if (attr != 0)
+ return false;
+ }
+
+ /* Check that the abbrev is one for a DW_TAG_compile_unit. */
+ if (read_uleb128 (&da, enda) != DW_TAG_compile_unit)
+ return false;
+ if (da == enda)
+ return false;
+ da++; /* Skip the DW_CHILDREN_* value. */
+
+ /* Now, go through the DIE looking for DW_AT_name,
+ DW_AT_comp_dir, and DW_AT_stmt_list. */
+ for (;;)
+ {
+ uint64_t attr = read_uleb128 (&da, enda);
+ uint64_t form = read_uleb128 (&da, enda);
+
+ if (attr == (uint64_t) -1)
+ return false;
+ else if (attr == 0)
+ return true;
+
+ if (form == DW_FORM_indirect)
+ form = read_uleb128 (&di, end);
+
+ if (attr == DW_AT_name)
+ *name = getDwarfString(form, di);
+ else if (attr == DW_AT_comp_dir)
+ *comp_dir = getDwarfString(form, di);
+ else if (attr == DW_AT_stmt_list && form == DW_FORM_data4)
+ *stmt_list = A::P::E::get32(*(uint32_t*)di);
+ else if (attr == DW_AT_stmt_list && form == DW_FORM_data8)
+ *stmt_list = A::P::E::get64(*(uint64_t*)di);
+ if (! skip_form (&di, end, form, address_size, dwarf64))
+ return false;
+ }
+}
+
+template <typename A>
+const char* Reader<A>::assureFullPath(const char* path)
+{
+ if ( path[0] == '/' )
+ return path;
+ char cwdbuff[MAXPATHLEN];
+ if ( getcwd(cwdbuff, MAXPATHLEN) != NULL ) {
+ char* result;
+ asprintf(&result, "%s/%s", cwdbuff, path);
+ if ( result != NULL )
+ return result;
+ }
+ return path;
+}
+
+
+//
+//
+// To implement architecture xxx, you must write template specializations for the following six methods:
+// Reader<xxx>::validFile()
+// Reader<xxx>::addRelocReference()
+// Reference<xxx>::getDescription()
+//
+//
+
+
+template <>
+bool Reader<ppc>::validFile(const uint8_t* fileContent, bool, cpu_subtype_t)
+{
+ 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_OBJECT )
+ return false;
+ return true;
+}
+
+template <>
+bool Reader<ppc64>::validFile(const uint8_t* fileContent, bool, cpu_subtype_t)
+{
+ 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_OBJECT )
+ return false;
+ return true;
+}
+
+template <>
+bool Reader<x86>::validFile(const uint8_t* fileContent, bool, cpu_subtype_t)
+{
+ 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_OBJECT )
+ return false;
+ return true;
+}
+
+template <>
+bool Reader<x86_64>::validFile(const uint8_t* fileContent, bool, cpu_subtype_t)
+{
+ 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 <>
+bool Reader<arm>::validFile(const uint8_t* fileContent, bool subtypeMustMatch, cpu_subtype_t subtype)
+{
+ const macho_header<P>* header = (const macho_header<P>*)fileContent;
+ if ( header->magic() != MH_MAGIC )
+ return false;
+ if ( header->cputype() != CPU_TYPE_ARM )
+ return false;
+ if ( header->filetype() != MH_OBJECT )
+ return false;
+ if ( subtypeMustMatch && ((cpu_subtype_t)header->cpusubtype() != subtype) )
+ return false;
+ return true;
+}
+
+template <typename A>
+bool Reader<A>::isWeakImportSymbol(const macho_nlist<P>* sym)
+{
+ return ( ((sym->n_type() & N_TYPE) == N_UNDF) && ((sym->n_desc() & N_WEAK_REF) != 0) );
+}
+
+template <>
+bool Reader<ppc64>::addRelocReference(const macho_section<ppc64::P>* sect, const macho_relocation_info<ppc64::P>* reloc)
+{
+ return addRelocReference_powerpc(sect, reloc);
+}
+
+template <>
+bool Reader<ppc>::addRelocReference(const macho_section<ppc::P>* sect, const macho_relocation_info<ppc::P>* reloc)
+{
+ return addRelocReference_powerpc(sect, reloc);
+}
+
+
+//
+// ppc and ppc64 both use the same relocations, so process them in one common routine
+//
+template <typename A>
+bool Reader<A>::addRelocReference_powerpc(const macho_section<typename A::P>* sect,
+ const macho_relocation_info<typename A::P>* reloc)
+{
+ uint32_t srcAddr;
+ uint32_t dstAddr;
+ uint32_t* fixUpPtr;
+ int32_t displacement = 0;
+ uint32_t instruction = 0;
+ uint32_t offsetInTarget;
+ int16_t lowBits;
+ bool result = false;
+ if ( (reloc->r_address() & R_SCATTERED) == 0 ) {
+ const macho_relocation_info<P>* nextReloc = &reloc[1];
+ const char* targetName = NULL;
+ bool weakImport = false;
+ fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address());
+ if ( reloc->r_type() != PPC_RELOC_PAIR )
+ instruction = BigEndian::get32(*fixUpPtr);
+ srcAddr = sect->addr() + reloc->r_address();
+ if ( reloc->r_extern() ) {
+ const macho_nlist<P>* targetSymbol = &fSymbols[reloc->r_symbolnum()];
+ targetName = &fStrings[targetSymbol->n_strx()];
+ weakImport = this->isWeakImportSymbol(targetSymbol);
+ }
+ switch ( reloc->r_type() ) {
+ case PPC_RELOC_BR24:
+ {
+ if ( (instruction & 0x4C000000) == 0x48000000 ) {
+ displacement = (instruction & 0x03FFFFFC);
+ if ( (displacement & 0x02000000) != 0 )
+ displacement |= 0xFC000000;
+ }
+ else {
+ printf("bad instruction for BR24 reloc");
+ }
+ if ( reloc->r_extern() ) {
+ offsetInTarget = srcAddr + displacement;
+ if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) {
+ makeByNameReference(A::kDtraceProbeSite, srcAddr, targetName, 0);
+ addDtraceExtraInfos(srcAddr, &targetName[16]);
+ }
+ else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) {
+ makeByNameReference(A::kDtraceIsEnabledSite, srcAddr, targetName, 0);
+ addDtraceExtraInfos(srcAddr, &targetName[20]);
+ }
+ else if ( weakImport )
+ makeByNameReference(A::kBranch24WeakImport, srcAddr, targetName, offsetInTarget);
+ else
+ makeByNameReference(A::kBranch24, srcAddr, targetName, offsetInTarget);
+ }
+ else {
+ dstAddr = srcAddr + displacement;
+ // if this is a branch to a stub, we need to see if the stub is for a weak imported symbol
+ ObjectFile::Atom* atom = findAtomAndOffset(dstAddr).atom;
+ targetName = atom->getName();
+ if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_probe$", 16) == 0) ) {
+ makeByNameReference(A::kDtraceProbeSite, srcAddr, targetName, 0);
+ addDtraceExtraInfos(srcAddr, &targetName[16]);
+ }
+ else if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_isenabled$", 20) == 0) ) {
+ makeByNameReference(A::kDtraceIsEnabledSite, srcAddr, targetName, 0);
+ addDtraceExtraInfos(srcAddr, &targetName[20]);
+ }
+ else if ( (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn)
+ && ((AnonymousAtom<A>*)atom)->isWeakImportStub() )
+ makeReference(A::kBranch24WeakImport, srcAddr, dstAddr);
+ else
+ makeReference(A::kBranch24, srcAddr, dstAddr);
+ }
+ }
+ break;
+ case PPC_RELOC_BR14:
+ {
+ displacement = (instruction & 0x0000FFFC);
+ if ( (displacement & 0x00008000) != 0 )
+ displacement |= 0xFFFF0000;
+ if ( reloc->r_extern() ) {
+ offsetInTarget = srcAddr + displacement;
+ makeByNameReference(A::kBranch14, srcAddr, targetName, offsetInTarget);
+ }
+ else {
+ dstAddr = srcAddr + displacement;
+ makeReference(A::kBranch14, srcAddr, dstAddr);
+ }
+ }
+ break;
+ case PPC_RELOC_PAIR:
+ // skip, processed by a previous look ahead
+ break;
+ case PPC_RELOC_LO16:
+ {
+ if ( nextReloc->r_type() != PPC_RELOC_PAIR ) {
+ throw "PPC_RELOC_LO16 missing following pair";
+ }
+ result = true;
+ lowBits = (instruction & 0xFFFF);
+ if ( reloc->r_extern() ) {
+ offsetInTarget = (nextReloc->r_address() << 16) | ((uint32_t)lowBits & 0x0000FFFF);
+ makeByNameReference(A::kAbsLow16, srcAddr, targetName, offsetInTarget);
+ }
+ else {
+ dstAddr = (nextReloc->r_address() << 16) + ((uint32_t)lowBits & 0x0000FFFF);
+ if ( reloc->r_symbolnum() == R_ABS ) {
+ // find absolute symbol that corresponds to pointerValue
+ typename AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr);
+ if ( pos != fAddrToAbsoluteAtom.end() )
+ makeByNameReference(A::kAbsLow16, srcAddr, pos->second->getName(), 0);
+ else
+ makeReference(A::kAbsLow16, srcAddr, dstAddr);
+ }
+ else {
+ makeReference(A::kAbsLow16, srcAddr, dstAddr);
+ }
+ }
+ }
+ break;
+ case PPC_RELOC_LO14:
+ {
+ if ( nextReloc->r_type() != PPC_RELOC_PAIR ) {
+ throw "PPC_RELOC_LO14 missing following pair";
+ }
+ result = true;
+ lowBits = (instruction & 0xFFFC);
+ if ( reloc->r_extern() ) {
+ offsetInTarget = (nextReloc->r_address() << 16) | ((uint32_t)lowBits & 0x0000FFFF);
+ makeByNameReference(A::kAbsLow14, srcAddr, targetName, offsetInTarget);
+ }
+ else {
+ dstAddr = (nextReloc->r_address() << 16) | ((uint32_t)lowBits & 0x0000FFFF);
+ if ( reloc->r_symbolnum() == R_ABS ) {
+ // find absolute symbol that corresponds to pointerValue
+ typename AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr);
+ if ( pos != fAddrToAbsoluteAtom.end() )
+ makeByNameReference(A::kAbsLow14, srcAddr, pos->second->getName(), 0);
+ else
+ makeReference(A::kAbsLow14, srcAddr, dstAddr);
+ }
+ else {
+ makeReference(A::kAbsLow14, srcAddr, dstAddr);
+ }
+ }
+ }
+ break;
+ case PPC_RELOC_HI16:
+ {
+ if ( nextReloc->r_type() != PPC_RELOC_PAIR ) {
+ throw "PPC_RELOC_HI16 missing following pair";
+ }
+ result = true;
+ if ( reloc->r_extern() ) {
+ offsetInTarget = ((instruction & 0x0000FFFF) << 16) | (nextReloc->r_address() & 0x0000FFFF);
+ makeByNameReference(A::kAbsHigh16, srcAddr, targetName, offsetInTarget);
+ }
+ else {
+ dstAddr = ((instruction & 0x0000FFFF) << 16) | (nextReloc->r_address() & 0x0000FFFF);
+ if ( reloc->r_symbolnum() == R_ABS ) {
+ // find absolute symbol that corresponds to pointerValue
+ typename AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr);
+ if ( pos != fAddrToAbsoluteAtom.end() )
+ makeByNameReference(A::kAbsHigh16, srcAddr, pos->second->getName(), 0);
+ else
+ makeReference(A::kAbsHigh16, srcAddr, dstAddr);
+ }
+ else {
+ makeReference(A::kAbsHigh16, srcAddr, dstAddr);
+ }
+ }
+ }
+ break;
+ case PPC_RELOC_HA16:
+ {
+ if ( nextReloc->r_type() != PPC_RELOC_PAIR ) {
+ throw "PPC_RELOC_HA16 missing following pair";
+ }
+ result = true;
+ lowBits = (nextReloc->r_address() & 0x0000FFFF);
+ if ( reloc->r_extern() ) {
+ offsetInTarget = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits;
+ makeByNameReference(A::kAbsHigh16AddLow, srcAddr, targetName, offsetInTarget);
+ }
+ else {
+ dstAddr = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits;
+ if ( reloc->r_symbolnum() == R_ABS ) {
+ // find absolute symbol that corresponds to pointerValue
+ typename AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr);
+ if ( pos != fAddrToAbsoluteAtom.end() )
+ makeByNameReference(A::kAbsHigh16AddLow, srcAddr, pos->second->getName(), 0);
+ else
+ makeReference(A::kAbsHigh16AddLow, srcAddr, dstAddr);
+ }
+ else {
+ makeReference(A::kAbsHigh16AddLow, srcAddr, dstAddr);
+ }
+ }
+ }
+ break;
+ case PPC_RELOC_VANILLA:
+ {
+ pint_t pointerValue = P::getP(*((pint_t*)fixUpPtr));
+ if ( reloc->r_extern() ) {
+ if ( weakImport )
+ makeByNameReference(A::kPointerWeakImport, srcAddr, targetName, pointerValue);
+ else
+ makeByNameReference(A::kPointer, srcAddr, targetName, pointerValue);
+ }
+ else {
+ makeReference(A::kPointer, srcAddr, pointerValue);
+ }
+ }
+ break;
+ case PPC_RELOC_JBSR:
+ // this is from -mlong-branch codegen. We ignore the jump island and make reference to the real target
+ if ( nextReloc->r_type() != PPC_RELOC_PAIR ) {
+ throw "PPC_RELOC_JBSR missing following pair";
+ }
+ if ( !fHasLongBranchStubs )
+ warning("object file compiled with -mlong-branch which is no longer needed. To remove this warning, recompile without -mlong-branch: %s", fPath);
+ fHasLongBranchStubs = true;
+ result = true;
+ if ( reloc->r_extern() ) {
+ throw "PPC_RELOC_JBSR should not be using an external relocation";
+ }
+ makeReference(A::kBranch24, srcAddr, nextReloc->r_address());
+ if ( (instruction & 0x4C000000) == 0x48000000 ) {
+ displacement = (instruction & 0x03FFFFFC);
+ if ( (displacement & 0x02000000) != 0 )
+ displacement |= 0xFC000000;
+ }
+ else {
+ fprintf(stderr, "bad instruction for BR24 reloc");
+ }
+ break;
+ default:
+ warning("unknown relocation type %d", reloc->r_type());
+ }
+ }
+ else {
+ const macho_scattered_relocation_info<P>* sreloc = (macho_scattered_relocation_info<P>*)reloc;
+ srcAddr = sect->addr() + sreloc->r_address();
+ dstAddr = sreloc->r_value();
+ uint32_t betterDstAddr;
+ fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + sreloc->r_address());
+ const macho_scattered_relocation_info<P>* nextSReloc = &sreloc[1];
+ const macho_relocation_info<P>* nextReloc = &reloc[1];
+ // file format allows pair to be scattered or not
+ bool nextRelocIsPair = false;
+ uint32_t nextRelocAddress = 0;
+ uint32_t nextRelocValue = 0;
+ if ( (nextReloc->r_address() & R_SCATTERED) == 0 ) {
+ if ( nextReloc->r_type() == PPC_RELOC_PAIR ) {
+ nextRelocIsPair = true;
+ nextRelocAddress = nextReloc->r_address();
+ result = true;
+ }
+ }
+ else {
+ if ( nextSReloc->r_type() == PPC_RELOC_PAIR ) {
+ nextRelocIsPair = true;
+ nextRelocAddress = nextSReloc->r_address();
+ nextRelocValue = nextSReloc->r_value();
+ result = true;
+ }
+ }
+ switch (sreloc->r_type()) {
+ case PPC_RELOC_VANILLA:
+ {
+ betterDstAddr = P::getP(*(pint_t*)fixUpPtr);
+ //fprintf(stderr, "scattered pointer reloc: srcAddr=0x%08X, dstAddr=0x%08X, pointer=0x%08X\n", srcAddr, dstAddr, betterDstAddr);
+ // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr)
+ makeReferenceWithToBase(A::kPointer, srcAddr, betterDstAddr, dstAddr);
+ }
+ break;
+ case PPC_RELOC_BR14:
+ {
+ instruction = BigEndian::get32(*fixUpPtr);
+ displacement = (instruction & 0x0000FFFC);
+ if ( (displacement & 0x00008000) != 0 )
+ displacement |= 0xFFFF0000;
+ betterDstAddr = srcAddr+displacement;
+ //fprintf(stderr, "betterDstAddr=0x%08X, srcAddr=0x%08X, displacement=0x%08X\n", betterDstAddr, srcAddr, displacement);
+ makeReferenceWithToBase(A::kBranch14, srcAddr, betterDstAddr, dstAddr);
+ }
+ break;
+ case PPC_RELOC_BR24:
+ {
+ instruction = BigEndian::get32(*fixUpPtr);
+ if ( (instruction & 0x4C000000) == 0x48000000 ) {
+ displacement = (instruction & 0x03FFFFFC);
+ if ( (displacement & 0x02000000) != 0 )
+ displacement |= 0xFC000000;
+ betterDstAddr = srcAddr+displacement;
+ makeReferenceWithToBase(A::kBranch24, srcAddr, betterDstAddr, dstAddr);
+ }
+ }
+ break;
+ case PPC_RELOC_LO16_SECTDIFF:
+ {
+ if ( ! nextRelocIsPair ) {
+ throw "PPC_RELOC_LO16_SECTDIFF missing following pair";
+ }
+ instruction = BigEndian::get32(*fixUpPtr);
+ lowBits = (instruction & 0xFFFF);
+ displacement = (nextRelocAddress << 16) | ((uint32_t)lowBits & 0x0000FFFF);
+ makeReferenceWithToBase(A::kPICBaseLow16, srcAddr, nextRelocValue, nextRelocValue + displacement, dstAddr);
+ }
+ break;
+ case PPC_RELOC_LO14_SECTDIFF:
+ {
+ if ( ! nextRelocIsPair ) {
+ throw "PPC_RELOC_LO14_SECTDIFF missing following pair";
+ }
+ instruction = BigEndian::get32(*fixUpPtr);
+ lowBits = (instruction & 0xFFFC);
+ displacement = (nextRelocAddress << 16) | ((uint32_t)lowBits & 0x0000FFFF);
+ makeReferenceWithToBase(A::kPICBaseLow14, srcAddr, nextRelocValue, nextRelocValue + displacement, dstAddr);
+ }
+ break;
+ case PPC_RELOC_HA16_SECTDIFF:
+ {
+ if ( ! nextRelocIsPair ) {
+ throw "PPC_RELOC_HA16_SECTDIFF missing following pair";
+ }
+ instruction = BigEndian::get32(*fixUpPtr);
+ lowBits = (nextRelocAddress & 0x0000FFFF);
+ displacement = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits;
+ makeReferenceWithToBase(A::kPICBaseHigh16, srcAddr, nextRelocValue, nextRelocValue + displacement, dstAddr);
+ }
+ break;
+ case PPC_RELOC_LO14:
+ {
+ if ( ! nextRelocIsPair ) {
+ throw "PPC_RELOC_LO14 missing following pair";
+ }
+ instruction = BigEndian::get32(*fixUpPtr);
+ lowBits = (instruction & 0xFFFC);
+ betterDstAddr = (nextRelocAddress << 16) + ((uint32_t)lowBits & 0x0000FFFF);
+ makeReferenceWithToBase(A::kAbsLow14, srcAddr, betterDstAddr, dstAddr);
+ }
+ break;
+ case PPC_RELOC_LO16:
+ {
+ if ( ! nextRelocIsPair ) {
+ throw "PPC_RELOC_LO16 missing following pair";
+ }
+ instruction = BigEndian::get32(*fixUpPtr);
+ lowBits = (instruction & 0xFFFF);
+ betterDstAddr = (nextRelocAddress << 16) + ((uint32_t)lowBits & 0x0000FFFF);
+ makeReferenceWithToBase(A::kAbsLow16, srcAddr, betterDstAddr, dstAddr);
+ }
+ break;
+ case PPC_RELOC_HA16:
+ {
+ if ( ! nextRelocIsPair ) {
+ throw "PPC_RELOC_HA16 missing following pair";
+ }
+ instruction = BigEndian::get32(*fixUpPtr);
+ lowBits = (nextRelocAddress & 0xFFFF);
+ betterDstAddr = ((instruction & 0xFFFF) << 16) + (int32_t)lowBits;
+ makeReferenceWithToBase(A::kAbsHigh16AddLow, srcAddr, betterDstAddr, dstAddr);
+ }
+ break;
+ case PPC_RELOC_HI16:
+ {
+ if ( ! nextRelocIsPair ) {
+ throw "PPC_RELOC_HI16 missing following pair";
+ }
+ instruction = BigEndian::get32(*fixUpPtr);
+ lowBits = (nextRelocAddress & 0xFFFF);
+ betterDstAddr = ((instruction & 0xFFFF) << 16) | (lowBits & 0x0000FFFF);
+ makeReferenceWithToBase(A::kAbsHigh16, srcAddr, betterDstAddr, dstAddr);
+ }
+ break;
+ case PPC_RELOC_SECTDIFF:
+ case PPC_RELOC_LOCAL_SECTDIFF:
+ {
+ if ( ! nextRelocIsPair ) {
+ throw "PPC_RELOC_SECTDIFF missing following pair";
+ }
+ Kinds kind = A::kPointerDiff32;;
+ uint32_t contentAddr = 0;
+ switch ( sreloc->r_length() ) {
+ case 0:
+ throw "bad diff relocations r_length (0) for ppc architecture";
+ case 1:
+ kind = A::kPointerDiff16;
+ contentAddr = BigEndian::get16(*((uint16_t*)fixUpPtr));
+ break;
+ case 2:
+ kind = A::kPointerDiff32;
+ contentAddr = BigEndian::get32(*fixUpPtr);
+ break;
+ case 3:
+ kind = A::kPointerDiff64;
+ contentAddr = BigEndian::get64(*((uint64_t*)fixUpPtr));
+ break;
+ }
+ AtomAndOffset srcao = findAtomAndOffset(srcAddr);
+ AtomAndOffset fromao = findAtomAndOffset(nextRelocValue);
+ AtomAndOffset toao = findAtomAndOffset(dstAddr);
+ // check for addend encoded in the section content
+ //fprintf(stderr, "addRef: dstAddr=0x%X, nextRelocValue=0x%X, contentAddr=0x%X\n",
+ // dstAddr, nextRelocValue, contentAddr);
+ if ( (dstAddr - nextRelocValue) != contentAddr ) {
+ if ( toao.atom == srcao.atom )
+ toao.offset += (contentAddr + nextRelocValue) - dstAddr;
+ else if ( fromao.atom == srcao.atom )
+ toao.offset += (contentAddr + nextRelocValue) - dstAddr;
+ else
+ fromao.offset += (dstAddr - contentAddr) - nextRelocValue;
+ }
+ //fprintf(stderr, "addRef: src=%s+0x%X, from=%s+0x%X, to=%s+0x%X\n",
+ // srcao.atom->getDisplayName(), srcao.offset,
+ // fromao.atom->getDisplayName(), fromao.offset,
+ // toao.atom->getDisplayName(), toao.offset);
+ new Reference<A>(kind, srcao, fromao, toao);
+ }
+ break;
+ case PPC_RELOC_PAIR:
+ break;
+ case PPC_RELOC_HI16_SECTDIFF:
+ warning("unexpected scattered relocation type PPC_RELOC_HI16_SECTDIFF");
+ break;
+ default:
+ warning("unknown scattered relocation type %d", sreloc->r_type());
+ }
+ }
+ return result;
+}
+
+
+template <>
+bool Reader<x86>::addRelocReference(const macho_section<x86::P>* sect, const macho_relocation_info<x86::P>* reloc)
+{
+ uint32_t srcAddr;
+ uint32_t dstAddr;
+ uint32_t* fixUpPtr;
+ bool result = false;
+ if ( (reloc->r_address() & R_SCATTERED) == 0 ) {
+ srcAddr = sect->addr() + reloc->r_address();
+ fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address());
+ switch ( reloc->r_type() ) {
+ case GENERIC_RELOC_VANILLA:
+ {
+ x86::ReferenceKinds kind = x86::kPointer;
+ uint32_t pointerValue = E::get32(*fixUpPtr);
+ if ( reloc->r_pcrel() ) {
+ switch( reloc->r_length() ) {
+ case 0:
+ kind = x86::kPCRel8;
+ pointerValue = srcAddr + *((int8_t*)fixUpPtr) + sizeof(int8_t);
+ break;
+ case 1:
+ kind = x86::kPCRel16;
+ pointerValue = srcAddr + (int16_t)E::get16(*((uint16_t*)fixUpPtr)) + sizeof(uint16_t);
+ break;
+ case 2:
+ kind = x86::kPCRel32;
+ pointerValue += srcAddr + sizeof(uint32_t);
+ break;
+ case 3:
+ throw "bad pc-rel vanilla relocation length";
+ }
+ }
+ else if ( strcmp(sect->segname(), "__TEXT") == 0 ) {
+ kind = x86::kAbsolute32;
+ if ( reloc->r_length() != 2 )
+ throw "bad vanilla relocation length";
+ }
+ else {
+ kind = x86::kPointer;
+ if ( reloc->r_length() != 2 )
+ throw "bad vanilla relocation length";
+ }
+ if ( reloc->r_extern() ) {
+ const macho_nlist<P>* targetSymbol = &fSymbols[reloc->r_symbolnum()];
+ if ( this->isWeakImportSymbol(targetSymbol) ) {
+ if ( reloc->r_pcrel() )
+ kind = x86::kPCRel32WeakImport;
+ else
+ kind = x86::kPointerWeakImport;
+ }
+ const char* targetName = &fStrings[targetSymbol->n_strx()];
+ if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) {
+ makeByNameReference(x86::kDtraceProbeSite, srcAddr, targetName, 0);
+ addDtraceExtraInfos(srcAddr, &targetName[16]);
+ }
+ else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) {
+ makeByNameReference(x86::kDtraceIsEnabledSite, srcAddr, targetName, 0);
+ addDtraceExtraInfos(srcAddr, &targetName[20]);
+ }
+ else
+ makeByNameReference(kind, srcAddr, targetName, pointerValue);
+ }
+ else {
+ // if this is a branch to a stub, we need to see if the stub is for a weak imported symbol
+ ObjectFile::Atom* atom = findAtomAndOffset(pointerValue).atom;
+ const char* targetName = atom->getName();
+ if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_probe$", 16) == 0) ) {
+ makeByNameReference(x86::kDtraceProbeSite, srcAddr, targetName, 0);
+ addDtraceExtraInfos(srcAddr, &targetName[16]);
+ }
+ else if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_isenabled$", 20) == 0) ) {
+ makeByNameReference(x86::kDtraceIsEnabledSite, srcAddr, targetName, 0);
+ addDtraceExtraInfos(srcAddr, &targetName[20]);
+ }
+ else if ( reloc->r_pcrel() && (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn)
+ && ((AnonymousAtom<x86>*)atom)->isWeakImportStub() )
+ makeReference(x86::kPCRel32WeakImport, srcAddr, pointerValue);
+ else if ( reloc->r_symbolnum() != R_ABS )
+ makeReference(kind, srcAddr, pointerValue);
+ else {
+ // find absolute symbol that corresponds to pointerValue
+ AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(pointerValue);
+ if ( pos != fAddrToAbsoluteAtom.end() )
+ makeByNameReference(kind, srcAddr, pos->second->getName(), 0);
+ else
+ throwf("R_ABS reloc but no absolute symbol at target address");
+ }
+ }
+ }
+ break;
+ default:
+ warning("unknown relocation type %d", reloc->r_type());
+ }
+ }
+ else {
+ const macho_scattered_relocation_info<P>* sreloc = (macho_scattered_relocation_info<P>*)reloc;
+ srcAddr = sect->addr() + sreloc->r_address();
+ dstAddr = sreloc->r_value();
+ fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + sreloc->r_address());
+ const macho_scattered_relocation_info<P>* nextSReloc = &sreloc[1];
+ const macho_relocation_info<P>* nextReloc = &reloc[1];
+ pint_t betterDstAddr;
+ // file format allows pair to be scattered or not
+ bool nextRelocIsPair = false;
+ uint32_t nextRelocAddress = 0;
+ uint32_t nextRelocValue = 0;
+ if ( (nextReloc->r_address() & R_SCATTERED) == 0 ) {
+ if ( nextReloc->r_type() == GENERIC_RELOC_PAIR ) {
+ nextRelocIsPair = true;
+ nextRelocAddress = nextReloc->r_address();
+ result = true;
+ }
+ }
+ else {
+ if ( nextSReloc->r_type() == GENERIC_RELOC_PAIR ) {
+ nextRelocIsPair = true;
+ nextRelocAddress = nextSReloc->r_address();
+ nextRelocValue = nextSReloc->r_value();
+ }
+ }
+ switch (sreloc->r_type()) {
+ case GENERIC_RELOC_VANILLA:
+ betterDstAddr = LittleEndian::get32(*fixUpPtr);
+ //fprintf(stderr, "pointer reloc: srcAddr=0x%08X, dstAddr=0x%08X, pointer=0x%08lX\n", srcAddr, dstAddr, betterDstAddr);
+ // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr)
+ if ( sreloc->r_pcrel() ) {
+ switch ( sreloc->r_length() ) {
+ case 2:
+ betterDstAddr += srcAddr + 4;
+ makeReferenceWithToBase(x86::kPCRel32, srcAddr, betterDstAddr, dstAddr);
+ break;
+ case 1:
+ betterDstAddr = LittleEndian::get16(*((uint16_t*)fixUpPtr)) + srcAddr + 2;
+ makeReferenceWithToBase(x86::kPCRel16, srcAddr, betterDstAddr, dstAddr);
+ break;
+ case 0:
+ betterDstAddr = *((uint8_t*)fixUpPtr) + srcAddr + 1;
+ makeReferenceWithToBase(x86::kPCRel8, srcAddr, betterDstAddr, dstAddr);
+ break;
+ case 3:
+ throwf("unsupported r_length=3 for scattered pc-rel vanilla reloc");
+ break;
+ }
+ }
+ else {
+ if ( sreloc->r_length() != 2 )
+ throwf("unsupported r_length=%d for scattered vanilla reloc", sreloc->r_length());
+ if ( strcmp(sect->segname(), "__TEXT") == 0 )
+ makeReferenceWithToBase(x86::kAbsolute32, srcAddr, betterDstAddr, dstAddr);
+ else
+ makeReferenceWithToBase(x86::kPointer, srcAddr, betterDstAddr, dstAddr);
+ }
+ break;
+ case GENERIC_RELOC_SECTDIFF:
+ case GENERIC_RELOC_LOCAL_SECTDIFF:
+ {
+ if ( !nextRelocIsPair ) {
+ throw "GENERIC_RELOC_SECTDIFF missing following pair";
+ }
+ x86::ReferenceKinds kind = x86::kPointerDiff;
+ uint32_t contentAddr = 0;
+ switch ( sreloc->r_length() ) {
+ case 0:
+ case 3:
+ throw "bad length for GENERIC_RELOC_SECTDIFF";
+ case 1:
+ kind = x86::kPointerDiff16;
+ contentAddr = LittleEndian::get16(*((uint16_t*)fixUpPtr));
+ break;
+ case 2:
+ kind = x86::kPointerDiff;
+ contentAddr = LittleEndian::get32(*fixUpPtr);
+ break;
+ }
+ AtomAndOffset srcao = findAtomAndOffset(srcAddr);
+ AtomAndOffset fromao = findAtomAndOffset(nextRelocValue);
+ AtomAndOffset toao = findAtomAndOffset(dstAddr);
+ // check for addend encoded in the section content
+ //fprintf(stderr, "addRef: dstAddr=0x%X, nextRelocValue=0x%X, contentAddr=0x%X\n",
+ // dstAddr, nextRelocValue, contentAddr);
+ if ( (dstAddr - nextRelocValue) != contentAddr ) {
+ if ( toao.atom == srcao.atom )
+ toao.offset += (contentAddr + nextRelocValue) - dstAddr;
+ else if ( fromao.atom == srcao.atom )
+ toao.offset += (contentAddr + nextRelocValue) - dstAddr;
+ else
+ fromao.offset += (dstAddr - contentAddr) - nextRelocValue;
+ }
+ //fprintf(stderr, "addRef: src=%s+0x%X, from=%s+0x%X, to=%s+0x%X\n",
+ // srcao.atom->getDisplayName(), srcao.offset,
+ // fromao.atom->getDisplayName(), fromao.offset,
+ // toao.atom->getDisplayName(), toao.offset);
+ new Reference<x86>(kind, srcao, fromao, toao);
+ }
+ break;
+ case GENERIC_RELOC_PAIR:
+ // do nothing, already used via a look ahead
+ break;
+ default:
+ warning("unknown scattered relocation type %d", sreloc->r_type());
+ }
+ }
+ 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 = x86_64::kNoFixUp;
+ 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, len=%d, address=0x%X\n", reloc->r_type(), reloc->r_length(), reloc->r_address());
+ 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";
+ switch ( reloc->r_length() ) {
+ case 0:
+ case 1:
+ throw "length < 2 and X86_64_RELOC_UNSIGNED not supported";
+ case 2:
+ kind = x86_64::kPointer32;
+ break;
+ case 3:
+ if ( reloc->r_extern() && isWeakImportSymbol(targetSymbol) )
+ kind = x86_64::kPointerWeakImport;
+ else
+ kind = x86_64::kPointer;
+ break;
+ }
+ dstAddr = E::get64(*((uint64_t*)fixUpPtr));
+ if ( reloc->r_extern() ) {
+ makeReferenceToSymbol(kind, srcAddr, targetSymbol, dstAddr);
+ }
+ else {
+ makeReference(kind, srcAddr, dstAddr);
+ // verify that dstAddr is in the section being targeted
+ int sectNum = reloc->r_symbolnum();
+ const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)fSegment + sizeof(macho_segment_command<P>));
+ const macho_section<P>* const targetSection = §ionsStart[sectNum-1];
+ if ( (dstAddr < targetSection->addr()) || (dstAddr > (targetSection->addr()+targetSection->size())) ) {
+ throwf("local relocation for address 0x%08llX in section %s does not target section %s",
+ srcAddr, sect->sectname(), targetSection->sectname());
+ }
+ }
+ break;
+ case X86_64_RELOC_SIGNED:
+ case X86_64_RELOC_SIGNED_1:
+ case X86_64_RELOC_SIGNED_2:
+ case X86_64_RELOC_SIGNED_4:
+ 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";
+ addend = (int64_t)((int32_t)(E::get32(*fixUpPtr)));
+ if ( reloc->r_extern() ) {
+ switch ( reloc->r_type() ) {
+ case X86_64_RELOC_SIGNED:
+ kind = x86_64::kPCRel32;
+ // begin support for old .o files before X86_64_RELOC_SIGNED_1 was created
+ if ( addend == (uint64_t)(-1) ) {
+ addend = 0;
+ kind = x86_64::kPCRel32_1;
+ }
+ else if ( addend == (uint64_t)(-2) ) {
+ addend = 0;
+ kind = x86_64::kPCRel32_2;
+ }
+ else if ( addend == (uint64_t)(-4) ) {
+ addend = 0;
+ kind = x86_64::kPCRel32_4;
+ }
+ break;
+ // end support for old .o files before X86_64_RELOC_SIGNED_1 was created
+ case X86_64_RELOC_SIGNED_1:
+ kind = x86_64::kPCRel32_1;
+ addend += 1;
+ break;
+ case X86_64_RELOC_SIGNED_2:
+ kind = x86_64::kPCRel32_2;
+ addend += 2;
+ break;
+ case X86_64_RELOC_SIGNED_4:
+ kind = x86_64::kPCRel32_4;
+ addend += 4;
+ break;
+ }
+ makeReferenceToSymbol(kind, srcAddr, targetSymbol, addend);
+ }
+ else {
+ uint64_t ripRelativeOffset = addend;
+ switch ( reloc->r_type() ) {
+ case X86_64_RELOC_SIGNED:
+ dstAddr = srcAddr + 4 + ripRelativeOffset;
+ kind = x86_64::kPCRel32;
+ break;
+ case X86_64_RELOC_SIGNED_1:
+ dstAddr = srcAddr + 5 + ripRelativeOffset;
+ kind = x86_64::kPCRel32_1;
+ break;
+ case X86_64_RELOC_SIGNED_2:
+ dstAddr = srcAddr + 6 + ripRelativeOffset;
+ kind = x86_64::kPCRel32_2;
+ break;
+ case X86_64_RELOC_SIGNED_4:
+ dstAddr = srcAddr + 8 + ripRelativeOffset;
+ kind = x86_64::kPCRel32_4;
+ break;
+ }
+ makeReference(kind, srcAddr, dstAddr);
+ // verify that dstAddr is in the section being targeted
+ int sectNum = reloc->r_symbolnum();
+ const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)fSegment + sizeof(macho_segment_command<P>));
+ const macho_section<P>* const targetSection = §ionsStart[sectNum-1];
+ if ( (dstAddr < targetSection->addr()) || (dstAddr > (targetSection->addr()+targetSection->size())) ) {
+ throwf("local relocation for address 0x%08llX in section %s does not target section %s",
+ srcAddr, sect->sectname(), targetSection->sectname());
+ }
+ }
+ 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 ) {
+ dstAddr = (int64_t)((int32_t)(E::get32(*fixUpPtr)));
+ if ( reloc->r_extern() ) {
+ if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) {
+ makeByNameReference(x86_64::kDtraceProbeSite, srcAddr, targetName, 0);
+ addDtraceExtraInfos(srcAddr, &targetName[16]);
+ }
+ else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) {
+ makeByNameReference(x86_64::kDtraceIsEnabledSite, srcAddr, targetName, 0);
+ addDtraceExtraInfos(srcAddr, &targetName[16]);
+ }
+ else 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);
+ }
+ }
+ else if ( reloc->r_length() == 0 ) {
+ dstAddr = *((int8_t*)fixUpPtr);
+ if ( reloc->r_extern() ) {
+ makeReferenceToSymbol(x86_64::kBranchPCRel8, srcAddr, targetSymbol, dstAddr);
+ }
+ else {
+ makeReference(x86_64::kBranchPCRel8, srcAddr, srcAddr+1+dstAddr);
+ }
+ }
+ else {
+ throwf("length=%d and X86_64_RELOC_BRANCH not supported", reloc->r_length());;
+ }
+ 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);
+ }
+ AtomAndOffset inAtomAndOffset = this->findAtomAndOffset(srcAddr);
+ ObjectFile::Atom* inAtom = inAtomAndOffset.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);
+ }
+ }
+ else {
+ throw "X86_64_RELOC_SUBTRACTOR not supported with r_extern=0";
+ }
+ // addend goes in from side iff negative
+ if ( negativeAddend )
+ ref->setFromTargetOffset(-dstAddr);
+ else
+ ref->setToTargetOffset(dstAddr);
+ break;
+ }
+ default:
+ warning("unknown relocation type %d", reloc->r_type());
+ }
+ return result;
+}
+
+
+/// Reader<arm>::addRelocReference -
+/// turns arm relocation entries into references. Returns true if the next
+/// relocation should be skipped, false otherwise.
+template <>
+bool Reader<arm>::addRelocReference(const macho_section<arm::P>* sect,
+ const macho_relocation_info<arm::P>* reloc)
+{
+ uint32_t * fixUpPtr;
+ int32_t displacement;
+ uint32_t instruction = 0;
+ bool result = false;
+ uint32_t srcAddr;
+ uint32_t dstAddr;
+ uint32_t pointerValue;
+ arm::ReferenceKinds kind = arm::kNoFixUp;
+
+ if ( (reloc->r_address() & R_SCATTERED) == 0 ) {
+ // non-scattered relocation
+ const char* targetName = NULL;
+ bool weakImport = false;
+
+ srcAddr = sect->addr() + reloc->r_address();
+ fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address());
+ if ( reloc->r_type() != ARM_RELOC_PAIR )
+ instruction = LittleEndian::get32(*fixUpPtr);
+
+ if ( reloc->r_extern() ) {
+ const macho_nlist<P>* targetSymbol = &fSymbols[reloc->r_symbolnum()];
+ targetName = &fStrings[targetSymbol->n_strx()];
+ weakImport = this->isWeakImportSymbol(targetSymbol);
+ }
+
+ switch ( reloc->r_type() ) {
+ case ARM_RELOC_BR24:
+ // Sign-extend displacement
+ displacement = (instruction & 0x00FFFFFF) << 2;
+ if ( (displacement & 0x02000000) != 0 )
+ displacement |= 0xFC000000;
+ // The pc added will be +8 from the pc
+ displacement += 8;
+ // If this is BLX add H << 1
+ if ((instruction & 0xFE000000) == 0xFA000000)
+ displacement += ((instruction & 0x01000000) >> 23);
+
+ if ( reloc->r_extern() ) {
+ uint32_t offsetInTarget = srcAddr + displacement;
+ if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) {
+ makeByNameReference(arm::kDtraceProbeSite, srcAddr, targetName, 0);
+ addDtraceExtraInfos(srcAddr, &targetName[16]);
+ }
+ else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) {
+ makeByNameReference(arm::kDtraceIsEnabledSite, srcAddr, targetName, 0);
+ addDtraceExtraInfos(srcAddr, &targetName[20]);
+ }
+ else if ( weakImport )
+ makeByNameReference(arm::kBranch24WeakImport, srcAddr, targetName, offsetInTarget);
+ else
+ makeByNameReference(arm::kBranch24, srcAddr, targetName, offsetInTarget);
+ }
+ else {
+ dstAddr = srcAddr + displacement;
+ ObjectFile::Atom* atom = findAtomAndOffset(dstAddr).atom;
+ // check for dtrace probes and weak_import stubs
+ const char* targetName = atom->getName();
+ if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_probe$", 16) == 0) ) {
+ makeByNameReference(arm::kDtraceProbeSite, srcAddr, targetName, 0);
+ addDtraceExtraInfos(srcAddr, &targetName[16]);
+ }
+ else if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_isenabled$", 20) == 0) ) {
+ makeByNameReference(arm::kDtraceIsEnabledSite, srcAddr, targetName, 0);
+ addDtraceExtraInfos(srcAddr, &targetName[20]);
+ }
+ else if ( (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn)
+ && ((AnonymousAtom<x86>*)atom)->isWeakImportStub() )
+ makeReference(arm::kBranch24WeakImport, srcAddr, dstAddr);
+ else if ( reloc->r_symbolnum() != R_ABS )
+ makeReference(arm::kBranch24, srcAddr, dstAddr);
+ else {
+ // find absolute symbol that corresponds to pointerValue
+ AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr);
+ if ( pos != fAddrToAbsoluteAtom.end() )
+ makeByNameReference(arm::kBranch24, srcAddr, pos->second->getName(), 0);
+ else
+ throwf("R_ABS reloc but no absolute symbol at target address");
+ }
+ }
+ break;
+
+ case ARM_THUMB_RELOC_BR22:
+ // thumb2 added two more bits to displacement, complicating the displacement decoding
+ {
+ uint32_t s = (instruction >> 10) & 0x1;
+ uint32_t j1 = (instruction >> 29) & 0x1;
+ uint32_t j2 = (instruction >> 27) & 0x1;
+ uint32_t imm10 = instruction & 0x3FF;
+ uint32_t imm11 = (instruction >> 16) & 0x7FF;
+ uint32_t i1 = (j1 == s);
+ uint32_t i2 = (j2 == s);
+ uint32_t dis = (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1);
+ int32_t sdis = dis;
+ if ( s )
+ sdis |= 0xFE000000;
+ displacement = sdis;
+ }
+ // The pc added will be +4 from the pc
+ displacement += 4;
+ // If the instruction was blx, force the low 2 bits to be clear
+ dstAddr = srcAddr + displacement;
+ if ((instruction & 0xF8000000) == 0xE8000000)
+ dstAddr &= 0xFFFFFFFC;
+
+ if ( reloc->r_extern() ) {
+ uint32_t offsetInTarget = dstAddr;
+ if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) {
+ makeByNameReference(arm::kDtraceProbeSite, srcAddr, targetName, 0);
+ addDtraceExtraInfos(srcAddr, &targetName[16]);
+ }
+ else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) {
+ makeByNameReference(arm::kDtraceIsEnabledSite, srcAddr, targetName, 0);
+ addDtraceExtraInfos(srcAddr, &targetName[20]);
+ }
+ else if ( weakImport )
+ makeByNameReference(arm::kThumbBranch22WeakImport, srcAddr, targetName, offsetInTarget);
+ else
+ makeByNameReference(arm::kThumbBranch22, srcAddr, targetName, offsetInTarget);
+ }
+ else {
+ ObjectFile::Atom* atom = findAtomAndOffset(dstAddr).atom;
+ // check for dtrace probes and weak_import stubs
+ const char* targetName = atom->getName();
+ if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_probe$", 16) == 0) ) {
+ makeByNameReference(arm::kDtraceProbeSite, srcAddr, targetName, 0);
+ addDtraceExtraInfos(srcAddr, &targetName[16]);
+ }
+ else if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_isenabled$", 20) == 0) ) {
+ makeByNameReference(arm::kDtraceIsEnabledSite, srcAddr, targetName, 0);
+ addDtraceExtraInfos(srcAddr, &targetName[20]);
+ }
+ else if ( (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn)
+ && ((AnonymousAtom<x86>*)atom)->isWeakImportStub() )
+ makeReference(arm::kThumbBranch22WeakImport, srcAddr, dstAddr);
+ else if ( reloc->r_symbolnum() != R_ABS )
+ makeReference(arm::kThumbBranch22, srcAddr, dstAddr);
+ else {
+ // find absolute symbol that corresponds to pointerValue
+ AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr);
+ if ( pos != fAddrToAbsoluteAtom.end() )
+ makeByNameReference(arm::kThumbBranch22, srcAddr, pos->second->getName(), 0);
+ else
+ throwf("R_ABS reloc but no absolute symbol at target address");
+ }
+ }
+ break;
+
+ case ARM_RELOC_VANILLA:
+ if ( reloc->r_length() != 2 )
+ throw "bad length for ARM_RELOC_VANILLA";
+
+ pointerValue = instruction;
+ kind = arm::kPointer;
+ if ( strcmp(sect->segname(), "__TEXT") == 0 )
+ kind = arm::kReadOnlyPointer;
+ if ( weakImport )
+ kind = arm::kPointerWeakImport;
+ if ( reloc->r_extern() ) {
+ makeByNameReference(kind, srcAddr, targetName, pointerValue);
+ }
+ else {
+ AtomAndOffset at = findAtomAndOffset(srcAddr);
+ AtomAndOffset to = findAtomAndOffset(pointerValue);
+ if ( to.atom->isThumb() )
+ to.offset &= -2;
+ new Reference<arm>(kind, at, to);
+ }
+ break;
+
+ case ARM_THUMB_32BIT_BRANCH:
+ // work around for <rdar://problem/6489480>
+ break;
+
+ default:
+ warning("unexpected relocation type %u", reloc->r_type());
+ break;
+ }
+ }
+ else {
+ const macho_scattered_relocation_info<P>* sreloc = (macho_scattered_relocation_info<P>*)reloc;
+ const macho_scattered_relocation_info<P>* nextSReloc = &sreloc[1];
+ srcAddr = sect->addr() + sreloc->r_address();
+ dstAddr = sreloc->r_value();
+ uint32_t betterDstAddr;
+ fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + sreloc->r_address());
+ instruction = LittleEndian::get32(*fixUpPtr);
+
+ // A ARM_RELOC_PAIR only follows ARM_RELOC_{SECTDIFF,LOCAL_SECTDIFF}
+ // relocation types, and it is an error to see one otherwise.
+ bool nextRelocIsPair = false;
+ uint32_t nextRelocAddress = 0;
+ uint32_t nextRelocValue = 0;
+ if ( nextSReloc->r_type() == ARM_RELOC_PAIR ) {
+ nextRelocIsPair = true;
+ nextRelocAddress = nextSReloc->r_address();
+ nextRelocValue = nextSReloc->r_value();
+ result = true;
+ }
+
+ switch (sreloc->r_type()) {
+ case ARM_RELOC_VANILLA:
+ if ( sreloc->r_length() != 2 )
+ throw "bad length for ARM_RELOC_VANILLA";
+
+ //fprintf(stderr, "scattered pointer reloc: srcAddr=0x%08X, dstAddr=0x%08X, pointer=0x%08X\n", srcAddr, dstAddr, betterDstAddr);
+ betterDstAddr = LittleEndian::get32(*fixUpPtr);
+ kind = arm::kPointer;
+ if ( strcmp(sect->segname(), "__TEXT") == 0 )
+ kind = arm::kReadOnlyPointer;
+ // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr)
+ makeReferenceWithToBase(kind, srcAddr, betterDstAddr, dstAddr);
+ break;
+
+ case ARM_RELOC_BR24:
+ // Sign-extend displacement
+ displacement = (instruction & 0x00FFFFFF) << 2;
+ if ( (displacement & 0x02000000) != 0 )
+ displacement |= 0xFC000000;
+ // The pc added will be +8 from the pc
+ displacement += 8;
+ // If this is BLX add H << 1
+ if ((instruction & 0xFE000000) == 0xFA000000)
+ displacement += ((instruction & 0x01000000) >> 23);
+ betterDstAddr = srcAddr+displacement;
+ makeReferenceWithToBase(arm::kBranch24, srcAddr, betterDstAddr, dstAddr);
+ break;
+
+ case ARM_THUMB_RELOC_BR22:
+ // thumb2 added two more bits to displacement, complicating the displacement decoding
+ {
+ uint32_t s = (instruction >> 10) & 0x1;
+ uint32_t j1 = (instruction >> 29) & 0x1;
+ uint32_t j2 = (instruction >> 27) & 0x1;
+ uint32_t imm10 = instruction & 0x3FF;
+ uint32_t imm11 = (instruction >> 16) & 0x7FF;
+ uint32_t i1 = (j1 == s);
+ uint32_t i2 = (j2 == s);
+ uint32_t dis = (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1);
+ int32_t sdis = dis;
+ if ( s )
+ sdis |= 0xFE000000;
+ displacement = sdis;
+ }
+ // The pc added will be +4 from the pc
+ displacement += 4;
+ betterDstAddr = srcAddr+displacement;
+ // If the instruction was blx, force the low 2 bits to be clear
+ if ((instruction & 0xF8000000) == 0xE8000000)
+ betterDstAddr &= 0xFFFFFFFC;
+ makeReferenceWithToBase(arm::kThumbBranch22, srcAddr, betterDstAddr, dstAddr);
+ break;
+
+ case ARM_RELOC_SECTDIFF:
+ case ARM_RELOC_LOCAL_SECTDIFF:
+ if ( !nextRelocIsPair ) {
+ throw "ARM_RELOC_SECTDIFF missing following pair";
+ }
+ if ( sreloc->r_length() != 2 )
+ throw "bad length for ARM_RELOC_SECTDIFF";
+ {
+ AtomAndOffset srcao = findAtomAndOffset(srcAddr);
+ AtomAndOffset fromao = findAtomAndOffset(nextRelocValue);
+ AtomAndOffset toao = findAtomAndOffset(dstAddr);
+ // check for addend encoded in the section content
+ pointerValue = LittleEndian::get32(*fixUpPtr);
+ if ( (dstAddr - nextRelocValue) != pointerValue ) {
+ if ( toao.atom == srcao.atom )
+ toao.offset += (pointerValue + nextRelocValue) - dstAddr;
+ else if ( fromao.atom == srcao.atom )
+ toao.offset += (pointerValue + nextRelocValue) - dstAddr;
+ else
+ fromao.offset += (dstAddr - pointerValue) - nextRelocValue;
+ }
+ new Reference<arm>(arm::kPointerDiff, srcao, fromao, toao);
+ }
+ break;
+
+ default:
+ warning("unexpected srelocation type %u", sreloc->r_type());
+ break;
+ }
+ }
+ return result;
+}
+
+template <typename A>
+void Reader<A>::addReferencesForSection(const macho_section<P>* sect)
+{
+ // ignore dwarf sections. If ld ever supports processing dwarf, this logic will need to change
+ if ( (sect->flags() & S_ATTR_DEBUG) == 0 ) {
+ switch ( sect->flags() & SECTION_TYPE ) {
+ case S_SYMBOL_STUBS:
+ case S_LAZY_SYMBOL_POINTERS:
+ // we ignore compiler generated stubs, so ignore those relocs too
+ break;
+ default:
+ // ignore all relocations in __eh_frame section
+ if ( sect == fehFrameSection )
+ return;
+ const macho_relocation_info<P>* relocs = (macho_relocation_info<P>*)((char*)(fHeader) + sect->reloff());
+ const uint32_t relocCount = sect->nreloc();
+ //fprintf(stderr, "relocCount = %d in section %s\n", relocCount, sect->sectname());
+ for (uint32_t r = 0; r < relocCount; ++r) {
+ try {
+ if ( addRelocReference(sect, &relocs[r]) )
+ ++r; // skip next
+ }
+ catch (const char* msg) {
+ throwf("in section %s,%s reloc %u: %s", sect->segname(), sect->sectname(), r, msg);
+ }
+ }
+ }
+ }
+}
+
+
+template <>
+const char* Reference<x86>::getDescription() const
+{
+ static char temp[2048];
+ switch( fKind ) {
+ case x86::kNoFixUp:
+ sprintf(temp, "reference to ");
+ break;
+ case x86::kFollowOn:
+ sprintf(temp, "followed by ");
+ break;
+ case x86::kGroupSubordinate:
+ sprintf(temp, "group subordinate ");
+ break;
+ case x86::kPointerWeakImport:
+ sprintf(temp, "offset 0x%04X, weak import pointer to ", fFixUpOffsetInSrc);
+ break;
+ case x86::kPointer:
+ sprintf(temp, "offset 0x%04X, pointer to ", fFixUpOffsetInSrc);
+ break;
+ case x86::kPointerDiff:
+ {
+ // by-name references have quoted names
+ const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : "";
+ const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : "";
+ sprintf(temp, "offset 0x%04X, 32-bit pointer difference: (&%s%s%s + 0x%08X) - (&%s%s%s + 0x%08X)",
+ fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset,
+ fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset );
+ return temp;
+ }
+ break;
+ case x86::kPointerDiff16:
+ {
+ // by-name references have quoted names
+ const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : "";
+ const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : "";
+ sprintf(temp, "offset 0x%04X, 16-bit pointer difference: (&%s%s%s + 0x%08X) - (&%s%s%s + 0x%08X)",
+ fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset,
+ fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset );
+ return temp;
+ }
+ break;
+ case x86::kPCRel32WeakImport:
+ sprintf(temp, "offset 0x%04X, rel32 reference to weak imported ", fFixUpOffsetInSrc);
+ break;
+ case x86::kPCRel32:
+ sprintf(temp, "offset 0x%04X, rel32 reference to ", fFixUpOffsetInSrc);
+ break;
+ case x86::kPCRel16:
+ sprintf(temp, "offset 0x%04X, rel16 reference to ", fFixUpOffsetInSrc);
+ break;
+ case x86::kPCRel8:
+ sprintf(temp, "offset 0x%04X, rel8 reference to ", fFixUpOffsetInSrc);
+ break;
+ case x86::kAbsolute32:
+ sprintf(temp, "offset 0x%04X, absolute32 reference to ", fFixUpOffsetInSrc);
+ break;
+ case x86::kImageOffset32:
+ sprintf(temp, "offset 0x%04X, 32-bit offset of ", fFixUpOffsetInSrc);
+ break;
+ case x86::kPointerDiff24:
+ sprintf(temp, "offset 0x%04X, 24-bit pointer difference: (&%s + 0x%08X) - (&%s + 0x%08X)",
+ fFixUpOffsetInSrc, this->getTargetDisplayName(), fToTarget.offset,
+ this->getFromTargetDisplayName(), fFromTarget.offset );
+ return temp;
+ break;
+ case x86::kSectionOffset24:
+ sprintf(temp, "offset 0x%04X, 24-bit section offset of ", fFixUpOffsetInSrc);
+ break;
+ case x86::kDtraceProbe:
+ sprintf(temp, "offset 0x%04X, dtrace static probe ", fFixUpOffsetInSrc);
+ break;
+ case x86::kDtraceProbeSite:
+ sprintf(temp, "offset 0x%04X, dtrace static probe site", fFixUpOffsetInSrc);
+ break;
+ case x86::kDtraceIsEnabledSite:
+ sprintf(temp, "offset 0x%04X, dtrace static probe is-enabled site", fFixUpOffsetInSrc);
+ break;
+ case x86::kDtraceTypeReference:
+ sprintf(temp, "offset 0x%04X, dtrace type/stability reference", fFixUpOffsetInSrc);
+ break;
+ }
+ // always quote by-name references
+ if ( fToTargetName != NULL ) {
+ strcat(temp, "\"");
+ strcat(temp, fToTargetName);
+ strcat(temp, "\"");
+ }
+ else if ( fToTarget.atom != NULL ) {
+ strcat(temp, fToTarget.atom->getDisplayName());
+ }
+ else {
+ strcat(temp, "NULL target");
+ }
+ if ( fToTarget.offset != 0 )
+ sprintf(&temp[strlen(temp)], " plus 0x%08X", fToTarget.offset);
+
+ return temp;
+}
+
+
+template <>
+const char* Reference<ppc>::getDescription() const
+{
+ static char temp[2048];
+ switch( fKind ) {
+ case ppc::kNoFixUp:
+ sprintf(temp, "reference to ");
+ break;
+ case ppc::kFollowOn:
+ sprintf(temp, "followed by ");
+ break;
+ case ppc::kGroupSubordinate:
+ sprintf(temp, "group subordinate ");
+ break;
+ case ppc::kPointerWeakImport:
+ sprintf(temp, "offset 0x%04X, weak import pointer to ", fFixUpOffsetInSrc);
+ break;
+ case ppc::kPointer:
+ sprintf(temp, "offset 0x%04X, pointer to ", fFixUpOffsetInSrc);
+ break;
+ case ppc::kPointerDiff16:
+ {
+ // by-name references have quoted names
+ const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : "";
+ const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : "";
+ sprintf(temp, "offset 0x%04X, 16-bit pointer difference: (&%s%s%s + %d) - (&%s%s%s + %d)",
+ fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset,
+ fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset );
+ return temp;
+ }
+ case ppc::kPointerDiff32:
+ {
+ // by-name references have quoted names
+ const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : "";
+ const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : "";
+ sprintf(temp, "offset 0x%04X, 32-bit pointer difference: (&%s%s%s + %d) - (&%s%s%s + %d)",
+ fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset,
+ fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset );
+ return temp;
+ }
+ case ppc::kPointerDiff64:
+ throw "unsupported refrence kind";
+ break;
+ case ppc::kBranch24WeakImport:
+ sprintf(temp, "offset 0x%04X, pc-rel branch fixup to weak imported ", fFixUpOffsetInSrc);
+ break;
+ case ppc::kBranch24:
+ case ppc::kBranch14:
+ sprintf(temp, "offset 0x%04X, pc-rel branch fixup to ", fFixUpOffsetInSrc);
+ break;
+ case ppc::kPICBaseLow16:
+ sprintf(temp, "offset 0x%04X, low 16 fixup from pic-base of %s plus 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.atom->getDisplayName(), fFromTarget.offset);
+ break;
+ case ppc::kPICBaseLow14:
+ sprintf(temp, "offset 0x%04X, low 14 fixup from pic-base of %s plus 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.atom->getDisplayName(), fFromTarget.offset);
+ break;
+ case ppc::kPICBaseHigh16:
+ sprintf(temp, "offset 0x%04X, high 16 fixup from pic-base of %s plus 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.atom->getDisplayName(), fFromTarget.offset);
+ break;
+ case ppc::kAbsLow16:
+ sprintf(temp, "offset 0x%04X, low 16 fixup to absolute address of ", fFixUpOffsetInSrc);
+ break;
+ case ppc::kAbsLow14:
+ sprintf(temp, "offset 0x%04X, low 14 fixup to absolute address of ", fFixUpOffsetInSrc);
+ break;
+ case ppc::kAbsHigh16:
+ sprintf(temp, "offset 0x%04X, high 16 fixup or to absolute address of ", fFixUpOffsetInSrc);
+ break;
+ case ppc::kAbsHigh16AddLow:
+ sprintf(temp, "offset 0x%04X, high 16 fixup add to absolute address of ", fFixUpOffsetInSrc);
+ break;
+ case ppc::kDtraceProbe:
+ sprintf(temp, "offset 0x%04X, dtrace static probe ", fFixUpOffsetInSrc);
+ break;
+ case ppc::kDtraceProbeSite:
+ sprintf(temp, "offset 0x%04X, dtrace static probe site", fFixUpOffsetInSrc);
+ break;
+ case ppc::kDtraceIsEnabledSite:
+ sprintf(temp, "offset 0x%04X, dtrace static probe is-enabled site", fFixUpOffsetInSrc);
+ break;
+ case ppc::kDtraceTypeReference:
+ sprintf(temp, "offset 0x%04X, dtrace type/stability reference", fFixUpOffsetInSrc);
+ break;
+ }
+ // always quote by-name references
+ if ( fToTargetName != NULL ) {
+ strcat(temp, "\"");
+ strcat(temp, fToTargetName);
+ strcat(temp, "\"");
+ }
+ else if ( fToTarget.atom != NULL ) {
+ strcat(temp, fToTarget.atom->getDisplayName());
+ }
+ else {
+ strcat(temp, "NULL target");
+ }
+ if ( fToTarget.offset != 0 )
+ sprintf(&temp[strlen(temp)], " plus 0x%08X", fToTarget.offset);
+
+ return temp;
+}
+
+template <>
+const char* Reference<ppc64>::getDescription() const
+{
+ static char temp[2048];
+ switch( fKind ) {
+ case ppc64::kNoFixUp:
+ sprintf(temp, "reference to ");
+ break;
+ case ppc64::kFollowOn:
+ sprintf(temp, "followed by ");
+ break;
+ case ppc64::kGroupSubordinate:
+ sprintf(temp, "group subordinate ");
+ break;
+ case ppc64::kPointerWeakImport:
+ sprintf(temp, "offset 0x%04llX, weak import pointer to ", fFixUpOffsetInSrc);
+ break;
+ case ppc64::kPointer:
+ sprintf(temp, "offset 0x%04llX, pointer to ", fFixUpOffsetInSrc);
+ break;
+ case ppc64::kPointerDiff64:
+ {
+ // by-name references have quoted names
+ const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : "";
+ const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : "";
+ sprintf(temp, "offset 0x%04llX, 64-bit pointer difference: (&%s%s%s + %u) - (&%s%s%s + %u)",
+ fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset,
+ fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset );
+ return temp;
+ }
+ case ppc64::kPointerDiff32:
+ {
+ // by-name references have quoted names
+ const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : "";
+ const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : "";
+ sprintf(temp, "offset 0x%04llX, 32-bit pointer difference: (&%s%s%s + %u) - (&%s%s%s + %u)",
+ fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset,
+ fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset );
+ return temp;
+ }
+ case ppc64::kPointerDiff16:
+ {
+ // by-name references have quoted names
+ const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : "";
+ const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : "";
+ sprintf(temp, "offset 0x%04llX, 16-bit pointer difference: (&%s%s%s + %u) - (&%s%s%s + %u)",
+ fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset,
+ fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset );
+ return temp;
+ }
+ case ppc64::kBranch24WeakImport:
+ sprintf(temp, "offset 0x%04llX, pc-rel branch fixup to weak imported ", fFixUpOffsetInSrc);
+ break;
+ case ppc64::kBranch24:
+ case ppc64::kBranch14:
+ sprintf(temp, "offset 0x%04llX, pc-rel branch fixup to ", fFixUpOffsetInSrc);
+ break;
+ case ppc64::kPICBaseLow16:
+ sprintf(temp, "offset 0x%04llX, low 16 fixup from pic-base offset 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.offset);
+ break;
+ case ppc64::kPICBaseLow14:
+ sprintf(temp, "offset 0x%04llX, low 14 fixup from pic-base offset 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.offset);
+ break;
+ case ppc64::kPICBaseHigh16:
+ sprintf(temp, "offset 0x%04llX, high 16 fixup from pic-base offset 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.offset);
+ break;
+ case ppc64::kAbsLow16:
+ sprintf(temp, "offset 0x%04llX, low 16 fixup to absolute address of ", fFixUpOffsetInSrc);
+ break;
+ case ppc64::kAbsLow14:
+ sprintf(temp, "offset 0x%04llX, low 14 fixup to absolute address of ", fFixUpOffsetInSrc);
+ break;
+ case ppc64::kAbsHigh16:
+ sprintf(temp, "offset 0x%04llX, high 16 fixup or to absolute address of ", fFixUpOffsetInSrc);
+ break;
+ case ppc64::kAbsHigh16AddLow:
+ sprintf(temp, "offset 0x%04llX, high 16 fixup add to absolute address of ", fFixUpOffsetInSrc);
+ break;
+ case ppc64::kDtraceProbe:
+ sprintf(temp, "offset 0x%04llX, dtrace static probe ", fFixUpOffsetInSrc);
+ break;
+ case ppc64::kDtraceProbeSite:
+ sprintf(temp, "offset 0x%04llX, dtrace static probe site", fFixUpOffsetInSrc);
+ break;
+ case ppc64::kDtraceIsEnabledSite:
+ sprintf(temp, "offset 0x%04llX, dtrace static probe is-enabled site", fFixUpOffsetInSrc);
+ break;
+ case ppc64::kDtraceTypeReference:
+ sprintf(temp, "offset 0x%04llX, dtrace type/stability reference", 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;
+}
+
+
+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::kGroupSubordinate:
+ sprintf(temp, "group subordinate ");
+ 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::kPointer32:
+ sprintf(temp, "offset 0x%04llX, 32-bit 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->getTargetDisplayName(), targetQuotes, fToTarget.offset,
+ fromQuotes, this->getFromTargetDisplayName(), 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;
+ case x86_64::kGOTNoFixUp:
+ sprintf(temp, "reference to GOT entry for ");
+ break;
+ case x86_64::kBranchPCRel8:
+ sprintf(temp, "offset 0x%04llX, branch rel8 reference to ", fFixUpOffsetInSrc);
+ break;
+ case x86_64::kPointerDiff24:
+ sprintf(temp, "offset 0x%04llX, 24-bit pointer difference: (&%s + 0x%08X) - (&%s + 0x%08X)",
+ fFixUpOffsetInSrc, this->getTargetDisplayName(), fToTarget.offset,
+ this->getFromTargetDisplayName(), fFromTarget.offset );
+ return temp;
+ case x86_64::kImageOffset32:
+ sprintf(temp, "offset 0x%04llX, 32bit offset of ", fFixUpOffsetInSrc);
+ break;
+ case x86_64::kSectionOffset24:
+ sprintf(temp, "offset 0x%04llX, 24-bit section offset of ", fFixUpOffsetInSrc);
+ break;
+ case x86_64::kDtraceProbe:
+ sprintf(temp, "offset 0x%04llX, dtrace static probe ", fFixUpOffsetInSrc);
+ break;
+ case x86_64::kDtraceProbeSite:
+ sprintf(temp, "offset 0x%04llX, dtrace static probe site", fFixUpOffsetInSrc);
+ break;
+ case x86_64::kDtraceIsEnabledSite:
+ sprintf(temp, "offset 0x%04llX, dtrace static probe is-enabled site", fFixUpOffsetInSrc);
+ break;
+ case x86_64::kDtraceTypeReference:
+ sprintf(temp, "offset 0x%04llX, dtrace type/stability reference", 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;
+}
+
+
+template <>
+const char* Reference<arm>::getDescription() const
+{
+ static char temp[2048];
+ switch( fKind ) {
+ case arm::kNoFixUp:
+ sprintf(temp, "reference to ");
+ break;
+ case arm::kFollowOn:
+ sprintf(temp, "followed by ");
+ break;
+ case arm::kGroupSubordinate:
+ sprintf(temp, "group subordinate ");
+ break;
+ case arm::kPointer:
+ sprintf(temp, "offset 0x%04X, pointer to ", fFixUpOffsetInSrc);
+ break;
+ case arm::kPointerWeakImport:
+ sprintf(temp, "offset 0x%04X, weak import pointer to ", fFixUpOffsetInSrc);
+ break;
+ case arm::kPointerDiff:
+ {
+ // by-name references have quoted names
+ const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : "";
+ const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : "";
+ sprintf(temp, "offset 0x%04X, 32-bit pointer difference: (&%s%s%s + %d) - (&%s%s%s + %d)",
+ fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset,
+ fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset );
+ return temp;
+ }
+ case arm::kReadOnlyPointer:
+ sprintf(temp, "offset 0x%04X, read-only pointer to ", fFixUpOffsetInSrc);
+ break;
+ case arm::kBranch24:
+ case arm::kThumbBranch22:
+ sprintf(temp, "offset 0x%04X, pc-rel branch fixup to ", fFixUpOffsetInSrc);
+ break;
+ case arm::kBranch24WeakImport:
+ case arm::kThumbBranch22WeakImport:
+ sprintf(temp, "offset 0x%04X, pc-rel branch fixup to weak imported ", fFixUpOffsetInSrc);
+ break;
+ case arm::kDtraceProbe:
+ sprintf(temp, "offset 0x%04X, dtrace static probe ", fFixUpOffsetInSrc);
+ break;
+ case arm::kDtraceProbeSite:
+ sprintf(temp, "offset 0x%04X, dtrace static probe site", fFixUpOffsetInSrc);
+ break;
+ case arm::kDtraceIsEnabledSite:
+ sprintf(temp, "offset 0x%04X, dtrace static probe is-enabled site", fFixUpOffsetInSrc);
+ break;
+ case arm::kDtraceTypeReference:
+ sprintf(temp, "offset 0x%04X, dtrace type/stability reference", fFixUpOffsetInSrc);
+ break;
+ }
+ // always quote by-name references
+ if ( fToTargetName != NULL ) {
+ strcat(temp, "\"");
+ strcat(temp, fToTargetName);
+ strcat(temp, "\"");
+ }
+ else if ( fToTarget.atom != NULL ) {
+ strcat(temp, fToTarget.atom->getDisplayName());
+ }
+ else {
+ strcat(temp, "NULL target");
+ }
+ if ( fToTarget.offset != 0 )
+ sprintf(&temp[strlen(temp)], " plus 0x%08X", fToTarget.offset);
+
+ return temp;
+}
+
+
+template <>
+bool Reference<x86>::isBranch() const
+{
+ switch ( fKind ) {
+ case x86::kPCRel32:
+ case x86::kPCRel32WeakImport:
+ return true;
+ default:
+ return false;
+ }
+}
+
+template <>
+bool Reference<x86_64>::isBranch() const
+{
+ switch ( fKind ) {
+ case x86_64::kBranchPCRel32:
+ case x86_64::kBranchPCRel32WeakImport:
+ return true;
+ default:
+ return false;
+ }
+}
+
+template <>
+bool Reference<ppc>::isBranch() const
+{
+ switch ( fKind ) {
+ case ppc::kBranch24:
+ case ppc::kBranch24WeakImport:
+ return true;
+ default:
+ return false;
+ }
+}
+
+template <>
+bool Reference<ppc64>::isBranch() const
+{
+ switch ( fKind ) {
+ case ppc64::kBranch24:
+ case ppc64::kBranch24WeakImport:
+ return true;
+ default:
+ return false;
+ }
+}
+
+template <>
+bool Reference<arm>::isBranch() const
+{
+ switch ( fKind ) {
+ case arm::kBranch24:
+ case arm::kBranch24WeakImport:
+ case arm::kThumbBranch22:
+ case arm::kThumbBranch22WeakImport:
+ return true;
+ default:
+ return false;
+ }
+}
+
+
+
+}; // namespace relocatable
+}; // namespace mach_o
+
+#endif // __OBJECT_FILE_MACH_O__
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2005-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifndef __EXECUTABLE_MACH_O__
+#define __EXECUTABLE_MACH_O__
+
+#include <stdint.h>
+#include <stddef.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <uuid/uuid.h>
+#include <mach/i386/thread_status.h>
+#include <mach/ppc/thread_status.h>
+#include <CommonCrypto/CommonDigest.h>
+
+#include <vector>
+#include <algorithm>
+#include <map>
+#include <set>
+#include <ext/hash_map>
+
+#include "ObjectFile.h"
+#include "ExecutableFile.h"
+#include "Options.h"
+
+#include "MachOFileAbstraction.hpp"
+#include "MachOTrie.hpp"
+
+
+//
+//
+// To implement architecture xxx, you must write template specializations for the following methods:
+// MachHeaderAtom<xxx>::setHeaderInfo()
+// ThreadsLoadCommandsAtom<xxx>::getSize()
+// ThreadsLoadCommandsAtom<xxx>::copyRawContent()
+// Writer<xxx>::addObjectRelocs()
+// Writer<xxx>::fixUpReferenceRelocatable()
+// Writer<xxx>::fixUpReferenceFinal()
+// Writer<xxx>::stubableReference()
+// Writer<xxx>::weakImportReferenceKind()
+// Writer<xxx>::GOTReferenceKind()
+//
+
+
+namespace mach_o {
+namespace executable {
+
+// forward references
+template <typename A> class WriterAtom;
+template <typename A> class PageZeroAtom;
+template <typename A> class CustomStackAtom;
+template <typename A> class MachHeaderAtom;
+template <typename A> class SegmentLoadCommandsAtom;
+template <typename A> class EncryptionLoadCommandsAtom;
+template <typename A> class SymbolTableLoadCommandsAtom;
+template <typename A> class DyldInfoLoadCommandsAtom;
+template <typename A> class ThreadsLoadCommandsAtom;
+template <typename A> class DylibIDLoadCommandsAtom;
+template <typename A> class RoutinesLoadCommandsAtom;
+template <typename A> class DyldLoadCommandsAtom;
+template <typename A> class UUIDLoadCommandAtom;
+template <typename A> class LinkEditAtom;
+template <typename A> class SectionRelocationsLinkEditAtom;
+template <typename A> class CompressedRebaseInfoLinkEditAtom;
+template <typename A> class CompressedBindingInfoLinkEditAtom;
+template <typename A> class CompressedWeakBindingInfoLinkEditAtom;
+template <typename A> class CompressedLazyBindingInfoLinkEditAtom;
+template <typename A> class CompressedExportInfoLinkEditAtom;
+template <typename A> class LocalRelocationsLinkEditAtom;
+template <typename A> class ExternalRelocationsLinkEditAtom;
+template <typename A> class SymbolTableLinkEditAtom;
+template <typename A> class SegmentSplitInfoLoadCommandsAtom;
+template <typename A> class SegmentSplitInfoContentAtom;
+template <typename A> class IndirectTableLinkEditAtom;
+template <typename A> class ModuleInfoLinkEditAtom;
+template <typename A> class StringsLinkEditAtom;
+template <typename A> class LoadCommandsPaddingAtom;
+template <typename A> class UnwindInfoAtom;
+template <typename A> class StubAtom;
+template <typename A> class StubHelperAtom;
+template <typename A> class ClassicStubHelperAtom;
+template <typename A> class HybridStubHelperAtom;
+template <typename A> class HybridStubHelperHelperAtom;
+template <typename A> class FastStubHelperAtom;
+template <typename A> class FastStubHelperHelperAtom;
+template <typename A> class LazyPointerAtom;
+template <typename A> class NonLazyPointerAtom;
+template <typename A> class DylibLoadCommandsAtom;
+
+
+// SectionInfo should be nested inside Writer, but I can't figure out how to make the type accessible to the Atom classes
+class SectionInfo : public ObjectFile::Section {
+public:
+ SectionInfo() : fFileOffset(0), fSize(0), fRelocCount(0), fRelocOffset(0),
+ fIndirectSymbolOffset(0), fAlignment(0), fAllLazyPointers(false),
+ fAllLazyDylibPointers(false),fAllNonLazyPointers(false), fAllStubs(false),
+ fAllSelfModifyingStubs(false), fAllStubHelpers(false),
+ fAllZeroFill(false), fVirtualSection(false),
+ fHasTextLocalRelocs(false), fHasTextExternalRelocs(false)
+ { fSegmentName[0] = '\0'; fSectionName[0] = '\0'; }
+ void setIndex(unsigned int index) { fIndex=index; }
+ std::vector<ObjectFile::Atom*> fAtoms;
+ char fSegmentName[20];
+ char fSectionName[20];
+ uint64_t fFileOffset;
+ uint64_t fSize;
+ uint32_t fRelocCount;
+ uint32_t fRelocOffset;
+ uint32_t fIndirectSymbolOffset;
+ uint8_t fAlignment;
+ bool fAllLazyPointers;
+ bool fAllLazyDylibPointers;
+ bool fAllNonLazyPointers;
+ bool fAllStubs;
+ bool fAllSelfModifyingStubs;
+ bool fAllStubHelpers;
+ bool fAllZeroFill;
+ bool fVirtualSection;
+ bool fHasTextLocalRelocs;
+ bool fHasTextExternalRelocs;
+};
+
+// SegmentInfo should be nested inside Writer, but I can't figure out how to make the type accessible to the Atom classes
+class SegmentInfo
+{
+public:
+ SegmentInfo(uint64_t pageSize) : fInitProtection(0), fMaxProtection(0), fFileOffset(0), fFileSize(0),
+ fBaseAddress(0), fSize(0), fPageSize(pageSize), fFixedAddress(false),
+ fIndependentAddress(false), fHasLoadCommand(true) { fName[0] = '\0'; }
+ std::vector<class SectionInfo*> fSections;
+ char fName[20];
+ uint32_t fInitProtection;
+ uint32_t fMaxProtection;
+ uint64_t fFileOffset;
+ uint64_t fFileSize;
+ uint64_t fBaseAddress;
+ uint64_t fSize;
+ uint64_t fPageSize;
+ bool fFixedAddress;
+ bool fIndependentAddress;
+ bool fHasLoadCommand;
+};
+
+
+struct RebaseInfo {
+ RebaseInfo(uint8_t t, uint64_t addr) : fType(t), fAddress(addr) {}
+ uint8_t fType;
+ uint64_t fAddress;
+ // for sorting
+ int operator<(const RebaseInfo& rhs) const {
+ // sort by type, then address
+ if ( this->fType != rhs.fType )
+ return (this->fType < rhs.fType );
+ return (this->fAddress < rhs.fAddress );
+ }
+};
+
+struct BindingInfo {
+ BindingInfo(uint8_t t, int ord, const char* sym, bool weak_import, uint64_t addr, int64_t addend)
+ : fType(t), fFlags(weak_import ? BIND_SYMBOL_FLAGS_WEAK_IMPORT : 0 ), fLibraryOrdinal(ord),
+ fSymbolName(sym), fAddress(addr), fAddend(addend) {}
+ BindingInfo(uint8_t t, const char* sym, bool non_weak_definition, uint64_t addr, int64_t addend)
+ : fType(t), fFlags(non_weak_definition ? BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION : 0 ), fLibraryOrdinal(0),
+ fSymbolName(sym), fAddress(addr), fAddend(addend) {}
+ uint8_t fType;
+ uint8_t fFlags;
+ int fLibraryOrdinal;
+ const char* fSymbolName;
+ uint64_t fAddress;
+ int64_t fAddend;
+
+ // for sorting
+ int operator<(const BindingInfo& rhs) const {
+ // sort by library, symbol, type, then address
+ if ( this->fLibraryOrdinal != rhs.fLibraryOrdinal )
+ return (this->fLibraryOrdinal < rhs.fLibraryOrdinal );
+ if ( this->fSymbolName != rhs.fSymbolName )
+ return ( strcmp(this->fSymbolName, rhs.fSymbolName) < 0 );
+ if ( this->fType != rhs.fType )
+ return (this->fType < rhs.fType );
+ return (this->fAddress < rhs.fAddress );
+ }
+};
+
+
+class ByteStream {
+private:
+ std::vector<uint8_t> fData;
+public:
+ std::vector<uint8_t>& bytes() { return fData; }
+ unsigned long size() const { return fData.size(); }
+ void reserve(unsigned long l) { fData.reserve(l); }
+ const uint8_t* start() const { return &fData[0]; }
+
+ void append_uleb128(uint64_t value) {
+ uint8_t byte;
+ do {
+ byte = value & 0x7F;
+ value &= ~0x7F;
+ if ( value != 0 )
+ byte |= 0x80;
+ fData.push_back(byte);
+ value = value >> 7;
+ } while( byte >= 0x80 );
+ }
+
+ void append_sleb128(int64_t value) {
+ bool isNeg = ( value < 0 );
+ uint8_t byte;
+ bool more;
+ do {
+ byte = value & 0x7F;
+ value = value >> 7;
+ if ( isNeg )
+ more = ( (value != -1) || ((byte & 0x40) == 0) );
+ else
+ more = ( (value != 0) || ((byte & 0x40) != 0) );
+ if ( more )
+ byte |= 0x80;
+ fData.push_back(byte);
+ }
+ while( more );
+ }
+
+ void append_string(const char* str) {
+ for (const char* s = str; *s != '\0'; ++s)
+ fData.push_back(*s);
+ fData.push_back('\0');
+ }
+
+ void append_byte(uint8_t byte) {
+ fData.push_back(byte);
+ }
+
+ static unsigned int uleb128_size(uint64_t value) {
+ uint32_t result = 0;
+ do {
+ value = value >> 7;
+ ++result;
+ } while ( value != 0 );
+ return result;
+ }
+
+ void pad_to_size(unsigned int alignment) {
+ while ( (fData.size() % alignment) != 0 )
+ fData.push_back(0);
+ }
+};
+
+
+template <typename A>
+class Writer : public ExecutableFile::Writer
+{
+public:
+ Writer(const char* path, Options& options, std::vector<ExecutableFile::DyLibUsed>& dynamicLibraries);
+ virtual ~Writer();
+
+ virtual const char* getPath() { return fFilePath; }
+ virtual time_t getModificationTime() { return 0; }
+ virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; }
+ virtual std::vector<class ObjectFile::Atom*>& getAtoms() { return fWriterSynthesizedAtoms; }
+ virtual std::vector<class ObjectFile::Atom*>* getJustInTimeAtomsFor(const char* name) { return NULL; }
+ virtual std::vector<Stab>* getStabs() { return NULL; }
+
+ virtual ObjectFile::Atom& makeObjcInfoAtom(ObjectFile::Reader::ObjcConstraint objcContraint,
+ bool objcReplacementClasses);
+ virtual class ObjectFile::Atom* getUndefinedProxyAtom(const char* name);
+ virtual uint64_t write(std::vector<class ObjectFile::Atom*>& atoms,
+ std::vector<class ObjectFile::Reader::Stab>& stabs,
+ class ObjectFile::Atom* entryPointAtom,
+ class ObjectFile::Atom* dyldClassicHelperAtom,
+ class ObjectFile::Atom* dyldCompressedHelperAtom,
+ class ObjectFile::Atom* dyldLazyDylibHelperAtom,
+ bool createUUID, bool canScatter,
+ ObjectFile::Reader::CpuConstraint cpuConstraint,
+ bool biggerThanTwoGigs,
+ std::set<const class ObjectFile::Atom*>& atomsThatOverrideWeak,
+ bool hasExternalWeakDefinitions);
+
+private:
+ typedef typename A::P P;
+ typedef typename A::P::uint_t pint_t;
+
+ enum RelocKind { kRelocNone, kRelocInternal, kRelocExternal };
+
+ void assignFileOffsets();
+ void synthesizeStubs();
+ void synthesizeKextGOT();
+ void createSplitSegContent();
+ void synthesizeUnwindInfoTable();
+ void insertDummyStubs();
+ void partitionIntoSections();
+ bool addBranchIslands();
+ bool addPPCBranchIslands();
+ bool isBranch24Reference(uint8_t kind);
+ void adjustLoadCommandsAndPadding();
+ void createDynamicLinkerCommand();
+ void createDylibCommands();
+ void buildLinkEdit();
+ const char* getArchString();
+ void writeMap();
+ uint64_t writeAtoms();
+ void writeNoOps(int fd, uint32_t from, uint32_t to);
+ void copyNoOps(uint8_t* from, uint8_t* to);
+ bool segmentsCanSplitApart(const ObjectFile::Atom& from, const ObjectFile::Atom& to);
+ void addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref);
+ void collectExportedAndImportedAndLocalAtoms();
+ void setNlistRange(std::vector<class ObjectFile::Atom*>& atoms, uint32_t startIndex, uint32_t count);
+ void addLocalLabel(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* name);
+ void addGlobalLabel(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* name);
+ void buildSymbolTable();
+ bool stringsNeedLabelsInObjects();
+ const char* symbolTableName(const ObjectFile::Atom* atom);
+ void setExportNlist(const ObjectFile::Atom* atom, macho_nlist<P>* entry);
+ void setImportNlist(const ObjectFile::Atom* atom, macho_nlist<P>* entry);
+ void setLocalNlist(const ObjectFile::Atom* atom, macho_nlist<P>* entry);
+ void copyNlistRange(const std::vector<macho_nlist<P> >& entries, uint32_t startIndex);
+ uint64_t getAtomLoadAddress(const ObjectFile::Atom* atom);
+ uint8_t ordinalForLibrary(ObjectFile::Reader* file);
+ bool targetRequiresWeakBinding(const ObjectFile::Atom& target);
+ int compressedOrdinalForImortedAtom(ObjectFile::Atom* target);
+ bool shouldExport(const ObjectFile::Atom& atom) const;
+ void buildFixups();
+ void adjustLinkEditSections();
+ void buildObjectFileFixups();
+ void buildExecutableFixups();
+ bool preboundLazyPointerType(uint8_t* type);
+ uint64_t relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const;
+ void fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const;
+ void fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const;
+ void fixUpReference_powerpc(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom,
+ uint8_t buffer[], bool finalLinkedImage) const;
+ uint32_t symbolIndex(ObjectFile::Atom& atom);
+ bool makesExternalRelocatableReference(ObjectFile::Atom& target) const;
+ uint32_t addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref);
+ uint32_t addObjectRelocs_powerpc(ObjectFile::Atom* atom, ObjectFile::Reference* ref);
+ uint8_t getRelocPointerSize();
+ uint64_t maxAddress();
+ bool stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref);
+ bool GOTReferenceKind(uint8_t kind);
+ bool optimizableGOTReferenceKind(uint8_t kind);
+ bool weakImportReferenceKind(uint8_t kind);
+ unsigned int collectStabs();
+ uint64_t valueForStab(const ObjectFile::Reader::Stab& stab);
+ uint32_t stringOffsetForStab(const ObjectFile::Reader::Stab& stab);
+ uint8_t sectionIndexForStab(const ObjectFile::Reader::Stab& stab);
+ void addStabs(uint32_t startIndex);
+ RelocKind relocationNeededInFinalLinkedImage(const ObjectFile::Atom& target) const;
+ bool illegalRelocInFinalLinkedImage(const ObjectFile::Reference&);
+ bool generatesLocalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection);
+ bool generatesExternalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection);
+ bool mightNeedPadSegment();
+ void scanForAbsoluteReferences();
+ bool needsModuleTable();
+ void optimizeDylibReferences();
+ bool indirectSymbolInRelocatableIsLocal(const ObjectFile::Reference* ref) const;
+
+ struct DirectLibrary {
+ class ObjectFile::Reader* fLibrary;
+ bool fWeak;
+ bool fReExport;
+ };
+
+ friend class WriterAtom<A>;
+ friend class PageZeroAtom<A>;
+ friend class CustomStackAtom<A>;
+ friend class MachHeaderAtom<A>;
+ friend class SegmentLoadCommandsAtom<A>;
+ friend class EncryptionLoadCommandsAtom<A>;
+ friend class SymbolTableLoadCommandsAtom<A>;
+ friend class DyldInfoLoadCommandsAtom<A>;
+ friend class ThreadsLoadCommandsAtom<A>;
+ friend class DylibIDLoadCommandsAtom<A>;
+ friend class RoutinesLoadCommandsAtom<A>;
+ friend class DyldLoadCommandsAtom<A>;
+ friend class UUIDLoadCommandAtom<A>;
+ friend class LinkEditAtom<A>;
+ friend class SectionRelocationsLinkEditAtom<A>;
+ friend class CompressedRebaseInfoLinkEditAtom<A>;
+ friend class CompressedBindingInfoLinkEditAtom<A>;
+ friend class CompressedWeakBindingInfoLinkEditAtom<A>;
+ friend class CompressedLazyBindingInfoLinkEditAtom<A>;
+ friend class CompressedExportInfoLinkEditAtom<A>;
+ friend class LocalRelocationsLinkEditAtom<A>;
+ friend class ExternalRelocationsLinkEditAtom<A>;
+ friend class SymbolTableLinkEditAtom<A>;
+ friend class SegmentSplitInfoLoadCommandsAtom<A>;
+ friend class SegmentSplitInfoContentAtom<A>;
+ friend class IndirectTableLinkEditAtom<A>;
+ friend class ModuleInfoLinkEditAtom<A>;
+ friend class StringsLinkEditAtom<A>;
+ friend class LoadCommandsPaddingAtom<A>;
+ friend class UnwindInfoAtom<A>;
+ friend class StubAtom<A>;
+ friend class StubHelperAtom<A>;
+ friend class ClassicStubHelperAtom<A>;
+ friend class HybridStubHelperAtom<A>;
+ friend class FastStubHelperAtom<A>;
+ friend class FastStubHelperHelperAtom<A>;
+ friend class HybridStubHelperHelperAtom<A>;
+ friend class LazyPointerAtom<A>;
+ friend class NonLazyPointerAtom<A>;
+ friend class DylibLoadCommandsAtom<A>;
+
+ const char* fFilePath;
+ Options& fOptions;
+ std::vector<class ObjectFile::Atom*>* fAllAtoms;
+ std::vector<class ObjectFile::Reader::Stab>* fStabs;
+ std::set<const class ObjectFile::Atom*>* fRegularDefAtomsThatOverrideADylibsWeakDef;
+ class SectionInfo* fLoadCommandsSection;
+ class SegmentInfo* fLoadCommandsSegment;
+ class MachHeaderAtom<A>* fMachHeaderAtom;
+ class EncryptionLoadCommandsAtom<A>* fEncryptionLoadCommand;
+ class SegmentLoadCommandsAtom<A>* fSegmentCommands;
+ class SymbolTableLoadCommandsAtom<A>* fSymbolTableCommands;
+ class LoadCommandsPaddingAtom<A>* fHeaderPadding;
+ class UnwindInfoAtom<A>* fUnwindInfoAtom;
+ class UUIDLoadCommandAtom<A>* fUUIDAtom;
+ std::vector<class ObjectFile::Atom*> fWriterSynthesizedAtoms;
+ std::vector<SegmentInfo*> fSegmentInfos;
+ class SegmentInfo* fPadSegmentInfo;
+ class ObjectFile::Atom* fEntryPoint;
+ class ObjectFile::Atom* fDyldClassicHelperAtom;
+ class ObjectFile::Atom* fDyldCompressedHelperAtom;
+ class ObjectFile::Atom* fDyldLazyDylibHelper;
+ std::map<class ObjectFile::Reader*, DylibLoadCommandsAtom<A>*> fLibraryToLoadCommand;
+ std::map<class ObjectFile::Reader*, uint32_t> fLibraryToOrdinal;
+ std::map<class ObjectFile::Reader*, class ObjectFile::Reader*> fLibraryAliases;
+ std::set<class ObjectFile::Reader*> fForcedWeakImportReaders;
+ std::vector<class ObjectFile::Atom*> fExportedAtoms;
+ std::vector<class ObjectFile::Atom*> fImportedAtoms;
+ std::vector<class ObjectFile::Atom*> fLocalSymbolAtoms;
+ std::vector<macho_nlist<P> > fLocalExtraLabels;
+ std::vector<macho_nlist<P> > fGlobalExtraLabels;
+ std::map<ObjectFile::Atom*, uint32_t> fAtomToSymbolIndex;
+ class SectionRelocationsLinkEditAtom<A>* fSectionRelocationsAtom;
+ class CompressedRebaseInfoLinkEditAtom<A>* fCompressedRebaseInfoAtom;
+ class CompressedBindingInfoLinkEditAtom<A>* fCompressedBindingInfoAtom;
+ class CompressedWeakBindingInfoLinkEditAtom<A>* fCompressedWeakBindingInfoAtom;
+ class CompressedLazyBindingInfoLinkEditAtom<A>* fCompressedLazyBindingInfoAtom;
+ class CompressedExportInfoLinkEditAtom<A>* fCompressedExportInfoAtom;
+ class LocalRelocationsLinkEditAtom<A>* fLocalRelocationsAtom;
+ class ExternalRelocationsLinkEditAtom<A>* fExternalRelocationsAtom;
+ class SymbolTableLinkEditAtom<A>* fSymbolTableAtom;
+ class SegmentSplitInfoContentAtom<A>* fSplitCodeToDataContentAtom;
+ class IndirectTableLinkEditAtom<A>* fIndirectTableAtom;
+ class ModuleInfoLinkEditAtom<A>* fModuleInfoAtom;
+ class StringsLinkEditAtom<A>* fStringsAtom;
+ class PageZeroAtom<A>* fPageZeroAtom;
+ class NonLazyPointerAtom<A>* fFastStubGOTAtom;
+ macho_nlist<P>* fSymbolTable;
+ std::vector<macho_relocation_info<P> > fSectionRelocs;
+ std::vector<macho_relocation_info<P> > fInternalRelocs;
+ std::vector<macho_relocation_info<P> > fExternalRelocs;
+ std::vector<RebaseInfo> fRebaseInfo;
+ std::vector<BindingInfo> fBindingInfo;
+ std::vector<BindingInfo> fWeakBindingInfo;
+ std::map<const ObjectFile::Atom*,ObjectFile::Atom*> fStubsMap;
+ std::map<ObjectFile::Atom*,ObjectFile::Atom*> fGOTMap;
+ std::vector<class StubAtom<A>*> fAllSynthesizedStubs;
+ std::vector<ObjectFile::Atom*> fAllSynthesizedStubHelpers;
+ std::vector<class LazyPointerAtom<A>*> fAllSynthesizedLazyPointers;
+ std::vector<class LazyPointerAtom<A>*> fAllSynthesizedLazyDylibPointers;
+ std::vector<class NonLazyPointerAtom<A>*> fAllSynthesizedNonLazyPointers;
+ uint32_t fSymbolTableCount;
+ uint32_t fSymbolTableStabsCount;
+ uint32_t fSymbolTableStabsStartIndex;
+ uint32_t fSymbolTableLocalCount;
+ uint32_t fSymbolTableLocalStartIndex;
+ uint32_t fSymbolTableExportCount;
+ uint32_t fSymbolTableExportStartIndex;
+ uint32_t fSymbolTableImportCount;
+ uint32_t fSymbolTableImportStartIndex;
+ uint32_t fLargestAtomSize;
+ bool fEmitVirtualSections;
+ bool fHasWeakExports;
+ bool fReferencesWeakImports;
+ bool fCanScatter;
+ bool fWritableSegmentPastFirst4GB;
+ bool fNoReExportedDylibs;
+ bool fBiggerThanTwoGigs;
+ bool fSlideable;
+ std::map<const ObjectFile::Atom*,bool> fWeakImportMap;
+ std::set<const ObjectFile::Reader*> fDylibReadersWithNonWeakImports;
+ std::set<const ObjectFile::Reader*> fDylibReadersWithWeakImports;
+ SegmentInfo* fFirstWritableSegment;
+ ObjectFile::Reader::CpuConstraint fCpuConstraint;
+ uint32_t fAnonNameIndex;
+};
+
+
+class Segment : public ObjectFile::Segment
+{
+public:
+ Segment(const char* name, bool readable, bool writable, bool executable, bool fixedAddress)
+ : fName(name), fReadable(readable), fWritable(writable), fExecutable(executable), fFixedAddress(fixedAddress) {}
+ virtual const char* getName() const { return fName; }
+ virtual bool isContentReadable() const { return fReadable; }
+ virtual bool isContentWritable() const { return fWritable; }
+ virtual bool isContentExecutable() const { return fExecutable; }
+ virtual bool hasFixedAddress() const { return fFixedAddress; }
+
+ static Segment fgTextSegment;
+ static Segment fgPageZeroSegment;
+ static Segment fgLinkEditSegment;
+ static Segment fgStackSegment;
+ static Segment fgImportSegment;
+ static Segment fgROImportSegment;
+ static Segment fgDataSegment;
+ static Segment fgObjCSegment;
+ static Segment fgHeaderSegment;
+
+
+private:
+ const char* fName;
+ const bool fReadable;
+ const bool fWritable;
+ const bool fExecutable;
+ const bool fFixedAddress;
+};
+
+Segment Segment::fgPageZeroSegment("__PAGEZERO", false, false, false, true);
+Segment Segment::fgTextSegment("__TEXT", true, false, true, false);
+Segment Segment::fgLinkEditSegment("__LINKEDIT", true, false, false, false);
+Segment Segment::fgStackSegment("__UNIXSTACK", true, true, false, true);
+Segment Segment::fgImportSegment("__IMPORT", true, true, true, false);
+Segment Segment::fgROImportSegment("__IMPORT", true, false, true, false);
+Segment Segment::fgDataSegment("__DATA", true, true, false, false);
+Segment Segment::fgObjCSegment("__OBJC", true, true, false, false);
+Segment Segment::fgHeaderSegment("__HEADER", true, false, true, false);
+
+
+template <typename A>
+class WriterAtom : public ObjectFile::Atom
+{
+public:
+ enum Kind { zeropage, machHeaderApp, machHeaderDylib, machHeaderBundle, machHeaderObject, loadCommands, undefinedProxy };
+ 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 const char* getName() const { return NULL; }
+ virtual const char* getDisplayName() const { return this->getName(); }
+ virtual Scope getScope() const { return ObjectFile::Atom::scopeTranslationUnit; }
+ virtual DefinitionKind getDefinitionKind() const { return kRegularDefinition; }
+ virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; }
+ virtual bool dontDeadStrip() const { return true; }
+ virtual bool isZeroFill() const { return false; }
+ virtual bool isThumb() const { return false; }
+ virtual std::vector<ObjectFile::Reference*>& getReferences() const { return fgEmptyReferenceList; }
+ virtual bool mustRemainInSection() const { return true; }
+ virtual ObjectFile::Segment& getSegment() const { return fSegment; }
+ virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); }
+ virtual uint32_t getOrdinal() const { return 0; }
+ virtual std::vector<ObjectFile::LineInfo>* getLineInfo() const { return NULL; }
+ virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(2); }
+ virtual void copyRawContent(uint8_t buffer[]) const { throw "don't use copyRawContent"; }
+ virtual void setScope(Scope) { }
+
+
+protected:
+ virtual ~WriterAtom() {}
+ typedef typename A::P P;
+ typedef typename A::P::E E;
+
+ static Segment& headerSegment(Writer<A>& writer) { return (writer.fOptions.outputKind()==Options::kPreload)
+ ? Segment::fgHeaderSegment : Segment::fgTextSegment; }
+
+ static std::vector<ObjectFile::Reference*> fgEmptyReferenceList;
+
+ Writer<A>& fWriter;
+ Segment& fSegment;
+};
+
+template <typename A> std::vector<ObjectFile::Reference*> WriterAtom<A>::fgEmptyReferenceList;
+
+
+template <typename A>
+class PageZeroAtom : public WriterAtom<A>
+{
+public:
+ 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 fSize; }
+ virtual const char* getSectionName() const { return "._zeropage"; }
+ virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(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>
+{
+public:
+ DsoHandleAtom(Writer<A>& writer) : WriterAtom<A>(writer, Segment::fgTextSegment) {}
+ virtual const char* getName() const { return "___dso_handle"; }
+ virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; }
+ virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; }
+ virtual uint64_t getSize() const { return 0; }
+ virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(12); }
+ virtual const char* getSectionName() const { return "._mach_header"; }
+ virtual void copyRawContent(uint8_t buffer[]) const {}
+};
+
+
+template <typename A>
+class MachHeaderAtom : public WriterAtom<A>
+{
+public:
+ MachHeaderAtom(Writer<A>& writer) : WriterAtom<A>(writer, headerSegment(writer)) {}
+ virtual const char* getName() const;
+ virtual const char* getDisplayName() const;
+ virtual ObjectFile::Atom::Scope getScope() const;
+ virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const;
+ virtual uint64_t getSize() const { return sizeof(macho_header<typename A::P>); }
+ virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(12); }
+ virtual const char* getSectionName() const { return "._mach_header"; }
+ virtual uint32_t getOrdinal() const { return 1; }
+ virtual void copyRawContent(uint8_t buffer[]) const;
+private:
+ using WriterAtom<A>::fWriter;
+ typedef typename A::P P;
+ void setHeaderInfo(macho_header<typename A::P>& header) const;
+};
+
+template <typename A>
+class CustomStackAtom : public WriterAtom<A>
+{
+public:
+ CustomStackAtom(Writer<A>& writer);
+ virtual const char* getDisplayName() const { return "custom stack content"; }
+ virtual bool isZeroFill() const { return true; }
+ virtual uint64_t getSize() const { return fWriter.fOptions.customStackSize(); }
+ virtual const char* getSectionName() const { return "._stack"; }
+ virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(12); }
+private:
+ using WriterAtom<A>::fWriter;
+ typedef typename A::P P;
+ static bool stackGrowsDown();
+};
+
+template <typename A>
+class LoadCommandAtom : public WriterAtom<A>
+{
+protected:
+ LoadCommandAtom(Writer<A>& writer) : WriterAtom<A>(writer, headerSegment(writer)), fOrdinal(fgCurrentOrdinal++) {}
+ virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(log2(sizeof(typename A::P::uint_t))); }
+ virtual const char* getSectionName() const { return "._load_commands"; }
+ virtual uint32_t getOrdinal() const { return fOrdinal; }
+ static uint64_t alignedSize(uint64_t size);
+protected:
+ uint32_t fOrdinal;
+ static uint32_t fgCurrentOrdinal;
+};
+
+template <typename A> uint32_t LoadCommandAtom<A>::fgCurrentOrdinal = 0;
+
+template <typename A>
+class SegmentLoadCommandsAtom : public LoadCommandAtom<A>
+{
+public:
+ SegmentLoadCommandsAtom(Writer<A>& writer)
+ : LoadCommandAtom<A>(writer), fCommandCount(0), fSize(0)
+ { writer.fSegmentCommands = this; }
+ virtual const char* getDisplayName() const { return "segment load commands"; }
+ virtual uint64_t getSize() const { return fSize; }
+ virtual void copyRawContent(uint8_t buffer[]) const;
+
+ void computeSize();
+ void setup();
+ unsigned int commandCount() { return fCommandCount; }
+private:
+ using WriterAtom<A>::fWriter;
+ typedef typename A::P P;
+ unsigned int fCommandCount;
+ uint32_t fSize;
+};
+
+
+template <typename A>
+class SymbolTableLoadCommandsAtom : public LoadCommandAtom<A>
+{
+public:
+ SymbolTableLoadCommandsAtom(Writer<A>&);
+ virtual const char* getDisplayName() const { return "symbol table load commands"; }
+ virtual uint64_t getSize() const;
+ virtual void copyRawContent(uint8_t buffer[]) const;
+ unsigned int commandCount();
+ void needDynamicTable();
+private:
+ using WriterAtom<A>::fWriter;
+ typedef typename A::P P;
+ bool fNeedsDynamicSymbolTable;
+ macho_symtab_command<typename A::P> fSymbolTable;
+ macho_dysymtab_command<typename A::P> fDynamicSymbolTable;
+};
+
+template <typename A>
+class ThreadsLoadCommandsAtom : public LoadCommandAtom<A>
+{
+public:
+ ThreadsLoadCommandsAtom(Writer<A>& writer)
+ : LoadCommandAtom<A>(writer) {}
+ virtual const char* getDisplayName() const { return "thread load commands"; }
+ virtual uint64_t getSize() const;
+ virtual void copyRawContent(uint8_t buffer[]) const;
+private:
+ using WriterAtom<A>::fWriter;
+ typedef typename A::P P;
+ uint8_t* fBuffer;
+ uint32_t fBufferSize;
+};
+
+template <typename A>
+class DyldLoadCommandsAtom : public LoadCommandAtom<A>
+{
+public:
+ DyldLoadCommandsAtom(Writer<A>& writer) : LoadCommandAtom<A>(writer) {}
+ virtual const char* getDisplayName() const { return "dyld load command"; }
+ virtual uint64_t getSize() const;
+ virtual void copyRawContent(uint8_t buffer[]) const;
+private:
+ using WriterAtom<A>::fWriter;
+ typedef typename A::P P;
+};
+
+template <typename A>
+class SegmentSplitInfoLoadCommandsAtom : public LoadCommandAtom<A>
+{
+public:
+ SegmentSplitInfoLoadCommandsAtom(Writer<A>& writer) : LoadCommandAtom<A>(writer) {}
+ virtual const char* getDisplayName() const { return "segment split info load command"; }
+ virtual uint64_t getSize() const;
+ virtual void copyRawContent(uint8_t buffer[]) const;
+private:
+ using WriterAtom<A>::fWriter;
+ typedef typename A::P P;
+};
+
+template <typename A>
+class AllowableClientLoadCommandsAtom : public LoadCommandAtom<A>
+{
+public:
+ AllowableClientLoadCommandsAtom(Writer<A>& writer, const char* client) :
+ LoadCommandAtom<A>(writer), clientString(client) {}
+ virtual const char* getDisplayName() const { return "allowable_client load command"; }
+ virtual uint64_t getSize() const;
+ virtual void copyRawContent(uint8_t buffer[]) const;
+private:
+ using WriterAtom<A>::fWriter;
+ typedef typename A::P P;
+ const char* clientString;
+};
+
+template <typename A>
+class DylibLoadCommandsAtom : public LoadCommandAtom<A>
+{
+public:
+ DylibLoadCommandsAtom(Writer<A>& writer, ExecutableFile::DyLibUsed& info)
+ : LoadCommandAtom<A>(writer), fInfo(info),
+ fOptimizedAway(false) { if (fInfo.options.fLazyLoad) this->fOrdinal += 256; }
+ virtual const char* getDisplayName() const { return "dylib load command"; }
+ virtual uint64_t getSize() const;
+ virtual void copyRawContent(uint8_t buffer[]) const;
+ virtual void optimizeAway() { fOptimizedAway = true; }
+ bool linkedWeak() { return fInfo.options.fWeakImport; }
+private:
+ using WriterAtom<A>::fWriter;
+ typedef typename A::P P;
+ ExecutableFile::DyLibUsed fInfo;
+ bool fOptimizedAway;
+};
+
+template <typename A>
+class DylibIDLoadCommandsAtom : public LoadCommandAtom<A>
+{
+public:
+ DylibIDLoadCommandsAtom(Writer<A>& writer) : LoadCommandAtom<A>(writer) {}
+ virtual const char* getDisplayName() const { return "dylib ID load command"; }
+ virtual uint64_t getSize() const;
+ virtual void copyRawContent(uint8_t buffer[]) const;
+private:
+ using WriterAtom<A>::fWriter;
+ typedef typename A::P P;
+};
+
+template <typename A>
+class RoutinesLoadCommandsAtom : public LoadCommandAtom<A>
+{
+public:
+ RoutinesLoadCommandsAtom(Writer<A>& writer) : LoadCommandAtom<A>(writer) {}
+ virtual const char* getDisplayName() const { return "routines load command"; }
+ virtual uint64_t getSize() const { return sizeof(macho_routines_command<typename A::P>); }
+ virtual void copyRawContent(uint8_t buffer[]) const;
+private:
+ using WriterAtom<A>::fWriter;
+ typedef typename A::P P;
+};
+
+template <typename A>
+class SubUmbrellaLoadCommandsAtom : public LoadCommandAtom<A>
+{
+public:
+ SubUmbrellaLoadCommandsAtom(Writer<A>& writer, const char* name)
+ : LoadCommandAtom<A>(writer), fName(name) {}
+ virtual const char* getDisplayName() const { return "sub-umbrella load command"; }
+ virtual uint64_t getSize() const;
+ virtual void copyRawContent(uint8_t buffer[]) const;
+private:
+ typedef typename A::P P;
+ const char* fName;
+};
+
+template <typename A>
+class SubLibraryLoadCommandsAtom : public LoadCommandAtom<A>
+{
+public:
+ SubLibraryLoadCommandsAtom(Writer<A>& writer, const char* nameStart, int nameLen)
+ : LoadCommandAtom<A>(writer), fNameStart(nameStart), fNameLength(nameLen) {}
+ virtual const char* getDisplayName() const { return "sub-library load command"; }
+ virtual uint64_t getSize() const;
+ virtual void copyRawContent(uint8_t buffer[]) const;
+private:
+ using WriterAtom<A>::fWriter;
+ typedef typename A::P P;
+ const char* fNameStart;
+ int fNameLength;
+};
+
+template <typename A>
+class UmbrellaLoadCommandsAtom : public LoadCommandAtom<A>
+{
+public:
+ UmbrellaLoadCommandsAtom(Writer<A>& writer, const char* name)
+ : LoadCommandAtom<A>(writer), fName(name) {}
+ virtual const char* getDisplayName() const { return "umbrella load command"; }
+ virtual uint64_t getSize() const;
+ virtual void copyRawContent(uint8_t buffer[]) const;
+private:
+ using WriterAtom<A>::fWriter;
+ typedef typename A::P P;
+ const char* fName;
+};
+
+template <typename A>
+class UUIDLoadCommandAtom : public LoadCommandAtom<A>
+{
+public:
+ UUIDLoadCommandAtom(Writer<A>& writer)
+ : LoadCommandAtom<A>(writer), fEmit(false) {}
+ virtual const char* getDisplayName() const { return "uuid load command"; }
+ virtual uint64_t getSize() const { return fEmit ? sizeof(macho_uuid_command<typename A::P>) : 0; }
+ virtual void copyRawContent(uint8_t buffer[]) const;
+ virtual void generate();
+ void setContent(const uint8_t uuid[16]);
+ const uint8_t* getUUID() { return fUUID; }
+private:
+ using WriterAtom<A>::fWriter;
+ typedef typename A::P P;
+ uuid_t fUUID;
+ bool fEmit;
+};
+
+
+template <typename A>
+class RPathLoadCommandsAtom : public LoadCommandAtom<A>
+{
+public:
+ RPathLoadCommandsAtom(Writer<A>& writer, const char* path)
+ : LoadCommandAtom<A>(writer), fPath(path) {}
+ virtual const char* getDisplayName() const { return "rpath load command"; }
+ virtual uint64_t getSize() const;
+ virtual void copyRawContent(uint8_t buffer[]) const;
+private:
+ using WriterAtom<A>::fWriter;
+ typedef typename A::P P;
+ const char* fPath;
+};
+
+template <typename A>
+class EncryptionLoadCommandsAtom : public LoadCommandAtom<A>
+{
+public:
+ EncryptionLoadCommandsAtom(Writer<A>& writer)
+ : LoadCommandAtom<A>(writer), fStartOffset(0),
+ fEndOffset(0) {}
+ virtual const char* getDisplayName() const { return "encryption info load command"; }
+ virtual uint64_t getSize() const { return sizeof(macho_encryption_info_command<typename A::P>); }
+ virtual void copyRawContent(uint8_t buffer[]) const;
+ void setStartEncryptionOffset(uint32_t off) { fStartOffset = off; }
+ void setEndEncryptionOffset(uint32_t off) { fEndOffset = off; }
+private:
+ using WriterAtom<A>::fWriter;
+ typedef typename A::P P;
+ uint32_t fStartOffset;
+ uint32_t fEndOffset;
+};
+
+template <typename A>
+class DyldInfoLoadCommandsAtom : public LoadCommandAtom<A>
+{
+public:
+ DyldInfoLoadCommandsAtom(Writer<A>& writer)
+ : LoadCommandAtom<A>(writer) {}
+ virtual const char* getDisplayName() const { return "dyld info load command"; }
+ virtual uint64_t getSize() const { return sizeof(macho_dyld_info_command<typename A::P>); }
+ virtual void copyRawContent(uint8_t buffer[]) const;
+private:
+ using WriterAtom<A>::fWriter;
+ typedef typename A::P P;
+};
+
+
+template <typename A>
+class LoadCommandsPaddingAtom : public WriterAtom<A>
+{
+public:
+ LoadCommandsPaddingAtom(Writer<A>& writer)
+ : WriterAtom<A>(writer, headerSegment(writer)), fSize(0) {}
+ virtual const char* getDisplayName() const { return "header padding"; }
+ virtual uint64_t getSize() const { return fSize; }
+ virtual const char* getSectionName() const { return "._load_cmds_pad"; }
+ virtual void copyRawContent(uint8_t buffer[]) const;
+
+ void setSize(uint64_t newSize);
+private:
+ using WriterAtom<A>::fWriter;
+ typedef typename A::P P;
+ uint64_t fSize;
+};
+
+template <typename A>
+class UnwindInfoAtom : public WriterAtom<A>
+{
+public:
+ UnwindInfoAtom(Writer<A>& writer) : WriterAtom<A>(writer, Segment::fgTextSegment),
+ fHeaderSize(0), fPagesSize(0), fAlignment(4) {}
+ virtual const char* getName() const { return "unwind info"; }
+ virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeTranslationUnit; }
+ virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; }
+ virtual uint64_t getSize() const { return fHeaderSize+fPagesSize; }
+ virtual ObjectFile::Alignment getAlignment() const { return fAlignment; }
+ virtual const char* getSectionName() const { return "__unwind_info"; }
+ virtual uint32_t getOrdinal() const { return 1; }
+ virtual std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)fReferences; }
+ virtual void copyRawContent(uint8_t buffer[]) const;
+
+ void addUnwindInfo(ObjectFile::Atom* func, uint32_t offset, uint32_t encoding,
+ ObjectFile::Reference* fdeRef, ObjectFile::Reference* lsda,
+ ObjectFile::Atom* personalityPointer);
+ void generate();
+
+private:
+ using WriterAtom<A>::fWriter;
+ typedef typename A::P P;
+ struct Info { ObjectFile::Atom* func; ObjectFile::Atom* fde; ObjectFile::Atom* lsda; uint32_t lsdaOffset; ObjectFile::Atom* personalityPointer; uint32_t encoding; };
+ struct LSDAEntry { ObjectFile::Atom* func; ObjectFile::Atom* lsda; uint32_t lsdaOffset; };
+ struct RegFixUp { uint8_t* contentPointer; ObjectFile::Atom* func; ObjectFile::Atom* fde; };
+ struct CompressedFixUp { uint8_t* contentPointer; ObjectFile::Atom* func; ObjectFile::Atom* fromFunc; };
+ struct CompressedEncodingFixUp { uint8_t* contentPointer; ObjectFile::Atom* fde; };
+
+ bool encodingMeansUseDwarf(compact_unwind_encoding_t encoding);
+ void compressDuplicates(std::vector<Info>& uniqueInfos);
+ void findCommonEncoding(const std::vector<Info>& uniqueInfos, std::map<uint32_t, unsigned int>& commonEncodings);
+ void makeLsdaIndex(const std::vector<Info>& uniqueInfos, std::map<ObjectFile::Atom*, uint32_t>& lsdaIndexOffsetMap);
+ unsigned int makeRegularSecondLevelPage(const std::vector<Info>& uniqueInfos, uint32_t pageSize, unsigned int endIndex,
+ uint8_t*& pageEnd);
+ unsigned int makeCompressedSecondLevelPage(const std::vector<Info>& uniqueInfos,
+ const std::map<uint32_t,unsigned int> commonEncodings,
+ uint32_t pageSize, unsigned int endIndex, uint8_t*& pageEnd);
+ void makePersonalityIndex(std::vector<Info>& uniqueInfos);
+
+
+ uint32_t fHeaderSize;
+ uint32_t fPagesSize;
+ uint8_t* fHeaderContent;
+ uint8_t* fPagesContent;
+ uint8_t* fPagesContentForDelete;
+ ObjectFile::Alignment fAlignment;
+ std::vector<Info> fInfos;
+ std::map<ObjectFile::Atom*, uint32_t> fPersonalityIndexMap;
+ std::vector<LSDAEntry> fLSDAIndex;
+ std::vector<RegFixUp> fRegFixUps;
+ std::vector<CompressedFixUp> fCompressedFixUps;
+ std::vector<CompressedEncodingFixUp> fCompressedEncodingFixUps;
+ std::vector<ObjectFile::Reference*> fReferences;
+};
+
+
+
+template <typename A>
+class LinkEditAtom : public WriterAtom<A>
+{
+public:
+ LinkEditAtom(Writer<A>& writer) : WriterAtom<A>(writer, Segment::fgLinkEditSegment), fOrdinal(fgCurrentOrdinal++) {}
+ uint64_t getFileOffset() const;
+ virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(log2(sizeof(typename A::P::uint_t))); }
+ virtual uint32_t getOrdinal() const { return fOrdinal; }
+private:
+ uint32_t fOrdinal;
+ static uint32_t fgCurrentOrdinal;
+private:
+ typedef typename A::P P;
+};
+
+template <typename A> uint32_t LinkEditAtom<A>::fgCurrentOrdinal = 0;
+
+template <typename A>
+class SectionRelocationsLinkEditAtom : public LinkEditAtom<A>
+{
+public:
+ SectionRelocationsLinkEditAtom(Writer<A>& writer) : LinkEditAtom<A>(writer) { }
+ virtual const char* getDisplayName() const { return "section relocations"; }
+ virtual uint64_t getSize() const;
+ virtual const char* getSectionName() const { return "._section_relocs"; }
+ virtual void copyRawContent(uint8_t buffer[]) const;
+private:
+ using WriterAtom<A>::fWriter;
+ typedef typename A::P P;
+};
+
+template <typename A>
+class CompressedInfoLinkEditAtom : public LinkEditAtom<A>
+{
+public:
+ CompressedInfoLinkEditAtom(Writer<A>& writer) : LinkEditAtom<A>(writer) { }
+ virtual uint64_t getSize() const { return fEncodedData.size(); }
+ virtual void copyRawContent(uint8_t buffer[]) const { memcpy(buffer, fEncodedData.start(), fEncodedData.size()); }
+protected:
+ typedef typename A::P::uint_t pint_t;
+ ByteStream fEncodedData;
+private:
+ using WriterAtom<A>::fWriter;
+ typedef typename A::P P;
+};
+
+
+
+template <typename A>
+class CompressedRebaseInfoLinkEditAtom : public CompressedInfoLinkEditAtom<A>
+{
+public:
+ CompressedRebaseInfoLinkEditAtom(Writer<A>& writer) : CompressedInfoLinkEditAtom<A>(writer) { }
+ virtual const char* getDisplayName() const { return "compressed rebase info"; }
+ virtual const char* getSectionName() const { return "._rebase info"; }
+ void encode();
+private:
+ using CompressedInfoLinkEditAtom<A>::fEncodedData;
+ using CompressedInfoLinkEditAtom<A>::fWriter;
+ typedef typename A::P P;
+ typedef typename A::P::uint_t pint_t;
+};
+
+template <typename A>
+class CompressedBindingInfoLinkEditAtom : public CompressedInfoLinkEditAtom<A>
+{
+public:
+ CompressedBindingInfoLinkEditAtom(Writer<A>& writer) : CompressedInfoLinkEditAtom<A>(writer) { }
+ virtual const char* getDisplayName() const { return "compressed binding info"; }
+ virtual const char* getSectionName() const { return "._binding info"; }
+ void encode();
+private:
+ using CompressedInfoLinkEditAtom<A>::fWriter;
+ using CompressedInfoLinkEditAtom<A>::fEncodedData;
+ typedef typename A::P P;
+ typedef typename A::P::uint_t pint_t;
+};
+
+template <typename A>
+class CompressedWeakBindingInfoLinkEditAtom : public CompressedInfoLinkEditAtom<A>
+{
+public:
+ CompressedWeakBindingInfoLinkEditAtom(Writer<A>& writer) : CompressedInfoLinkEditAtom<A>(writer) { }
+ virtual const char* getDisplayName() const { return "compressed weak binding info"; }
+ virtual const char* getSectionName() const { return "._wkbinding info"; }
+ void encode();
+private:
+ using CompressedInfoLinkEditAtom<A>::fWriter;
+ using CompressedInfoLinkEditAtom<A>::fEncodedData;
+ typedef typename A::P P;
+ typedef typename A::P::uint_t pint_t;
+};
+
+template <typename A>
+class CompressedLazyBindingInfoLinkEditAtom : public CompressedInfoLinkEditAtom<A>
+{
+public:
+ CompressedLazyBindingInfoLinkEditAtom(Writer<A>& writer) : CompressedInfoLinkEditAtom<A>(writer) { }
+ virtual const char* getDisplayName() const { return "compressed lazy binding info"; }
+ virtual const char* getSectionName() const { return "._lzbinding info"; }
+ void encode();
+private:
+ std::vector<uint32_t> fStarts;
+
+ using CompressedInfoLinkEditAtom<A>::fWriter;
+ using CompressedInfoLinkEditAtom<A>::fEncodedData;
+ typedef typename A::P P;
+ typedef typename A::P::uint_t pint_t;
+};
+
+
+template <typename A>
+class CompressedExportInfoLinkEditAtom : public CompressedInfoLinkEditAtom<A>
+{
+public:
+ CompressedExportInfoLinkEditAtom(Writer<A>& writer)
+ : CompressedInfoLinkEditAtom<A>(writer), fStartNode(strdup("")) { }
+ virtual const char* getDisplayName() const { return "compressed export info"; }
+ virtual const char* getSectionName() const { return "._export info"; }
+ void encode();
+private:
+ using WriterAtom<A>::fWriter;
+ using CompressedInfoLinkEditAtom<A>::fEncodedData;
+ typedef typename A::P P;
+ typedef typename A::P::uint_t pint_t;
+ struct node;
+
+ struct edge
+ {
+ edge(const char* s, struct node* n) : fSubString(s), fChild(n) { }
+ ~edge() { }
+ const char* fSubString;
+ struct node* fChild;
+
+ };
+
+ struct node
+ {
+ node(const char* s) : fCummulativeString(s), fAddress(0), fFlags(0), fOrdered(false),
+ fHaveExportInfo(false), fTrieOffset(0) {}
+ ~node() { }
+ const char* fCummulativeString;
+ std::vector<edge> fChildren;
+ uint64_t fAddress;
+ uint32_t fFlags;
+ bool fOrdered;
+ bool fHaveExportInfo;
+ uint32_t fTrieOffset;
+
+ void addSymbol(const char* fullStr, uint64_t address, uint32_t flags) {
+ const char* partialStr = &fullStr[strlen(fCummulativeString)];
+ for (typename std::vector<edge>::iterator it = fChildren.begin(); it != fChildren.end(); ++it) {
+ edge& e = *it;
+ int subStringLen = strlen(e.fSubString);
+ if ( strncmp(e.fSubString, partialStr, subStringLen) == 0 ) {
+ // already have matching edge, go down that path
+ e.fChild->addSymbol(fullStr, address, flags);
+ return;
+ }
+ else {
+ for (int i=subStringLen-1; i > 0; --i) {
+ if ( strncmp(e.fSubString, partialStr, i) == 0 ) {
+ // found a common substring, splice in new node
+ // was A -> C, now A -> B -> C
+ char* bNodeCummStr = strdup(e.fChild->fCummulativeString);
+ bNodeCummStr[strlen(bNodeCummStr)+i-subStringLen] = '\0';
+ //node* aNode = this;
+ node* bNode = new node(bNodeCummStr);
+ node* cNode = e.fChild;
+ char* abEdgeStr = strdup(e.fSubString);
+ abEdgeStr[i] = '\0';
+ char* bcEdgeStr = strdup(&e.fSubString[i]);
+ edge& abEdge = e;
+ abEdge.fSubString = abEdgeStr;
+ abEdge.fChild = bNode;
+ edge bcEdge(bcEdgeStr, cNode);
+ bNode->fChildren.push_back(bcEdge);
+ bNode->addSymbol(fullStr, address, flags);
+ return;
+ }
+ }
+ }
+ }
+ // no commonality with any existing child, make a new edge that is this whole string
+ node* newNode = new node(strdup(fullStr));
+ edge newEdge(strdup(partialStr), newNode);
+ fChildren.push_back(newEdge);
+ newNode->fAddress = address;
+ newNode->fFlags = flags;
+ newNode->fHaveExportInfo = true;
+ }
+
+ void addOrderedNodes(const char* name, std::vector<node*>& orderedNodes) {
+ if ( !fOrdered ) {
+ orderedNodes.push_back(this);
+ //fprintf(stderr, "ordered %p %s\n", this, fCummulativeString);
+ fOrdered = true;
+ }
+ const char* partialStr = &name[strlen(fCummulativeString)];
+ for (typename std::vector<edge>::iterator it = fChildren.begin(); it != fChildren.end(); ++it) {
+ edge& e = *it;
+ int subStringLen = strlen(e.fSubString);
+ if ( strncmp(e.fSubString, partialStr, subStringLen) == 0 ) {
+ // already have matching edge, go down that path
+ e.fChild->addOrderedNodes(name, orderedNodes);
+ return;
+ }
+ }
+ }
+
+ // byte for terminal node size in bytes, or 0x00 if not terminal node
+ // teminal node (uleb128 flags, uleb128 addr)
+ // byte for child node count
+ // each child: zero terminated substring, uleb128 node offset
+ bool updateOffset(uint32_t& offset) {
+ uint32_t nodeSize = 1; // byte for length of export info
+ if ( fHaveExportInfo )
+ nodeSize += ByteStream::uleb128_size(fFlags) + ByteStream::uleb128_size(fAddress);
+
+ // add children
+ ++nodeSize; // byte for count of chidren
+ for (typename std::vector<edge>::iterator it = fChildren.begin(); it != fChildren.end(); ++it) {
+ edge& e = *it;
+ nodeSize += strlen(e.fSubString) + 1 + ByteStream::uleb128_size(e.fChild->fTrieOffset);
+ }
+ bool result = (fTrieOffset != offset);
+ fTrieOffset = offset;
+ //fprintf(stderr, "updateOffset %p %05d %s\n", this, fTrieOffset, fCummulativeString);
+ offset += nodeSize;
+ // return true if fTrieOffset was changed
+ return result;
+ }
+
+ void appendToStream(ByteStream& out) {
+ if ( fHaveExportInfo ) {
+ // nodes with export info: size, flags, address
+ out.append_byte(out.uleb128_size(fFlags) + out.uleb128_size(fAddress));
+ out.append_uleb128(fFlags);
+ out.append_uleb128(fAddress);
+ }
+ else {
+ // no export info
+ out.append_byte(0);
+ }
+ // write number of children
+ out.append_byte(fChildren.size());
+ // write each child
+ for (typename std::vector<edge>::iterator it = fChildren.begin(); it != fChildren.end(); ++it) {
+ edge& e = *it;
+ out.append_string(e.fSubString);
+ out.append_uleb128(e.fChild->fTrieOffset);
+ }
+ }
+
+ };
+
+
+ struct node fStartNode;
+};
+
+template <typename A>
+class LocalRelocationsLinkEditAtom : public LinkEditAtom<A>
+{
+public:
+ LocalRelocationsLinkEditAtom(Writer<A>& writer) : LinkEditAtom<A>(writer) { }
+ virtual const char* getDisplayName() const { return "local relocations"; }
+ virtual uint64_t getSize() const;
+ virtual const char* getSectionName() const { return "._local_relocs"; }
+ virtual void copyRawContent(uint8_t buffer[]) const;
+private:
+ using WriterAtom<A>::fWriter;
+ typedef typename A::P P;
+};
+
+template <typename A>
+class SymbolTableLinkEditAtom : public LinkEditAtom<A>
+{
+public:
+ SymbolTableLinkEditAtom(Writer<A>& writer) : LinkEditAtom<A>(writer) { }
+ virtual const char* getDisplayName() const { return "symbol table"; }
+ virtual uint64_t getSize() const;
+ virtual const char* getSectionName() const { return "._symbol_table"; }
+ virtual void copyRawContent(uint8_t buffer[]) const;
+private:
+ using WriterAtom<A>::fWriter;
+ typedef typename A::P P;
+};
+
+template <typename A>
+class ExternalRelocationsLinkEditAtom : public LinkEditAtom<A>
+{
+public:
+ ExternalRelocationsLinkEditAtom(Writer<A>& writer) : LinkEditAtom<A>(writer) { }
+ virtual const char* getDisplayName() const { return "external relocations"; }
+ virtual uint64_t getSize() const;
+ virtual const char* getSectionName() const { return "._extern_relocs"; }
+ virtual void copyRawContent(uint8_t buffer[]) const;
+private:
+ using WriterAtom<A>::fWriter;
+ typedef typename A::P P;
+};
+
+struct IndirectEntry {
+ uint32_t indirectIndex;
+ uint32_t symbolIndex;
+};
+
+
+template <typename A>
+class SegmentSplitInfoContentAtom : public LinkEditAtom<A>
+{
+public:
+ SegmentSplitInfoContentAtom(Writer<A>& writer) : LinkEditAtom<A>(writer), fCantEncode(false) { }
+ virtual const char* getDisplayName() const { return "split segment info"; }
+ virtual uint64_t getSize() const;
+ virtual const char* getSectionName() const { return "._split_info"; }
+ virtual void copyRawContent(uint8_t buffer[]) const;
+ bool canEncode() { return !fCantEncode; }
+ void setCantEncode() { fCantEncode = true; }
+ void add32bitPointerLocation(const ObjectFile::Atom* atom, uint32_t offset) { fKind1Locations.push_back(AtomAndOffset(atom, offset)); }
+ void add64bitPointerLocation(const ObjectFile::Atom* atom, uint32_t offset) { fKind2Locations.push_back(AtomAndOffset(atom, offset)); }
+ void addPPCHi16Location(const ObjectFile::Atom* atom, uint32_t offset) { fKind3Locations.push_back(AtomAndOffset(atom, offset)); }
+ void add32bitImportLocation(const ObjectFile::Atom* atom, uint32_t offset) { fKind4Locations.push_back(AtomAndOffset(atom, offset)); }
+ void encode();
+
+private:
+ using WriterAtom<A>::fWriter;
+ typedef typename A::P P;
+ typedef typename A::P::uint_t pint_t;
+ struct AtomAndOffset {
+ AtomAndOffset(const ObjectFile::Atom* a, uint32_t off) : atom(a), offset(off) {}
+ const ObjectFile::Atom* atom;
+ uint32_t offset;
+ };
+ void uleb128EncodeAddresses(const std::vector<AtomAndOffset>& locations);
+
+ std::vector<AtomAndOffset> fKind1Locations;
+ std::vector<AtomAndOffset> fKind2Locations;
+ std::vector<AtomAndOffset> fKind3Locations;
+ std::vector<AtomAndOffset> fKind4Locations;
+ std::vector<uint8_t> fEncodedData;
+ bool fCantEncode;
+};
+
+template <typename A>
+class IndirectTableLinkEditAtom : public LinkEditAtom<A>
+{
+public:
+ IndirectTableLinkEditAtom(Writer<A>& writer) : LinkEditAtom<A>(writer) { }
+ virtual const char* getDisplayName() const { return "indirect symbol table"; }
+ virtual uint64_t getSize() const;
+ virtual const char* getSectionName() const { return "._indirect_syms"; }
+ virtual void copyRawContent(uint8_t buffer[]) const;
+
+ std::vector<IndirectEntry> fTable;
+
+private:
+ using WriterAtom<A>::fWriter;
+ typedef typename A::P P;
+};
+
+template <typename A>
+class ModuleInfoLinkEditAtom : public LinkEditAtom<A>
+{
+public:
+ ModuleInfoLinkEditAtom(Writer<A>& writer) : LinkEditAtom<A>(writer), fModuleNameOffset(0) { }
+ virtual const char* getDisplayName() const { return "module table"; }
+ virtual uint64_t getSize() const;
+ virtual const char* getSectionName() const { return "._module_info"; }
+ virtual void copyRawContent(uint8_t buffer[]) const;
+
+ void setName() { fModuleNameOffset = fWriter.fStringsAtom->add("single module"); }
+ uint32_t getTableOfContentsFileOffset() const;
+ uint32_t getModuleTableFileOffset() const;
+ uint32_t getReferencesFileOffset() const;
+ uint32_t getReferencesCount() const;
+
+private:
+ using WriterAtom<A>::fWriter;
+ typedef typename A::P P;
+ typedef typename A::P::uint_t pint_t;
+ uint32_t fModuleNameOffset;
+};
+
+
+class CStringEquals
+{
+public:
+ bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
+};
+
+template <typename A>
+class StringsLinkEditAtom : public LinkEditAtom<A>
+{
+public:
+ StringsLinkEditAtom(Writer<A>& writer);
+ virtual const char* getDisplayName() const { return "string pool"; }
+ virtual uint64_t getSize() const;
+ virtual const char* getSectionName() const { return "._string_pool"; }
+ virtual void copyRawContent(uint8_t buffer[]) const;
+
+ int32_t add(const char* name);
+ int32_t addUnique(const char* name);
+ int32_t emptyString() { return 1; }
+ const char* stringForIndex(int32_t) const;
+
+private:
+ using WriterAtom<A>::fWriter;
+ typedef typename A::P P;
+ enum { kBufferSize = 0x01000000 };
+ typedef __gnu_cxx::hash_map<const char*, int32_t, __gnu_cxx::hash<const char*>, CStringEquals> StringToOffset;
+
+ std::vector<char*> fFullBuffers;
+ char* fCurrentBuffer;
+ uint32_t fCurrentBufferUsed;
+ StringToOffset fUniqueStrings;
+};
+
+
+
+template <typename A>
+class UndefinedSymbolProxyAtom : public WriterAtom<A>
+{
+public:
+ UndefinedSymbolProxyAtom(Writer<A>& writer, const char* name) : WriterAtom<A>(writer, Segment::fgLinkEditSegment), fName(name) {}
+ virtual const char* getName() const { return fName; }
+ virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeGlobal; }
+ virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ObjectFile::Atom::kExternalDefinition; }
+ virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableIn; }
+ virtual uint64_t getSize() const { return 0; }
+ virtual const char* getSectionName() const { return "._imports"; }
+private:
+ using WriterAtom<A>::fWriter;
+ typedef typename A::P P;
+ const char* fName;
+};
+
+template <typename A>
+class BranchIslandAtom : public WriterAtom<A>
+{
+public:
+ BranchIslandAtom(Writer<A>& writer, const char* name, int islandRegion, ObjectFile::Atom& target, uint32_t targetOffset);
+ virtual const char* getName() const { return fName; }
+ virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; }
+ virtual uint64_t getSize() const;
+ virtual const char* getSectionName() const { return "__text"; }
+ virtual void copyRawContent(uint8_t buffer[]) const;
+private:
+ using WriterAtom<A>::fWriter;
+ const char* fName;
+ ObjectFile::Atom& fTarget;
+ uint32_t fTargetOffset;
+};
+
+template <typename A>
+class StubAtom : public WriterAtom<A>
+{
+public:
+ StubAtom(Writer<A>& writer, ObjectFile::Atom& target, bool forLazyDylib);
+ virtual const char* getName() const { return fName; }
+ virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; }
+ virtual uint64_t getSize() const;
+ virtual ObjectFile::Alignment getAlignment() const;
+ virtual const char* getSectionName() const { return "__symbol_stub1"; }
+ 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);
+ bool pic() const { return fWriter.fSlideable; }
+ using WriterAtom<A>::fWriter;
+ const char* fName;
+ ObjectFile::Atom& fTarget;
+ std::vector<ObjectFile::Reference*> fReferences;
+ bool fForLazyDylib;
+};
+
+
+template <typename A>
+class FastStubHelperHelperAtom : public WriterAtom<A>
+{
+public:
+ FastStubHelperHelperAtom(Writer<A>& writer);
+ virtual const char* getName() const { return " stub helpers"; } // name sorts to start of helpers
+ virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableIn; }
+ virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; }
+ 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;
+ virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); }
+protected:
+ using WriterAtom<A>::fWriter;
+ std::vector<ObjectFile::Reference*> fReferences;
+};
+
+template <typename A>
+class HybridStubHelperHelperAtom : public WriterAtom<A>
+{
+public:
+ HybridStubHelperHelperAtom(Writer<A>& writer);
+ virtual const char* getName() const { return " stub helpers"; } // name sorts to start of helpers
+ virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableIn; }
+ virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; }
+ 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;
+ virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); }
+protected:
+ using WriterAtom<A>::fWriter;
+ std::vector<ObjectFile::Reference*> fReferences;
+};
+
+template <typename A>
+class StubHelperAtom : public WriterAtom<A>
+{
+public:
+ StubHelperAtom(Writer<A>& writer, ObjectFile::Atom& target,
+ LazyPointerAtom<A>& lazyPointer, bool forLazyDylib)
+ : WriterAtom<A>(writer, Segment::fgTextSegment), fName(stubName(target.getName())),
+ fTarget(target), fLazyPointerAtom(lazyPointer) {
+ writer.fAllSynthesizedStubHelpers.push_back(this);
+ }
+
+ virtual const char* getName() const { return fName; }
+ virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; }
+ virtual const char* getSectionName() const { return "__stub_helper"; }
+ virtual std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(fReferences); }
+ ObjectFile::Atom* getTarget() { return &fTarget; }
+ virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); }
+protected:
+ static const char* stubName(const char* importName);
+ using WriterAtom<A>::fWriter;
+ const char* fName;
+ ObjectFile::Atom& fTarget;
+ LazyPointerAtom<A>& fLazyPointerAtom;
+ std::vector<ObjectFile::Reference*> fReferences;
+};
+
+template <typename A>
+class ClassicStubHelperAtom : public StubHelperAtom<A>
+{
+public:
+ ClassicStubHelperAtom(Writer<A>& writer, ObjectFile::Atom& target,
+ class LazyPointerAtom<A>& lazyPointer, bool forLazyDylib);
+
+ virtual uint64_t getSize() const;
+ virtual void copyRawContent(uint8_t buffer[]) const;
+};
+
+
+template <typename A>
+class HybridStubHelperAtom : public StubHelperAtom<A>
+{
+public:
+ HybridStubHelperAtom(Writer<A>& writer, ObjectFile::Atom& target,
+ class LazyPointerAtom<A>& lazyPointer, bool forLazyDylib);
+
+ virtual uint64_t getSize() const;
+ virtual void copyRawContent(uint8_t buffer[]) const;
+ static class HybridStubHelperHelperAtom<A>* fgHelperHelperAtom;
+};
+template <typename A> class HybridStubHelperHelperAtom<A>* HybridStubHelperAtom<A>::fgHelperHelperAtom = NULL;
+
+template <typename A>
+class FastStubHelperAtom : public StubHelperAtom<A>
+{
+public:
+ FastStubHelperAtom(Writer<A>& writer, ObjectFile::Atom& target,
+ class LazyPointerAtom<A>& lazyPointer, bool forLazyDylib);
+ virtual uint64_t getSize() const;
+ virtual void copyRawContent(uint8_t buffer[]) const;
+ static FastStubHelperHelperAtom<A>* fgHelperHelperAtom;
+};
+template <typename A> FastStubHelperHelperAtom<A>* FastStubHelperAtom<A>::fgHelperHelperAtom = NULL;
+
+
+
+template <typename A>
+class LazyPointerAtom : public WriterAtom<A>
+{
+public:
+ LazyPointerAtom(Writer<A>& writer, ObjectFile::Atom& target,
+ StubAtom<A>& stub, bool forLazyDylib);
+ virtual const char* getName() const { return fName; }
+ virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; }
+ virtual uint64_t getSize() const { return sizeof(typename A::P::uint_t); }
+ virtual const char* getSectionName() const { return fForLazyDylib ? "__ld_symbol_ptr" : "__la_symbol_ptr"; }
+ virtual std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(fReferences); }
+ virtual void copyRawContent(uint8_t buffer[]) const;
+ ObjectFile::Atom* getTarget() { return &fExternalTarget; }
+ void setLazyBindingInfoOffset(uint32_t off) { fLazyBindingOffset = off; }
+ uint32_t getLazyBindingInfoOffset() { return fLazyBindingOffset; }
+private:
+ using WriterAtom<A>::fWriter;
+ static const char* lazyPointerName(const char* importName);
+ const char* fName;
+ ObjectFile::Atom& fTarget;
+ ObjectFile::Atom& fExternalTarget;
+ std::vector<ObjectFile::Reference*> fReferences;
+ bool fForLazyDylib;
+ uint32_t fLazyBindingOffset;
+};
+
+
+template <typename A>
+class NonLazyPointerAtom : public WriterAtom<A>
+{
+public:
+ NonLazyPointerAtom(Writer<A>& writer, ObjectFile::Atom& target);
+ NonLazyPointerAtom(Writer<A>& writer, const char* targetName);
+ NonLazyPointerAtom(Writer<A>& writer);
+ virtual const char* getName() const { return fName; }
+ virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; }
+ virtual uint64_t getSize() const { return sizeof(typename A::P::uint_t); }
+ virtual const char* getSectionName() const { return (fWriter.fOptions.outputKind() == Options::kKextBundle) ? "__got" : "__nl_symbol_ptr"; }
+ 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:
+ using WriterAtom<A>::fWriter;
+ static const char* nonlazyPointerName(const char* importName);
+ const char* fName;
+ ObjectFile::Atom* fTarget;
+ std::vector<ObjectFile::Reference*> fReferences;
+};
+
+
+template <typename A>
+class ObjCInfoAtom : public WriterAtom<A>
+{
+public:
+ ObjCInfoAtom(Writer<A>& writer, ObjectFile::Reader::ObjcConstraint objcContraint,
+ bool objcReplacementClasses);
+ virtual const char* getName() const { return "objc$info"; }
+ virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; }
+ virtual uint64_t getSize() const { return 8; }
+ virtual const char* getSectionName() const;
+ virtual void copyRawContent(uint8_t buffer[]) const;
+private:
+ Segment& getInfoSegment() const;
+ uint32_t fContent[2];
+};
+
+
+template <typename A>
+class WriterReference : public ObjectFile::Reference
+{
+public:
+ typedef typename A::ReferenceKinds Kinds;
+
+ WriterReference(uint32_t offset, Kinds kind, ObjectFile::Atom* target,
+ uint32_t toOffset=0, ObjectFile::Atom* fromTarget=NULL, uint32_t fromOffset=0)
+ : fKind(kind), fFixUpOffsetInSrc(offset), fTarget(target), fTargetName(target->getName()),
+ fTargetOffset(toOffset), fFromTarget(fromTarget), fFromTargetOffset(fromOffset) {}
+ WriterReference(uint32_t offset, Kinds kind, const char* targetName)
+ : fKind(kind), fFixUpOffsetInSrc(offset), fTarget(NULL), fTargetName(targetName),
+ fTargetOffset(0), fFromTarget(NULL), fFromTargetOffset(0) {}
+
+ virtual ~WriterReference() {}
+
+ virtual ObjectFile::Reference::TargetBinding getTargetBinding() const { return (fTarget != NULL) ? ObjectFile::Reference::kBoundDirectly : ObjectFile::Reference::kUnboundByName; }
+ virtual ObjectFile::Reference::TargetBinding getFromTargetBinding() const { return (fFromTarget != NULL) ? ObjectFile::Reference::kBoundDirectly : ObjectFile::Reference::kDontBind; }
+ virtual uint8_t getKind() const { return (uint8_t)fKind; }
+ virtual uint64_t getFixUpOffset() const { return fFixUpOffsetInSrc; }
+ virtual const char* getTargetName() const { return fTargetName; }
+ virtual ObjectFile::Atom& getTarget() const { return *fTarget; }
+ virtual uint64_t getTargetOffset() const { return fTargetOffset; }
+ virtual ObjectFile::Atom& getFromTarget() const { return *fFromTarget; }
+ virtual const char* getFromTargetName() const { return fFromTarget->getName(); }
+ virtual void setTarget(ObjectFile::Atom& target, uint64_t offset) { fTarget = ⌖ fTargetOffset = offset; }
+ virtual void setFromTarget(ObjectFile::Atom& target) { fFromTarget = ⌖ }
+ virtual void setFromTargetName(const char* name) { }
+ virtual void setFromTargetOffset(uint64_t offset) { fFromTargetOffset = offset; }
+ virtual const char* getDescription() const { return "writer reference"; }
+ virtual uint64_t getFromTargetOffset() const { return fFromTargetOffset; }
+
+private:
+ Kinds fKind;
+ uint32_t fFixUpOffsetInSrc;
+ ObjectFile::Atom* fTarget;
+ const char* fTargetName;
+ uint32_t fTargetOffset;
+ ObjectFile::Atom* fFromTarget;
+ uint32_t fFromTargetOffset;
+};
+
+
+template <typename A>
+const char* StubHelperAtom<A>::stubName(const char* name)
+{
+ char* buf;
+ asprintf(&buf, "%s$stubHelper", name);
+ return buf;
+}
+
+template <>
+ClassicStubHelperAtom<x86_64>::ClassicStubHelperAtom(Writer<x86_64>& writer, ObjectFile::Atom& target,
+ class LazyPointerAtom<x86_64>& lazyPointer, bool forLazyDylib)
+ : StubHelperAtom<x86_64>(writer, target, lazyPointer, forLazyDylib)
+{
+ fReferences.push_back(new WriterReference<x86_64>(3, x86_64::kPCRel32, &fLazyPointerAtom));
+ if ( forLazyDylib ) {
+ if ( fWriter.fDyldLazyDylibHelper == NULL )
+ throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)";
+ fReferences.push_back(new WriterReference<x86_64>(8, x86_64::kPCRel32, fWriter.fDyldLazyDylibHelper));
+ }
+ else {
+ if ( fWriter.fDyldClassicHelperAtom == NULL )
+ throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)";
+ fReferences.push_back(new WriterReference<x86_64>(8, x86_64::kPCRel32, fWriter.fDyldClassicHelperAtom));
+ }
+}
+
+
+template <>
+uint64_t ClassicStubHelperAtom<x86_64>::getSize() const
+{
+ return 12;
+}
+
+template <>
+void ClassicStubHelperAtom<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 <>
+FastStubHelperHelperAtom<x86_64>::FastStubHelperHelperAtom(Writer<x86_64>& writer)
+ : WriterAtom<x86_64>(writer, Segment::fgTextSegment)
+{
+ fReferences.push_back(new WriterReference<x86_64>(3, x86_64::kPCRel32, new NonLazyPointerAtom<x86_64>(writer)));
+ fReferences.push_back(new WriterReference<x86_64>(11, x86_64::kPCRel32, writer.fFastStubGOTAtom));
+}
+
+template <>
+uint64_t FastStubHelperHelperAtom<x86_64>::getSize() const
+{
+ return 16;
+}
+
+template <>
+void FastStubHelperHelperAtom<x86_64>::copyRawContent(uint8_t buffer[]) const
+{
+ buffer[0] = 0x4C; // leaq dyld_mageLoaderCache(%rip),%r11
+ buffer[1] = 0x8D;
+ buffer[2] = 0x1D;
+ buffer[3] = 0x00;
+ buffer[4] = 0x00;
+ buffer[5] = 0x00;
+ buffer[6] = 0x00;
+ buffer[7] = 0x41; // pushq %r11
+ buffer[8] = 0x53;
+ buffer[9] = 0xFF; // jmp *_fast_lazy_bind(%rip)
+ buffer[10] = 0x25;
+ buffer[11] = 0x00;
+ buffer[12] = 0x00;
+ buffer[13] = 0x00;
+ buffer[14] = 0x00;
+ buffer[15] = 0x90; // nop
+}
+
+
+template <>
+HybridStubHelperHelperAtom<x86_64>::HybridStubHelperHelperAtom(Writer<x86_64>& writer)
+ : WriterAtom<x86_64>(writer, Segment::fgTextSegment)
+{
+ if ( writer.fDyldClassicHelperAtom == NULL )
+ throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)";
+ fReferences.push_back(new WriterReference<x86_64>(3, x86_64::kPCRel32_1, writer.fFastStubGOTAtom));
+ fReferences.push_back(new WriterReference<x86_64>(13, x86_64::kPCRel32, new NonLazyPointerAtom<x86_64>(writer)));
+ fReferences.push_back(new WriterReference<x86_64>(21, x86_64::kPCRel32, writer.fFastStubGOTAtom));
+ fReferences.push_back(new WriterReference<x86_64>(30, x86_64::kPCRel32, writer.fDyldClassicHelperAtom));
+}
+
+template <>
+uint64_t HybridStubHelperHelperAtom<x86_64>::getSize() const
+{
+ return 34;
+}
+
+template <>
+void HybridStubHelperHelperAtom<x86_64>::copyRawContent(uint8_t buffer[]) const
+{
+ buffer[0] = 0x48; // cmpl $0x00,_fast_lazy_bind
+ buffer[1] = 0x83;
+ buffer[2] = 0x3D;
+ buffer[3] = 0x00;
+ buffer[4] = 0x00;
+ buffer[5] = 0x00;
+ buffer[6] = 0x00;
+ buffer[7] = 0x00;
+ buffer[8] = 0x74; // je 16
+ buffer[9] = 0x0F;
+ buffer[10] = 0x4C; // leaq imageCache(%rip),%r11
+ buffer[11] = 0x8D;
+ buffer[12] = 0x1D;
+ buffer[13] = 0x00;
+ buffer[14] = 0x00;
+ buffer[15] = 0x00;
+ buffer[16] = 0x00;
+ buffer[17] = 0x41; // pushq %r11
+ buffer[18] = 0x53;
+ buffer[19] = 0xFF; // jmp *_fast_lazy_bind(%rip)
+ buffer[20] = 0x25;
+ buffer[21] = 0x00;
+ buffer[22] = 0x00;
+ buffer[23] = 0x00;
+ buffer[24] = 0x00;
+ buffer[25] = 0x48; // addq $8,%rsp
+ buffer[26] = 0x83;
+ buffer[27] = 0xC4;
+ buffer[28] = 0x08;
+ buffer[29] = 0xE9; // jmp dyld_stub_binding_helper
+ buffer[30] = 0x00;
+ buffer[31] = 0x00;
+ buffer[32] = 0x00;
+ buffer[33] = 0x00;
+}
+
+
+template <>
+HybridStubHelperAtom<x86_64>::HybridStubHelperAtom(Writer<x86_64>& writer, ObjectFile::Atom& target,
+ class LazyPointerAtom<x86_64>& lazyPointer, bool forLazyDylib)
+ : StubHelperAtom<x86_64>(writer, target, lazyPointer, forLazyDylib)
+{
+ if ( fgHelperHelperAtom == NULL ) {
+ fgHelperHelperAtom = new HybridStubHelperHelperAtom<x86_64>::HybridStubHelperHelperAtom(fWriter);
+ fWriter.fAllSynthesizedStubHelpers.push_back(fgHelperHelperAtom);
+ }
+ fReferences.push_back(new WriterReference<x86_64>(8, x86_64::kPCRel32, &fLazyPointerAtom));
+ fReferences.push_back(new WriterReference<x86_64>(13, x86_64::kPCRel32, fgHelperHelperAtom));
+}
+
+template <>
+uint64_t HybridStubHelperAtom<x86_64>::getSize() const
+{
+ return 18;
+}
+
+template <>
+void HybridStubHelperAtom<x86_64>::copyRawContent(uint8_t buffer[]) const
+{
+ buffer[0] = 0x68; // pushq $lazy-info-offset
+ buffer[1] = 0x00;
+ buffer[2] = 0x00;
+ buffer[3] = 0x00;
+ buffer[4] = 0x00;
+ buffer[5] = 0x4C; // lea foo$lazy_ptr(%rip),%r11
+ buffer[6] = 0x8D;
+ buffer[7] = 0x1D;
+ buffer[8] = 0x00;
+ buffer[9] = 0x00;
+ buffer[10] = 0x00;
+ buffer[11] = 0x00;
+ buffer[12] = 0xE9; // jmp helper-helper
+ buffer[13] = 0x00;
+ buffer[14] = 0x00;
+ buffer[15] = 0x00;
+ buffer[16] = 0x00;
+ buffer[17] = 0x90; // nop
+
+ // the lazy binding info is created later than this helper atom, so there
+ // is no Reference to update. Instead we blast the offset here.
+ uint32_t offset;
+ LittleEndian::set32(offset, fLazyPointerAtom.getLazyBindingInfoOffset());
+ memcpy(&buffer[1], &offset, 4);
+}
+
+template <>
+FastStubHelperAtom<x86_64>::FastStubHelperAtom(Writer<x86_64>& writer, ObjectFile::Atom& target,
+ class LazyPointerAtom<x86_64>& lazyPointer, bool forLazyDylib)
+ : StubHelperAtom<x86_64>(writer, target, lazyPointer, forLazyDylib)
+{
+ if ( fgHelperHelperAtom == NULL ) {
+ fgHelperHelperAtom = new FastStubHelperHelperAtom<x86_64>::FastStubHelperHelperAtom(fWriter);
+ fWriter.fAllSynthesizedStubHelpers.push_back(fgHelperHelperAtom);
+ }
+ fReferences.push_back(new WriterReference<x86_64>(6, x86_64::kPCRel32, fgHelperHelperAtom));
+}
+
+template <>
+uint64_t FastStubHelperAtom<x86_64>::getSize() const
+{
+ return 10;
+}
+
+template <>
+void FastStubHelperAtom<x86_64>::copyRawContent(uint8_t buffer[]) const
+{
+ buffer[0] = 0x68; // pushq $lazy-info-offset
+ buffer[1] = 0x00;
+ buffer[2] = 0x00;
+ buffer[3] = 0x00;
+ buffer[4] = 0x00;
+ buffer[5] = 0xE9; // jmp helperhelper
+ buffer[6] = 0x00;
+ buffer[7] = 0x00;
+ buffer[8] = 0x00;
+ buffer[9] = 0x00;
+
+ // the lazy binding info is created later than this helper atom, so there
+ // is no Reference to update. Instead we blast the offset here.
+ uint32_t offset;
+ LittleEndian::set32(offset, fLazyPointerAtom.getLazyBindingInfoOffset());
+ memcpy(&buffer[1], &offset, 4);
+}
+
+template <>
+FastStubHelperHelperAtom<x86>::FastStubHelperHelperAtom(Writer<x86>& writer)
+ : WriterAtom<x86>(writer, Segment::fgTextSegment)
+{
+ fReferences.push_back(new WriterReference<x86>(1, x86::kAbsolute32, new NonLazyPointerAtom<x86>(writer)));
+ fReferences.push_back(new WriterReference<x86>(7, x86::kAbsolute32, writer.fFastStubGOTAtom));
+}
+
+template <>
+uint64_t FastStubHelperHelperAtom<x86>::getSize() const
+{
+ return 12;
+}
+
+template <>
+void FastStubHelperHelperAtom<x86>::copyRawContent(uint8_t buffer[]) const
+{
+ buffer[0] = 0x68; // pushl $dyld_ImageLoaderCache
+ buffer[1] = 0x00;
+ buffer[2] = 0x00;
+ buffer[3] = 0x00;
+ buffer[4] = 0x00;
+ buffer[5] = 0xFF; // jmp *_fast_lazy_bind(%rip)
+ buffer[6] = 0x25;
+ buffer[7] = 0x00;
+ buffer[8] = 0x00;
+ buffer[9] = 0x00;
+ buffer[10] = 0x00;
+ buffer[11] = 0x90; // nop
+}
+
+
+template <>
+HybridStubHelperHelperAtom<x86>::HybridStubHelperHelperAtom(Writer<x86>& writer)
+ : WriterAtom<x86>(writer, Segment::fgTextSegment)
+{
+ if ( writer.fDyldClassicHelperAtom == NULL )
+ throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)";
+ fReferences.push_back(new WriterReference<x86>(2, x86::kAbsolute32, writer.fFastStubGOTAtom));
+ fReferences.push_back(new WriterReference<x86>(18, x86::kPCRel32, writer.fDyldClassicHelperAtom));
+ fReferences.push_back(new WriterReference<x86>(26, x86::kAbsolute32, new NonLazyPointerAtom<x86>(writer)));
+ fReferences.push_back(new WriterReference<x86>(32, x86::kAbsolute32, writer.fFastStubGOTAtom));
+}
+
+template <>
+uint64_t HybridStubHelperHelperAtom<x86>::getSize() const
+{
+ return 36;
+}
+
+
+template <>
+void HybridStubHelperHelperAtom<x86>::copyRawContent(uint8_t buffer[]) const
+{
+ buffer[0] = 0x83; // cmpl $0x00,_fast_lazy_bind
+ buffer[1] = 0x3D;
+ buffer[2] = 0x00;
+ buffer[3] = 0x00;
+ buffer[4] = 0x00;
+ buffer[5] = 0x00;
+ buffer[6] = 0x00;
+ buffer[7] = 0x75; // jne 22
+ buffer[8] = 0x0D;
+ buffer[9] = 0x89; // %eax,4(%esp)
+ buffer[10] = 0x44;
+ buffer[11] = 0x24;
+ buffer[12] = 0x04;
+ buffer[13] = 0x58; // popl %eax
+ buffer[14] = 0x87; // xchgl (%esp),%eax
+ buffer[15] = 0x04;
+ buffer[16] = 0x24;
+ buffer[17] = 0xE9; // jmpl dyld_stub_binding_helper
+ buffer[18] = 0x00;
+ buffer[19] = 0x00;
+ buffer[20] = 0x00;
+ buffer[21] = 0x00;
+ buffer[22] = 0x83; // addl $0x04,%esp
+ buffer[23] = 0xC4;
+ buffer[24] = 0x04;
+ buffer[25] = 0x68; // pushl imageloadercahce
+ buffer[26] = 0x00;
+ buffer[27] = 0x00;
+ buffer[28] = 0x00;
+ buffer[29] = 0x00;
+ buffer[30] = 0xFF; // jmp *_fast_lazy_bind(%rip)
+ buffer[31] = 0x25;
+ buffer[32] = 0x00;
+ buffer[33] = 0x00;
+ buffer[34] = 0x00;
+ buffer[35] = 0x00;
+}
+
+
+template <>
+ClassicStubHelperAtom<x86>::ClassicStubHelperAtom(Writer<x86>& writer, ObjectFile::Atom& target,
+ class LazyPointerAtom<x86>& lazyPointer, bool forLazyDylib)
+ : StubHelperAtom<x86>(writer, target, lazyPointer, forLazyDylib)
+{
+ fReferences.push_back(new WriterReference<x86>(1, x86::kAbsolute32, &fLazyPointerAtom));
+ if ( forLazyDylib ) {
+ if ( fWriter.fDyldLazyDylibHelper == NULL )
+ throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)";
+ fReferences.push_back(new WriterReference<x86>(6, x86::kPCRel32, fWriter.fDyldLazyDylibHelper));
+ }
+ else {
+ if ( fWriter.fDyldClassicHelperAtom == NULL )
+ throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)";
+ fReferences.push_back(new WriterReference<x86>(6, x86::kPCRel32, fWriter.fDyldClassicHelperAtom));
+ }
+}
+
+template <>
+uint64_t ClassicStubHelperAtom<x86>::getSize() const
+{
+ return 10;
+}
+
+template <>
+void ClassicStubHelperAtom<x86>::copyRawContent(uint8_t buffer[]) const
+{
+ buffer[0] = 0x68; // pushl $foo$lazy_ptr
+ buffer[1] = 0x00;
+ buffer[2] = 0x00;
+ buffer[3] = 0x00;
+ buffer[4] = 0x00;
+ buffer[5] = 0xE9; // jmp helperhelper
+ buffer[6] = 0x00;
+ buffer[7] = 0x00;
+ buffer[8] = 0x00;
+ buffer[9] = 0x00;
+}
+
+template <>
+HybridStubHelperAtom<x86>::HybridStubHelperAtom(Writer<x86>& writer, ObjectFile::Atom& target,
+ class LazyPointerAtom<x86>& lazyPointer, bool forLazyDylib)
+ : StubHelperAtom<x86>(writer, target, lazyPointer, forLazyDylib)
+{
+ if ( fgHelperHelperAtom == NULL ) {
+ fgHelperHelperAtom = new HybridStubHelperHelperAtom<x86>::HybridStubHelperHelperAtom(fWriter);
+ fWriter.fAllSynthesizedStubHelpers.push_back(fgHelperHelperAtom);
+ }
+ fReferences.push_back(new WriterReference<x86>(6, x86::kAbsolute32, &fLazyPointerAtom));
+ fReferences.push_back(new WriterReference<x86>(11, x86::kPCRel32, fgHelperHelperAtom));
+}
+
+
+template <>
+uint64_t HybridStubHelperAtom<x86>::getSize() const
+{
+ return 16;
+}
+
+template <>
+void HybridStubHelperAtom<x86>::copyRawContent(uint8_t buffer[]) const
+{
+ buffer[0] = 0x68; // pushl $lazy-info-offset
+ buffer[1] = 0x00;
+ buffer[2] = 0x00;
+ buffer[3] = 0x00;
+ buffer[4] = 0x00;
+ buffer[5] = 0x68; // pushl $foo$lazy_ptr
+ buffer[6] = 0x00;
+ buffer[7] = 0x00;
+ buffer[8] = 0x00;
+ buffer[9] = 0x00;
+ buffer[10] = 0xE9; // jmp dyld_hybrid_stub_binding_helper
+ buffer[11] = 0x00;
+ buffer[12] = 0x00;
+ buffer[13] = 0x00;
+ buffer[14] = 0x00;
+ buffer[15] = 0x90; // nop
+
+ // the lazy binding info is created later than this helper atom, so there
+ // is no Reference to update. Instead we blast the offset here.
+ uint32_t offset;
+ LittleEndian::set32(offset, fLazyPointerAtom.getLazyBindingInfoOffset());
+ memcpy(&buffer[1], &offset, 4);
+}
+
+
+template <>
+FastStubHelperAtom<x86>::FastStubHelperAtom(Writer<x86>& writer, ObjectFile::Atom& target,
+ class LazyPointerAtom<x86>& lazyPointer, bool forLazyDylib)
+ : StubHelperAtom<x86>(writer, target, lazyPointer, forLazyDylib)
+{
+ if ( fgHelperHelperAtom == NULL ) {
+ fgHelperHelperAtom = new FastStubHelperHelperAtom<x86>::FastStubHelperHelperAtom(fWriter);
+ fWriter.fAllSynthesizedStubHelpers.push_back(fgHelperHelperAtom);
+ }
+ fReferences.push_back(new WriterReference<x86>(6, x86::kPCRel32, fgHelperHelperAtom));
+}
+
+
+template <>
+uint64_t FastStubHelperAtom<x86>::getSize() const
+{
+ return 10;
+}
+
+template <>
+void FastStubHelperAtom<x86>::copyRawContent(uint8_t buffer[]) const
+{
+ buffer[0] = 0x68; // pushl $lazy-info-offset
+ buffer[1] = 0x00;
+ buffer[2] = 0x00;
+ buffer[3] = 0x00;
+ buffer[4] = 0x00;
+ buffer[5] = 0xE9; // jmp helperhelper
+ buffer[6] = 0x00;
+ buffer[7] = 0x00;
+ buffer[8] = 0x00;
+ buffer[9] = 0x00;
+
+ // the lazy binding info is created later than this helper atom, so there
+ // is no Reference to update. Instead we blast the offset here.
+ uint32_t offset;
+ LittleEndian::set32(offset, fLazyPointerAtom.getLazyBindingInfoOffset());
+ memcpy(&buffer[1], &offset, 4);
+}
+
+
+
+// specialize lazy pointer for x86_64 to initially pointer to stub helper
+template <>
+LazyPointerAtom<x86_64>::LazyPointerAtom(Writer<x86_64>& writer, ObjectFile::Atom& target, StubAtom<x86_64>& stub, bool forLazyDylib)
+ : WriterAtom<x86_64>(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target),
+ fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib), fLazyBindingOffset(0)
+{
+ if ( forLazyDylib )
+ writer.fAllSynthesizedLazyDylibPointers.push_back(this);
+ else
+ writer.fAllSynthesizedLazyPointers.push_back(this);
+
+ ObjectFile::Atom* helper;
+ if ( writer.fOptions.makeCompressedDyldInfo() && !forLazyDylib ) {
+ if ( writer.fOptions.makeClassicDyldInfo() )
+ // hybrid LINKEDIT, no fast bind info for weak symbols so use traditional helper
+ if ( writer.targetRequiresWeakBinding(target) )
+ helper = new ClassicStubHelperAtom<x86_64>(writer, target, *this, forLazyDylib);
+ else
+ helper = new HybridStubHelperAtom<x86_64>(writer, target, *this, forLazyDylib);
+ else {
+ if ( target.getDefinitionKind() == ObjectFile::Atom::kWeakDefinition )
+ helper = ⌖
+ else
+ helper = new FastStubHelperAtom<x86_64>(writer, target, *this, forLazyDylib);
+ }
+ }
+ else {
+ helper = new ClassicStubHelperAtom<x86_64>(writer, target, *this, forLazyDylib);
+ }
+ fReferences.push_back(new WriterReference<x86_64>(0, x86_64::kPointer, helper));
+}
+
+
+// specialize lazy pointer for x86 to initially pointer to stub helper
+template <>
+LazyPointerAtom<x86>::LazyPointerAtom(Writer<x86>& writer, ObjectFile::Atom& target, StubAtom<x86>& stub, bool forLazyDylib)
+ : WriterAtom<x86>(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target),
+ fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib)
+{
+ if ( forLazyDylib )
+ writer.fAllSynthesizedLazyDylibPointers.push_back(this);
+ else
+ writer.fAllSynthesizedLazyPointers.push_back(this);
+
+ ObjectFile::Atom* helper;
+ if ( writer.fOptions.makeCompressedDyldInfo() && !forLazyDylib ) {
+ if ( writer.fOptions.makeClassicDyldInfo() ) {
+ // hybrid LINKEDIT, no fast bind info for weak symbols so use traditional helper
+ if ( writer.targetRequiresWeakBinding(target) )
+ helper = new ClassicStubHelperAtom<x86>(writer, target, *this, forLazyDylib);
+ else
+ helper = new HybridStubHelperAtom<x86>(writer, target, *this, forLazyDylib);
+ }
+ else {
+ if ( target.getDefinitionKind() == ObjectFile::Atom::kWeakDefinition )
+ helper = ⌖
+ else
+ helper = new FastStubHelperAtom<x86>(writer, target, *this, forLazyDylib);
+ }
+ }
+ else {
+ helper = new ClassicStubHelperAtom<x86>(writer, target, *this, forLazyDylib);
+ }
+ fReferences.push_back(new WriterReference<x86>(0, x86::kPointer, helper));
+}
+
+template <typename A>
+LazyPointerAtom<A>::LazyPointerAtom(Writer<A>& writer, ObjectFile::Atom& target, StubAtom<A>& stub, bool forLazyDylib)
+ : WriterAtom<A>(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target),
+ fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib)
+{
+ if ( forLazyDylib )
+ writer.fAllSynthesizedLazyDylibPointers.push_back(this);
+ else
+ writer.fAllSynthesizedLazyPointers.push_back(this);
+
+ fReferences.push_back(new WriterReference<A>(0, A::kPointer, &target));
+}
+
+
+
+template <typename A>
+const char* LazyPointerAtom<A>::lazyPointerName(const char* name)
+{
+ char* buf;
+ asprintf(&buf, "%s$lazy_pointer", name);
+ return buf;
+}
+
+template <typename A>
+void LazyPointerAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ bzero(buffer, getSize());
+}
+
+
+template <typename A>
+NonLazyPointerAtom<A>::NonLazyPointerAtom(Writer<A>& writer, ObjectFile::Atom& target)
+ : WriterAtom<A>(writer, Segment::fgDataSegment), fName(nonlazyPointerName(target.getName())), fTarget(&target)
+{
+ writer.fAllSynthesizedNonLazyPointers.push_back(this);
+ fReferences.push_back(new WriterReference<A>(0, A::kPointer, &target));
+}
+
+template <typename A>
+NonLazyPointerAtom<A>::NonLazyPointerAtom(Writer<A>& writer)
+ : WriterAtom<A>(writer, Segment::fgDataSegment), fName("none"), fTarget(NULL)
+{
+ writer.fAllSynthesizedNonLazyPointers.push_back(this);
+}
+
+template <typename A>
+NonLazyPointerAtom<A>::NonLazyPointerAtom(Writer<A>& writer, const char* targetName)
+ : WriterAtom<A>(writer, Segment::fgDataSegment), fName(nonlazyPointerName(targetName)), fTarget(NULL)
+{
+ writer.fAllSynthesizedNonLazyPointers.push_back(this);
+ fReferences.push_back(new WriterReference<A>(0, A::kPointer, targetName));
+}
+
+template <typename A>
+const char* NonLazyPointerAtom<A>::nonlazyPointerName(const char* name)
+{
+ char* buf;
+ asprintf(&buf, "%s$non_lazy_pointer", name);
+ return buf;
+}
+
+template <typename A>
+void NonLazyPointerAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ bzero(buffer, getSize());
+}
+
+
+
+template <>
+bool StubAtom<ppc64>::pic() const
+{
+ // no-pic stubs for ppc64 don't work if lazy pointer is above low 2GB.
+ // Usually that only happens if page zero is very large
+ return ( fWriter.fSlideable || ((fWriter.fPageZeroAtom != NULL) && (fWriter.fPageZeroAtom->getSize() > 4096)) );
+}
+
+
+template <>
+bool StubAtom<arm>::pic() const
+{
+ return fWriter.fSlideable;
+}
+
+template <>
+ObjectFile::Alignment StubAtom<ppc>::getAlignment() const
+{
+ return 2;
+}
+
+template <>
+ObjectFile::Alignment StubAtom<ppc64>::getAlignment() const
+{
+ return 2;
+}
+
+template <>
+ObjectFile::Alignment StubAtom<arm>::getAlignment() const
+{
+ return 2;
+}
+
+template <>
+StubAtom<ppc>::StubAtom(Writer<ppc>& writer, ObjectFile::Atom& target, bool forLazyDylib)
+ : WriterAtom<ppc>(writer, Segment::fgTextSegment), fName(stubName(target.getName())),
+ fTarget(target), fForLazyDylib(forLazyDylib)
+{
+ writer.fAllSynthesizedStubs.push_back(this);
+ LazyPointerAtom<ppc>* lp;
+ if ( fWriter.fOptions.prebind() ) {
+ // for prebound ppc, lazy pointer starts out pointing to target symbol's address
+ // if target is a weak definition within this linkage unit or zero if in some dylib
+ lp = new LazyPointerAtom<ppc>(writer, target, *this, forLazyDylib);
+ }
+ else {
+ // for non-prebound ppc, lazy pointer starts out pointing to dyld_stub_binding_helper glue code
+ if ( forLazyDylib ) {
+ if ( writer.fDyldLazyDylibHelper == NULL )
+ throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)";
+ lp = new LazyPointerAtom<ppc>(writer, *writer.fDyldLazyDylibHelper, *this, forLazyDylib);
+ }
+ else {
+ if ( writer.fDyldClassicHelperAtom == NULL )
+ throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)";
+ lp = new LazyPointerAtom<ppc>(writer, *writer.fDyldClassicHelperAtom, *this, forLazyDylib);
+ }
+ }
+ if ( pic() ) {
+ // picbase is 8 bytes into atom
+ fReferences.push_back(new WriterReference<ppc>(12, ppc::kPICBaseHigh16, lp, 0, this, 8));
+ fReferences.push_back(new WriterReference<ppc>(20, ppc::kPICBaseLow16, lp, 0, this, 8));
+ }
+ else {
+ fReferences.push_back(new WriterReference<ppc>(0, ppc::kAbsHigh16AddLow, lp));
+ fReferences.push_back(new WriterReference<ppc>(4, ppc::kAbsLow16, lp));
+ }
+}
+
+template <>
+StubAtom<ppc64>::StubAtom(Writer<ppc64>& writer, ObjectFile::Atom& target, bool forLazyDylib)
+ : WriterAtom<ppc64>(writer, Segment::fgTextSegment), fName(stubName(target.getName())),
+ fTarget(target), fForLazyDylib(forLazyDylib)
+{
+ writer.fAllSynthesizedStubs.push_back(this);
+
+ LazyPointerAtom<ppc64>* lp;
+ if ( forLazyDylib ) {
+ if ( writer.fDyldLazyDylibHelper == NULL )
+ throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)";
+ lp = new LazyPointerAtom<ppc64>(writer, *writer.fDyldLazyDylibHelper, *this, forLazyDylib);
+ }
+ else {
+ if ( writer.fDyldClassicHelperAtom == NULL )
+ throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)";
+ lp = new LazyPointerAtom<ppc64>(writer, *writer.fDyldClassicHelperAtom, *this, forLazyDylib);
+ }
+ if ( pic() ) {
+ // picbase is 8 bytes into atom
+ fReferences.push_back(new WriterReference<ppc64>(12, ppc64::kPICBaseHigh16, lp, 0, this, 8));
+ fReferences.push_back(new WriterReference<ppc64>(20, ppc64::kPICBaseLow14, lp, 0, this, 8));
+ }
+ else {
+ fReferences.push_back(new WriterReference<ppc64>(0, ppc64::kAbsHigh16AddLow, lp));
+ fReferences.push_back(new WriterReference<ppc64>(4, ppc64::kAbsLow14, lp));
+ }
+}
+
+template <>
+StubAtom<x86>::StubAtom(Writer<x86>& writer, ObjectFile::Atom& target, bool forLazyDylib)
+ : WriterAtom<x86>(writer, (writer.fOptions.makeCompressedDyldInfo()|| forLazyDylib) ? Segment::fgTextSegment : Segment::fgImportSegment),
+ fName(NULL), fTarget(target), fForLazyDylib(forLazyDylib)
+{
+ if ( writer.fOptions.makeCompressedDyldInfo() || forLazyDylib ) {
+ fName = stubName(target.getName());
+ LazyPointerAtom<x86>* lp = new LazyPointerAtom<x86>(writer, target, *this, forLazyDylib);
+ fReferences.push_back(new WriterReference<x86>(2, x86::kAbsolute32, lp));
+ writer.fAllSynthesizedStubs.push_back(this);
+ }
+ else {
+ if ( &target == NULL )
+ fName = "cache-line-crossing-stub";
+ else {
+ fName = stubName(target.getName());
+ writer.fAllSynthesizedStubs.push_back(this);
+ }
+ }
+}
+
+
+template <>
+StubAtom<x86_64>::StubAtom(Writer<x86_64>& writer, ObjectFile::Atom& target, bool forLazyDylib)
+ : 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, *this, forLazyDylib);
+ fReferences.push_back(new WriterReference<x86_64>(2, x86_64::kPCRel32, lp));
+}
+
+template <>
+StubAtom<arm>::StubAtom(Writer<arm>& writer, ObjectFile::Atom& target, bool forLazyDylib)
+ : WriterAtom<arm>(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target)
+{
+ writer.fAllSynthesizedStubs.push_back(this);
+
+ LazyPointerAtom<arm>* lp;
+ if ( fWriter.fOptions.prebind() && !forLazyDylib ) {
+ // for prebound arm, lazy pointer starts out pointing to target symbol's address
+ // if target is a weak definition within this linkage unit or zero if in some dylib
+ lp = new LazyPointerAtom<arm>(writer, target, *this, forLazyDylib);
+ }
+ else {
+ // for non-prebound arm, lazy pointer starts out pointing to dyld_stub_binding_helper glue code
+ ObjectFile::Atom* helper;
+ if ( forLazyDylib ) {
+ if ( writer.fDyldLazyDylibHelper == NULL )
+ throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)";
+ helper = writer.fDyldLazyDylibHelper;
+ }
+ else {
+ if ( writer.fDyldClassicHelperAtom == NULL )
+ throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)";
+ helper = writer.fDyldClassicHelperAtom;
+ }
+ lp = new LazyPointerAtom<arm>(writer, *helper, *this, forLazyDylib);
+ }
+ if ( pic() )
+ fReferences.push_back(new WriterReference<arm>(12, arm::kPointerDiff, lp, 0, this, 12));
+ else
+ fReferences.push_back(new WriterReference<arm>(8, arm::kPointer, lp));
+}
+
+template <typename A>
+const char* StubAtom<A>::stubName(const char* name)
+{
+ char* buf;
+ asprintf(&buf, "%s$stub", name);
+ return buf;
+}
+
+template <>
+uint64_t StubAtom<ppc>::getSize() const
+{
+ return ( pic() ? 32 : 16 );
+}
+
+template <>
+uint64_t StubAtom<ppc64>::getSize() const
+{
+ return ( pic() ? 32 : 16 );
+}
+
+
+template <>
+uint64_t StubAtom<arm>::getSize() const
+{
+ return ( pic() ? 16 : 12 );
+}
+
+template <>
+uint64_t StubAtom<x86>::getSize() const
+{
+ if ( fWriter.fOptions.makeCompressedDyldInfo() || fForLazyDylib )
+ return 6;
+ else
+ return 5;
+}
+
+template <>
+uint64_t StubAtom<x86_64>::getSize() const
+{
+ return 6;
+}
+
+template <>
+ObjectFile::Alignment StubAtom<x86>::getAlignment() const
+{
+ if ( fWriter.fOptions.makeCompressedDyldInfo() || fForLazyDylib )
+ return 1;
+ else
+ return 0; // special case x86 self-modifying stubs to be byte aligned
+}
+
+template <>
+void StubAtom<ppc64>::copyRawContent(uint8_t buffer[]) const
+{
+ if ( pic() ) {
+ OSWriteBigInt32(&buffer [0], 0, 0x7c0802a6); // mflr r0
+ OSWriteBigInt32(&buffer[ 4], 0, 0x429f0005); // bcl 20,31,Lpicbase
+ OSWriteBigInt32(&buffer[ 8], 0, 0x7d6802a6); // Lpicbase: mflr r11
+ OSWriteBigInt32(&buffer[12], 0, 0x3d6b0000); // addis r11,r11,ha16(L_fwrite$lazy_ptr-Lpicbase)
+ OSWriteBigInt32(&buffer[16], 0, 0x7c0803a6); // mtlr r0
+ OSWriteBigInt32(&buffer[20], 0, 0xe98b0001); // ldu r12,lo16(L_fwrite$lazy_ptr-Lpicbase)(r11)
+ OSWriteBigInt32(&buffer[24], 0, 0x7d8903a6); // mtctr r12
+ OSWriteBigInt32(&buffer[28], 0, 0x4e800420); // bctr
+ }
+ else {
+ OSWriteBigInt32(&buffer[ 0], 0, 0x3d600000); // lis r11,ha16(L_fwrite$lazy_ptr)
+ OSWriteBigInt32(&buffer[ 4], 0, 0xe98b0001); // ldu r12,lo16(L_fwrite$lazy_ptr)(r11)
+ OSWriteBigInt32(&buffer[ 8], 0, 0x7d8903a6); // mtctr r12
+ OSWriteBigInt32(&buffer[12], 0, 0x4e800420); // bctr
+ }
+}
+
+template <>
+void StubAtom<ppc>::copyRawContent(uint8_t buffer[]) const
+{
+ if ( pic() ) {
+ OSWriteBigInt32(&buffer[ 0], 0, 0x7c0802a6); // mflr r0
+ OSWriteBigInt32(&buffer[ 4], 0, 0x429f0005); // bcl 20,31,Lpicbase
+ OSWriteBigInt32(&buffer[ 8], 0, 0x7d6802a6); // Lpicbase: mflr r11
+ OSWriteBigInt32(&buffer[12], 0, 0x3d6b0000); // addis r11,r11,ha16(L_fwrite$lazy_ptr-Lpicbase)
+ OSWriteBigInt32(&buffer[16], 0, 0x7c0803a6); // mtlr r0
+ OSWriteBigInt32(&buffer[20], 0, 0x858b0000); // lwzu r12,lo16(L_fwrite$lazy_ptr-Lpicbase)(r11)
+ OSWriteBigInt32(&buffer[24], 0, 0x7d8903a6); // mtctr r12
+ OSWriteBigInt32(&buffer[28], 0, 0x4e800420); // bctr
+ }
+ else {
+ OSWriteBigInt32(&buffer[ 0], 0, 0x3d600000); // lis r11,ha16(L_fwrite$lazy_ptr)
+ OSWriteBigInt32(&buffer[ 4], 0, 0x858b0000); // lwzu r12,lo16(L_fwrite$lazy_ptr)(r11)
+ OSWriteBigInt32(&buffer[ 8], 0, 0x7d8903a6); // mtctr r12
+ OSWriteBigInt32(&buffer[12], 0, 0x4e800420); // bctr
+ }
+}
+
+template <>
+void StubAtom<x86>::copyRawContent(uint8_t buffer[]) const
+{
+ if ( fWriter.fOptions.makeCompressedDyldInfo() || fForLazyDylib ) {
+ buffer[0] = 0xFF; // jmp *foo$lazy_pointer
+ buffer[1] = 0x25;
+ buffer[2] = 0x00;
+ buffer[3] = 0x00;
+ buffer[4] = 0x00;
+ buffer[5] = 0x00;
+ }
+ else {
+ if ( fWriter.fOptions.prebind() ) {
+ uint32_t address = this->getAddress();
+ int32_t rel32 = 0 - (address+5);
+ buffer[0] = 0xE9;
+ buffer[1] = rel32 & 0xFF;
+ buffer[2] = (rel32 >> 8) & 0xFF;
+ buffer[3] = (rel32 >> 16) & 0xFF;
+ buffer[4] = (rel32 >> 24) & 0xFF;
+ }
+ else {
+ buffer[0] = 0xF4;
+ buffer[1] = 0xF4;
+ buffer[2] = 0xF4;
+ buffer[3] = 0xF4;
+ 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;
+}
+
+template <>
+void StubAtom<arm>::copyRawContent(uint8_t buffer[]) const
+{
+ if ( pic() ) {
+ OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc004); // ldr ip, pc + 12
+ OSWriteLittleInt32(&buffer[ 4], 0, 0xe08fc00c); // add ip, pc, ip
+ OSWriteLittleInt32(&buffer[ 8], 0, 0xe59cf000); // ldr pc, [ip]
+ OSWriteLittleInt32(&buffer[12], 0, 0x00000000); // .long L_foo$lazy_ptr - (L1$scv + 8)
+ }
+ else {
+ OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc000); // ldr ip, [pc, #0]
+ OSWriteLittleInt32(&buffer[ 4], 0, 0xe59cf000); // ldr pc, [ip]
+ OSWriteLittleInt32(&buffer[ 8], 0, 0x00000000); // .long L_foo$lazy_ptr
+ }
+}
+
+// x86_64 stubs are 6 bytes
+template <>
+ObjectFile::Alignment StubAtom<x86_64>::getAlignment() const
+{
+ return 1;
+}
+
+template <>
+const char* StubAtom<ppc>::getSectionName() const
+{
+ return ( pic() ? "__picsymbolstub1" : "__symbol_stub1");
+}
+
+template <>
+const char* StubAtom<ppc64>::getSectionName() const
+{
+ return ( pic() ? "__picsymbolstub1" : "__symbol_stub1");
+}
+
+template <>
+const char* StubAtom<arm>::getSectionName() const
+{
+ return ( pic() ? "__picsymbolstub4" : "__symbol_stub4");
+}
+
+template <>
+const char* StubAtom<x86>::getSectionName() const
+{
+ if ( fWriter.fOptions.makeCompressedDyldInfo() || fForLazyDylib )
+ return "__symbol_stub";
+ else
+ return "__jump_table";
+}
+
+
+
+
+struct AtomByNameSorter
+{
+ bool operator()(ObjectFile::Atom* left, ObjectFile::Atom* right)
+ {
+ return (strcmp(left->getName(), right->getName()) < 0);
+ }
+};
+
+template <typename P>
+struct ExternalRelocSorter
+{
+ bool operator()(const macho_relocation_info<P>& left, const macho_relocation_info<P>& right)
+ {
+ // sort first by symbol number
+ if ( left.r_symbolnum() != right.r_symbolnum() )
+ return (left.r_symbolnum() < right.r_symbolnum());
+ // then sort all uses of the same symbol by address
+ return (left.r_address() < right.r_address());
+ }
+};
+
+
+template <typename A>
+Writer<A>::Writer(const char* path, Options& options, std::vector<ExecutableFile::DyLibUsed>& dynamicLibraries)
+ : ExecutableFile::Writer(dynamicLibraries), fFilePath(strdup(path)), fOptions(options),
+ fAllAtoms(NULL), fStabs(NULL), fRegularDefAtomsThatOverrideADylibsWeakDef(NULL), fLoadCommandsSection(NULL),
+ fLoadCommandsSegment(NULL), fMachHeaderAtom(NULL), fEncryptionLoadCommand(NULL), fSegmentCommands(NULL),
+ fSymbolTableCommands(NULL), fHeaderPadding(NULL), fUnwindInfoAtom(NULL),
+ fUUIDAtom(NULL), fPadSegmentInfo(NULL), fEntryPoint( NULL),
+ fDyldClassicHelperAtom(NULL), fDyldCompressedHelperAtom(NULL), fDyldLazyDylibHelper(NULL),
+ fSectionRelocationsAtom(NULL), fCompressedRebaseInfoAtom(NULL), fCompressedBindingInfoAtom(NULL),
+ fCompressedWeakBindingInfoAtom(NULL), fCompressedLazyBindingInfoAtom(NULL), fCompressedExportInfoAtom(NULL),
+ fLocalRelocationsAtom(NULL), fExternalRelocationsAtom(NULL),
+ fSymbolTableAtom(NULL), fSplitCodeToDataContentAtom(NULL), fIndirectTableAtom(NULL), fModuleInfoAtom(NULL),
+ fStringsAtom(NULL), fPageZeroAtom(NULL), fFastStubGOTAtom(NULL), fSymbolTable(NULL), fSymbolTableCount(0),
+ fSymbolTableStabsCount(0), fSymbolTableLocalCount(0), fSymbolTableExportCount(0), fSymbolTableImportCount(0),
+ fLargestAtomSize(1),
+ fEmitVirtualSections(false), fHasWeakExports(false), fReferencesWeakImports(false),
+ fCanScatter(false), fWritableSegmentPastFirst4GB(false), fNoReExportedDylibs(false),
+ fBiggerThanTwoGigs(false), fSlideable(false),
+ fFirstWritableSegment(NULL), fAnonNameIndex(1000)
+{
+ switch ( fOptions.outputKind() ) {
+ case Options::kDynamicExecutable:
+ case Options::kStaticExecutable:
+ if ( fOptions.zeroPageSize() != 0 )
+ fWriterSynthesizedAtoms.push_back(fPageZeroAtom = new PageZeroAtom<A>(*this));
+ if ( fOptions.outputKind() == Options::kDynamicExecutable )
+ fWriterSynthesizedAtoms.push_back(new DsoHandleAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fMachHeaderAtom = new MachHeaderAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom<A>(*this));
+ if ( fOptions.makeCompressedDyldInfo() )
+ fWriterSynthesizedAtoms.push_back(new DyldInfoLoadCommandsAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom<A>(*this));
+ if ( fOptions.outputKind() == Options::kDynamicExecutable )
+ fWriterSynthesizedAtoms.push_back(new DyldLoadCommandsAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(new ThreadsLoadCommandsAtom<A>(*this));
+ if ( fOptions.hasCustomStack() )
+ fWriterSynthesizedAtoms.push_back(new CustomStackAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom<A>(*this));
+ if ( fOptions.needsUnwindInfoSection() )
+ fWriterSynthesizedAtoms.push_back(fUnwindInfoAtom = new UnwindInfoAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom<A>(*this));
+ if ( fOptions.makeCompressedDyldInfo() ) {
+ fWriterSynthesizedAtoms.push_back(fCompressedRebaseInfoAtom = new CompressedRebaseInfoLinkEditAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fCompressedBindingInfoAtom = new CompressedBindingInfoLinkEditAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fCompressedWeakBindingInfoAtom = new CompressedWeakBindingInfoLinkEditAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fCompressedLazyBindingInfoAtom = new CompressedLazyBindingInfoLinkEditAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fCompressedExportInfoAtom = new CompressedExportInfoLinkEditAtom<A>(*this));
+ }
+ if ( fOptions.makeClassicDyldInfo() )
+ fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom<A>(*this));
+ if ( fOptions.makeClassicDyldInfo() )
+ fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom<A>(*this));
+ break;
+ case Options::kPreload:
+ fWriterSynthesizedAtoms.push_back(fMachHeaderAtom = new MachHeaderAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(new ThreadsLoadCommandsAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom<A>(*this));
+ break;
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ fWriterSynthesizedAtoms.push_back(new DsoHandleAtom<A>(*this));
+ case Options::kKextBundle:
+ fWriterSynthesizedAtoms.push_back(fMachHeaderAtom = new MachHeaderAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom<A>(*this));
+ if ( fOptions.outputKind() == Options::kDynamicLibrary ) {
+ fWriterSynthesizedAtoms.push_back(new DylibIDLoadCommandsAtom<A>(*this));
+ if ( fOptions.initFunctionName() != NULL )
+ fWriterSynthesizedAtoms.push_back(new RoutinesLoadCommandsAtom<A>(*this));
+ }
+ fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom<A>(*this));
+ if ( fOptions.makeCompressedDyldInfo() )
+ fWriterSynthesizedAtoms.push_back(new DyldInfoLoadCommandsAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom<A>(*this));
+ if ( fOptions.sharedRegionEligible() )
+ fWriterSynthesizedAtoms.push_back(new SegmentSplitInfoLoadCommandsAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom<A>(*this));
+ if ( fOptions.needsUnwindInfoSection() )
+ fWriterSynthesizedAtoms.push_back(fUnwindInfoAtom = new UnwindInfoAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom<A>(*this));
+ if ( fOptions.makeCompressedDyldInfo() ) {
+ fWriterSynthesizedAtoms.push_back(fCompressedRebaseInfoAtom = new CompressedRebaseInfoLinkEditAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fCompressedBindingInfoAtom = new CompressedBindingInfoLinkEditAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fCompressedWeakBindingInfoAtom = new CompressedWeakBindingInfoLinkEditAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fCompressedLazyBindingInfoAtom = new CompressedLazyBindingInfoLinkEditAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fCompressedExportInfoAtom = new CompressedExportInfoLinkEditAtom<A>(*this));
+ }
+ if ( fOptions.makeClassicDyldInfo() )
+ fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom<A>(*this));
+ if ( fOptions.sharedRegionEligible() ) {
+ fWriterSynthesizedAtoms.push_back(fSplitCodeToDataContentAtom = new SegmentSplitInfoContentAtom<A>(*this));
+ }
+ fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom<A>(*this));
+ if ( fOptions.makeClassicDyldInfo() )
+ fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom<A>(*this));
+ if ( fOptions.outputKind() != Options::kKextBundle )
+ fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom<A>(*this));
+ if ( this->needsModuleTable() )
+ fWriterSynthesizedAtoms.push_back(fModuleInfoAtom = new ModuleInfoLinkEditAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom<A>(*this));
+ break;
+ case Options::kObjectFile:
+ fWriterSynthesizedAtoms.push_back(fMachHeaderAtom = new MachHeaderAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom<A>(*this));
+ break;
+ case Options::kDyld:
+ fWriterSynthesizedAtoms.push_back(new DsoHandleAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fMachHeaderAtom = new MachHeaderAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(new DyldLoadCommandsAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(new ThreadsLoadCommandsAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom<A>(*this));
+ if ( fOptions.needsUnwindInfoSection() )
+ fWriterSynthesizedAtoms.push_back(fUnwindInfoAtom = new UnwindInfoAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom<A>(*this));
+ fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom<A>(*this));
+ break;
+ }
+
+ // add extra commmands
+ bool hasReExports = false;
+ uint32_t ordinal = 1;
+ switch ( fOptions.outputKind() ) {
+ case Options::kDynamicExecutable:
+ if ( fOptions.makeEncryptable() ) {
+ fEncryptionLoadCommand = new EncryptionLoadCommandsAtom<A>(*this);
+ fWriterSynthesizedAtoms.push_back(fEncryptionLoadCommand);
+ }
+ // fall through
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ {
+ // add dylib load command atoms for all dynamic libraries
+ const unsigned int libCount = dynamicLibraries.size();
+ for (unsigned int i=0; i < libCount; ++i) {
+ ExecutableFile::DyLibUsed& dylibInfo = dynamicLibraries[i];
+ //fprintf(stderr, "dynamicLibraries[%d]: reader=%p, %s, install=%s\n", i, dylibInfo.reader, dylibInfo.reader->getPath(), dylibInfo.reader->getInstallPath() );
+
+ if ( dylibInfo.options.fReExport ) {
+ hasReExports = true;
+ }
+ else {
+ const char* parentUmbrella = dylibInfo.reader->parentUmbrella();
+ if ( (parentUmbrella != NULL) && (fOptions.outputKind() == Options::kDynamicLibrary) ) {
+ const char* thisIDLastSlash = strrchr(fOptions.installPath(), '/');
+ if ( (thisIDLastSlash != NULL) && (strcmp(&thisIDLastSlash[1], parentUmbrella) == 0) )
+ hasReExports = true;
+ }
+ }
+
+ if ( dylibInfo.options.fWeakImport ) {
+ fForcedWeakImportReaders.insert(dylibInfo.reader);
+ }
+
+ if ( dylibInfo.options.fBundleLoader ) {
+ fLibraryToOrdinal[dylibInfo.reader] = EXECUTABLE_ORDINAL;
+ }
+ else {
+ // see if a DylibLoadCommandsAtom has already been created for this install path
+ bool newDylib = true;
+ const char* dylibInstallPath = dylibInfo.reader->getInstallPath();
+ for (unsigned int seenLib=0; seenLib < i; ++seenLib) {
+ ExecutableFile::DyLibUsed& seenDylibInfo = dynamicLibraries[seenLib];
+ if ( !seenDylibInfo.options.fBundleLoader ) {
+ const char* seenDylibInstallPath = seenDylibInfo.reader->getInstallPath();
+ if ( strcmp(seenDylibInstallPath, dylibInstallPath) == 0 ) {
+ fLibraryToOrdinal[dylibInfo.reader] = fLibraryToOrdinal[seenDylibInfo.reader];
+ fLibraryToLoadCommand[dylibInfo.reader] = fLibraryToLoadCommand[seenDylibInfo.reader];
+ fLibraryAliases[dylibInfo.reader] = seenDylibInfo.reader;
+ newDylib = false;
+ break;
+ }
+ }
+ }
+
+ if ( newDylib ) {
+ // assign new ordinal and check for other paired load commands
+ fLibraryToOrdinal[dylibInfo.reader] = ordinal++;
+ DylibLoadCommandsAtom<A>* dyliblc = new DylibLoadCommandsAtom<A>(*this, dylibInfo);
+ fLibraryToLoadCommand[dylibInfo.reader] = dyliblc;
+ fWriterSynthesizedAtoms.push_back(dyliblc);
+ if ( dylibInfo.options.fReExport
+ && (fOptions.macosxVersionMin() < ObjectFile::ReaderOptions::k10_5)
+ && (fOptions.outputKind() == Options::kDynamicLibrary) ) {
+ // see if child has sub-framework that is this
+ bool isSubFramework = false;
+ const char* childInUmbrella = dylibInfo.reader->parentUmbrella();
+ if ( childInUmbrella != NULL ) {
+ const char* myLeaf = strrchr(fOptions.installPath(), '/');
+ if ( myLeaf != NULL ) {
+ if ( strcmp(childInUmbrella, &myLeaf[1]) == 0 )
+ isSubFramework = true;
+ }
+ }
+ // LC_SUB_FRAMEWORK is in child, so do nothing in parent
+ if ( ! isSubFramework ) {
+ // this dylib also needs a sub_x load command
+ bool isFrameworkReExport = false;
+ const char* lastSlash = strrchr(dylibInstallPath, '/');
+ if ( lastSlash != NULL ) {
+ char frameworkName[strlen(lastSlash)+20];
+ sprintf(frameworkName, "/%s.framework/", &lastSlash[1]);
+ isFrameworkReExport = (strstr(dylibInstallPath, frameworkName) != NULL);
+ }
+ if ( isFrameworkReExport ) {
+ // needs a LC_SUB_UMBRELLA command
+ fWriterSynthesizedAtoms.push_back(new SubUmbrellaLoadCommandsAtom<A>(*this, &lastSlash[1]));
+ }
+ else {
+ // needs a LC_SUB_LIBRARY command
+ const char* nameStart = &lastSlash[1];
+ if ( lastSlash == NULL )
+ nameStart = dylibInstallPath;
+ int len = strlen(nameStart);
+ const char* dot = strchr(nameStart, '.');
+ if ( dot != NULL )
+ len = dot - nameStart;
+ fWriterSynthesizedAtoms.push_back(new SubLibraryLoadCommandsAtom<A>(*this, nameStart, len));
+ }
+ }
+ }
+ }
+ }
+ }
+ // add umbrella command if needed
+ if ( fOptions.umbrellaName() != NULL ) {
+ fWriterSynthesizedAtoms.push_back(new UmbrellaLoadCommandsAtom<A>(*this, fOptions.umbrellaName()));
+ }
+ // add allowable client commands if used
+ std::vector<const char*>& allowableClients = fOptions.allowableClients();
+ for (std::vector<const char*>::iterator it=allowableClients.begin(); it != allowableClients.end(); ++it)
+ fWriterSynthesizedAtoms.push_back(new AllowableClientLoadCommandsAtom<A>(*this, *it));
+ }
+ break;
+ case Options::kStaticExecutable:
+ case Options::kObjectFile:
+ case Options::kDyld:
+ case Options::kPreload:
+ case Options::kKextBundle:
+ break;
+ }
+ fNoReExportedDylibs = !hasReExports;
+
+ // add any rpath load commands
+ for(std::vector<const char*>::const_iterator it=fOptions.rpaths().begin(); it != fOptions.rpaths().end(); ++it) {
+ fWriterSynthesizedAtoms.push_back(new RPathLoadCommandsAtom<A>(*this, *it));
+ }
+
+ // set up fSlideable
+ switch ( fOptions.outputKind() ) {
+ case Options::kObjectFile:
+ case Options::kStaticExecutable:
+ fSlideable = false;
+ break;
+ case Options::kDynamicExecutable:
+ fSlideable = fOptions.positionIndependentExecutable();
+ break;
+ case Options::kDyld:
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ case Options::kPreload:
+ case Options::kKextBundle:
+ fSlideable = true;
+ break;
+ }
+
+ //fprintf(stderr, "ordinals table:\n");
+ //for (std::map<class ObjectFile::Reader*, uint32_t>::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) {
+ // fprintf(stderr, "%d <== %s\n", it->second, it->first->getPath());
+ //}
+}
+
+template <typename A>
+Writer<A>::~Writer()
+{
+ if ( fFilePath != NULL )
+ free((void*)fFilePath);
+ if ( fSymbolTable != NULL )
+ delete [] fSymbolTable;
+}
+
+
+// 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)
+{
+ if ( fOptions.outputKind() == Options::kKextBundle ) {
+ return new UndefinedSymbolProxyAtom<A>(*this, name);
+ }
+ else if ( fOptions.outputKind() == Options::kObjectFile ) {
+ // when doing -r -exported_symbols_list, don't create proxy for a symbol
+ // that is supposed to be exported. We want an error instead
+ // <rdar://problem/5062685> ld does not report error when -r is used and exported symbols are not defined.
+ if ( fOptions.hasExportMaskList() && fOptions.shouldExport(name) )
+ return NULL;
+ else
+ return new UndefinedSymbolProxyAtom<A>(*this, name);
+ }
+ else if ( (fOptions.undefinedTreatment() != Options::kUndefinedError) || fOptions.allowedUndefined(name) )
+ return new UndefinedSymbolProxyAtom<A>(*this, name);
+ else
+ return NULL;
+}
+
+template <typename A>
+uint8_t Writer<A>::ordinalForLibrary(ObjectFile::Reader* lib)
+{
+ // flat namespace images use zero for all ordinals
+ if ( fOptions.nameSpace() != Options::kTwoLevelNameSpace )
+ return 0;
+
+ // is an UndefinedSymbolProxyAtom
+ if ( lib == this )
+ if ( fOptions.nameSpace() == Options::kTwoLevelNameSpace )
+ return DYNAMIC_LOOKUP_ORDINAL;
+
+ std::map<class ObjectFile::Reader*, uint32_t>::iterator pos = fLibraryToOrdinal.find(lib);
+ if ( pos != fLibraryToOrdinal.end() )
+ return pos->second;
+
+ throw "can't find ordinal for imported symbol";
+}
+
+template <typename A>
+bool Writer<A>::targetRequiresWeakBinding(const ObjectFile::Atom& target)
+{
+ switch ( target.getDefinitionKind() ) {
+ case ObjectFile::Atom::kExternalWeakDefinition:
+ case ObjectFile::Atom::kWeakDefinition:
+ return true;
+ case ObjectFile::Atom::kExternalDefinition:
+ case ObjectFile::Atom::kAbsoluteSymbol:
+ case ObjectFile::Atom::kRegularDefinition:
+ case ObjectFile::Atom::kTentativeDefinition:
+ break;
+ }
+ return false;
+}
+
+template <typename A>
+int Writer<A>::compressedOrdinalForImortedAtom(ObjectFile::Atom* target)
+{
+ // flat namespace images use zero for all ordinals
+ if ( fOptions.nameSpace() != Options::kTwoLevelNameSpace )
+ return BIND_SPECIAL_DYLIB_FLAT_LOOKUP;
+
+ // is an UndefinedSymbolProxyAtom
+ ObjectFile::Reader* lib = target->getFile();
+ if ( lib == this )
+ if ( fOptions.nameSpace() == Options::kTwoLevelNameSpace )
+ return BIND_SPECIAL_DYLIB_FLAT_LOOKUP;
+
+ std::map<class ObjectFile::Reader*, uint32_t>::iterator pos;
+ switch ( target->getDefinitionKind() ) {
+ case ObjectFile::Atom::kExternalDefinition:
+ case ObjectFile::Atom::kExternalWeakDefinition:
+ pos = fLibraryToOrdinal.find(lib);
+ if ( pos != fLibraryToOrdinal.end() ) {
+ if ( pos->second == EXECUTABLE_ORDINAL )
+ return BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE;
+ else
+ return pos->second;
+ }
+ break;
+ case ObjectFile::Atom::kWeakDefinition:
+ throw "compressedOrdinalForImortedAtom() should not have been called on a weak definition";
+ case ObjectFile::Atom::kAbsoluteSymbol:
+ case ObjectFile::Atom::kRegularDefinition:
+ case ObjectFile::Atom::kTentativeDefinition:
+ return BIND_SPECIAL_DYLIB_SELF;
+ }
+
+ throw "can't find ordinal for imported symbol";
+}
+
+
+template <typename A>
+ObjectFile::Atom& Writer<A>::makeObjcInfoAtom(ObjectFile::Reader::ObjcConstraint objcContraint, bool objcReplacementClasses)
+{
+ return *(new ObjCInfoAtom<A>(*this, objcContraint, objcReplacementClasses));
+}
+
+
+template <typename A>
+uint64_t Writer<A>::write(std::vector<class ObjectFile::Atom*>& atoms,
+ std::vector<class ObjectFile::Reader::Stab>& stabs,
+ class ObjectFile::Atom* entryPointAtom,
+ class ObjectFile::Atom* dyldClassicHelperAtom,
+ class ObjectFile::Atom* dyldCompressedHelperAtom,
+ class ObjectFile::Atom* dyldLazyDylibHelperAtom,
+ bool createUUID, bool canScatter, ObjectFile::Reader::CpuConstraint cpuConstraint,
+ bool biggerThanTwoGigs,
+ std::set<const class ObjectFile::Atom*>& atomsThatOverrideWeak,
+ bool hasExternalWeakDefinitions)
+{
+ fAllAtoms = &atoms;
+ fStabs = &stabs;
+ fEntryPoint = entryPointAtom;
+ fDyldClassicHelperAtom = dyldClassicHelperAtom;
+ fDyldCompressedHelperAtom = dyldCompressedHelperAtom;
+ fDyldLazyDylibHelper = dyldLazyDylibHelperAtom;
+ fCanScatter = canScatter;
+ fCpuConstraint = cpuConstraint;
+ fBiggerThanTwoGigs = biggerThanTwoGigs;
+ fHasWeakExports = hasExternalWeakDefinitions; // dyld needs to search this image as if it had weak exports
+ fRegularDefAtomsThatOverrideADylibsWeakDef = &atomsThatOverrideWeak;
+
+
+ try {
+ // Set for create UUID
+ if (createUUID)
+ fUUIDAtom->generate();
+
+ // remove uneeded dylib load commands
+ optimizeDylibReferences();
+
+ // check for mdynamic-no-pic codegen
+ scanForAbsoluteReferences();
+
+ // create inter-library stubs
+ synthesizeStubs();
+
+ // create table of unwind info
+ synthesizeUnwindInfoTable();
+
+ // create SegmentInfo and SectionInfo objects and assign all atoms to a section
+ partitionIntoSections();
+
+ // segment load command can now be sized and padding can be set
+ adjustLoadCommandsAndPadding();
+
+ // assign each section a file offset
+ assignFileOffsets();
+
+ // if need to add branch islands, reassign file offsets
+ if ( addBranchIslands() )
+ assignFileOffsets();
+
+ // now that addresses are assigned, create unwind info
+ if ( fUnwindInfoAtom != NULL ) {
+ fUnwindInfoAtom->generate();
+ // re-layout
+ adjustLoadCommandsAndPadding();
+ assignFileOffsets();
+ }
+
+ // make spit-seg info now that all atoms exist
+ createSplitSegContent();
+
+ // build symbol table and relocations
+ buildLinkEdit();
+
+ // write map file if requested
+ writeMap();
+
+ // write everything
+ return writeAtoms();
+ } catch (...) {
+ // clean up if any errors
+ (void)unlink(fFilePath);
+ throw;
+ }
+}
+
+template <typename A>
+void Writer<A>::buildLinkEdit()
+{
+ this->collectExportedAndImportedAndLocalAtoms();
+ this->buildSymbolTable();
+ this->buildFixups();
+ this->adjustLinkEditSections();
+}
+
+
+
+template <typename A>
+uint64_t Writer<A>::getAtomLoadAddress(const ObjectFile::Atom* atom)
+{
+ return atom->getAddress();
+// SectionInfo* info = (SectionInfo*)atom->getSection();
+// return info->getBaseAddress() + atom->getSectionOffset();
+}
+
+template <>
+bool Writer<x86_64>::stringsNeedLabelsInObjects()
+{
+ return true;
+}
+
+template <typename A>
+bool Writer<A>::stringsNeedLabelsInObjects()
+{
+ return false;
+}
+
+template <typename A>
+const char* Writer<A>::symbolTableName(const ObjectFile::Atom* atom)
+{
+ static unsigned int counter = 0;
+ const char* name;
+ if ( stringsNeedLabelsInObjects()
+ && (atom->getContentType() == ObjectFile::Atom::kCStringType)
+ && (atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition) )
+ asprintf((char**)&name, "LC%u", counter++);
+ else
+ name = atom->getName();
+ return name;
+ return atom->getName();
+}
+
+template <typename A>
+void Writer<A>::setExportNlist(const ObjectFile::Atom* atom, macho_nlist<P>* entry)
+{
+ // set n_strx
+ entry->set_n_strx(this->fStringsAtom->add(this->symbolTableName(atom)));
+
+ // set n_type
+ 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);
+
+ // the __mh_execute_header is magic and must be an absolute symbol
+ if ( (sectionIndex==0)
+ && (fOptions.outputKind() == Options::kDynamicExecutable)
+ && (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip ))
+ entry->set_n_type(N_EXT | N_ABS);
+
+ // set n_desc
+ uint16_t desc = 0;
+ if ( atom->isThumb() )
+ desc |= N_ARM_THUMB_DEF;
+ if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip )
+ desc |= REFERENCED_DYNAMICALLY;
+ if ( atom->dontDeadStrip() && (fOptions.outputKind() == Options::kObjectFile) )
+ desc |= N_NO_DEAD_STRIP;
+ if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) {
+ desc |= N_WEAK_DEF;
+ fHasWeakExports = true;
+ }
+ entry->set_n_desc(desc);
+
+ // set n_value ( address this symbol will be at if this executable is loaded at it preferred address )
+ if ( atom->getDefinitionKind() == ObjectFile::Atom::kAbsoluteSymbol )
+ entry->set_n_value(atom->getSectionOffset());
+ else
+ entry->set_n_value(this->getAtomLoadAddress(atom));
+}
+
+template <typename A>
+void Writer<A>::setImportNlist(const ObjectFile::Atom* atom, macho_nlist<P>* entry)
+{
+ // set n_strx
+ entry->set_n_strx(this->fStringsAtom->add(atom->getName()));
+
+ // set n_type
+ if ( fOptions.outputKind() == Options::kObjectFile ) {
+ if ( (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit)
+ && (atom->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition) )
+ entry->set_n_type(N_UNDF | N_EXT | N_PEXT);
+ else
+ entry->set_n_type(N_UNDF | N_EXT);
+ }
+ else {
+ if ( fOptions.prebind() )
+ entry->set_n_type(N_PBUD | N_EXT);
+ else
+ entry->set_n_type(N_UNDF | N_EXT);
+ }
+
+ // set n_sect
+ entry->set_n_sect(0);
+
+ uint16_t desc = 0;
+ if ( fOptions.outputKind() != Options::kObjectFile ) {
+ // set n_desc ( high byte is library ordinal, low byte is reference type )
+ std::map<const ObjectFile::Atom*,ObjectFile::Atom*>::iterator pos = fStubsMap.find(atom);
+ if ( pos != fStubsMap.end() || ( strncmp(atom->getName(), ".objc_class_name_", 17) == 0) )
+ desc = REFERENCE_FLAG_UNDEFINED_LAZY;
+ else
+ desc = REFERENCE_FLAG_UNDEFINED_NON_LAZY;
+ try {
+ uint8_t ordinal = this->ordinalForLibrary(atom->getFile());
+ //fprintf(stderr, "ordinal=%u from reader=%p for symbol=%s\n", ordinal, atom->getFile(), atom->getName());
+ SET_LIBRARY_ORDINAL(desc, ordinal);
+ }
+ catch (const char* msg) {
+ throwf("%s %s from %s", msg, atom->getDisplayName(), atom->getFile()->getPath());
+ }
+ }
+ else if ( atom->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition ) {
+ uint8_t align = atom->getAlignment().powerOf2;
+ // always record custom alignment of common symbols to match what compiler does
+ SET_COMM_ALIGN(desc, align);
+ }
+ if ( atom->isThumb() )
+ desc |= N_ARM_THUMB_DEF;
+ if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip )
+ desc |= REFERENCED_DYNAMICALLY;
+ if ( ( fOptions.outputKind() != Options::kObjectFile) && (atom->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) {
+ desc |= N_REF_TO_WEAK;
+ fReferencesWeakImports = true;
+ }
+ // set weak_import attribute
+ if ( fWeakImportMap[atom] )
+ desc |= N_WEAK_REF;
+ entry->set_n_desc(desc);
+
+ // set n_value, zero for import proxy and size for tentative definition
+ entry->set_n_value(atom->getSize());
+}
+
+
+template <typename A>
+void Writer<A>::setLocalNlist(const ObjectFile::Atom* atom, macho_nlist<P>* entry)
+{
+ // set n_strx
+ const char* symbolName = this->symbolTableName(atom);
+ char anonName[32];
+ if ( (fOptions.outputKind() == Options::kObjectFile) && !fOptions.keepLocalSymbol(symbolName) ) {
+ if ( stringsNeedLabelsInObjects() && (atom->getContentType() == ObjectFile::Atom::kCStringType) ) {
+ // don't use 'l' labels for x86_64 strings
+ // <rdar://problem/6605499> x86_64 obj-c runtime confused when static lib is stripped
+ }
+ else {
+ sprintf(anonName, "l%u", fAnonNameIndex++);
+ symbolName = anonName;
+ }
+ }
+ entry->set_n_strx(this->fStringsAtom->add(symbolName));
+
+ // set n_type
+ uint8_t type = N_SECT;
+ if ( atom->getDefinitionKind() == ObjectFile::Atom::kAbsoluteSymbol )
+ type = N_ABS;
+ if ( atom->getScope() == ObjectFile::Atom::scopeLinkageUnit )
+ type |= N_PEXT;
+ entry->set_n_type(type);
+
+ // set n_sect (section number of implementation )
+ uint8_t sectIndex = atom->getSection()->getIndex();
+ if ( sectIndex == 0 ) {
+ // see <mach-o/ldsyms.h> synthesized lable for mach_header needs special section number...
+ if ( strcmp(atom->getSectionName(), "._mach_header") == 0 )
+ sectIndex = 1;
+ }
+ entry->set_n_sect(sectIndex);
+
+ // set n_desc
+ uint16_t desc = 0;
+ if ( atom->dontDeadStrip() && (fOptions.outputKind() == Options::kObjectFile) )
+ desc |= N_NO_DEAD_STRIP;
+ if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition )
+ desc |= N_WEAK_DEF;
+ if ( atom->isThumb() )
+ desc |= N_ARM_THUMB_DEF;
+ entry->set_n_desc(desc);
+
+ // set n_value ( address this symbol will be at if this executable is loaded at it preferred address )
+ if ( atom->getDefinitionKind() == ObjectFile::Atom::kAbsoluteSymbol )
+ entry->set_n_value(atom->getSectionOffset());
+ else
+ entry->set_n_value(this->getAtomLoadAddress(atom));
+}
+
+
+template <typename A>
+void Writer<A>::addLocalLabel(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* name)
+{
+ macho_nlist<P> entry;
+
+ // set n_strx
+ entry.set_n_strx(fStringsAtom->add(name));
+
+ // set n_type
+ entry.set_n_type(N_SECT);
+
+ // set n_sect (section number of implementation )
+ entry.set_n_sect(atom.getSection()->getIndex());
+
+ // set n_desc
+ entry.set_n_desc(0);
+
+ // set n_value ( address this symbol will be at if this executable is loaded at it preferred address )
+ entry.set_n_value(this->getAtomLoadAddress(&atom) + offsetInAtom);
+
+ // add
+ fLocalExtraLabels.push_back(entry);
+}
+
+
+
+template <typename A>
+void Writer<A>::addGlobalLabel(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* name)
+{
+ macho_nlist<P> entry;
+
+ // set n_strx
+ entry.set_n_strx(fStringsAtom->add(name));
+
+ // set n_type
+ entry.set_n_type(N_SECT|N_EXT);
+
+ // set n_sect (section number of implementation )
+ entry.set_n_sect(atom.getSection()->getIndex());
+
+ // set n_desc
+ entry.set_n_desc(0);
+
+ // set n_value ( address this symbol will be at if this executable is loaded at it preferred address )
+ entry.set_n_value(this->getAtomLoadAddress(&atom) + offsetInAtom);
+
+ // add
+ fGlobalExtraLabels.push_back(entry);
+}
+
+template <typename A>
+void Writer<A>::setNlistRange(std::vector<class ObjectFile::Atom*>& atoms, uint32_t startIndex, uint32_t count)
+{
+ macho_nlist<P>* entry = &fSymbolTable[startIndex];
+ for (uint32_t i=0; i < count; ++i, ++entry) {
+ ObjectFile::Atom* atom = atoms[i];
+ if ( &atoms == &fExportedAtoms ) {
+ this->setExportNlist(atom, entry);
+ }
+ else if ( &atoms == &fImportedAtoms ) {
+ this->setImportNlist(atom, entry);
+ }
+ else {
+ this->setLocalNlist(atom, entry);
+ }
+ }
+}
+
+template <typename A>
+void Writer<A>::copyNlistRange(const std::vector<macho_nlist<P> >& entries, uint32_t startIndex)
+{
+ for ( typename std::vector<macho_nlist<P> >::const_iterator it = entries.begin(); it != entries.end(); ++it)
+ fSymbolTable[startIndex++] = *it;
+}
+
+
+template <typename A>
+struct NListNameSorter
+{
+ NListNameSorter(StringsLinkEditAtom<A>* pool) : fStringPool(pool) {}
+
+ bool operator()(const macho_nlist<typename A::P>& left, const macho_nlist<typename A::P>& right)
+ {
+ return (strcmp(fStringPool->stringForIndex(left.n_strx()), fStringPool->stringForIndex(right.n_strx())) < 0);
+ }
+private:
+ StringsLinkEditAtom<A>* fStringPool;
+};
+
+
+template <typename A>
+void Writer<A>::buildSymbolTable()
+{
+ fSymbolTableStabsStartIndex = 0;
+ fSymbolTableStabsCount = fStabs->size();
+ fSymbolTableLocalStartIndex = fSymbolTableStabsStartIndex + fSymbolTableStabsCount;
+ fSymbolTableLocalCount = fLocalSymbolAtoms.size() + fLocalExtraLabels.size();
+ fSymbolTableExportStartIndex = fSymbolTableLocalStartIndex + fSymbolTableLocalCount;
+ fSymbolTableExportCount = fExportedAtoms.size() + fGlobalExtraLabels.size();
+ fSymbolTableImportStartIndex = fSymbolTableExportStartIndex + fSymbolTableExportCount;
+ fSymbolTableImportCount = fImportedAtoms.size();
+
+ // allocate symbol table
+ fSymbolTableCount = fSymbolTableStabsCount + fSymbolTableLocalCount + fSymbolTableExportCount + fSymbolTableImportCount;
+ fSymbolTable = new macho_nlist<P>[fSymbolTableCount];
+
+ // fill in symbol table and string pool (do stabs last so strings are at end of pool)
+ setNlistRange(fLocalSymbolAtoms, fSymbolTableLocalStartIndex, fLocalSymbolAtoms.size());
+ if ( fLocalExtraLabels.size() != 0 )
+ copyNlistRange(fLocalExtraLabels, fSymbolTableLocalStartIndex+fLocalSymbolAtoms.size());
+ setNlistRange(fExportedAtoms, fSymbolTableExportStartIndex, fExportedAtoms.size());
+ if ( fGlobalExtraLabels.size() != 0 ) {
+ copyNlistRange(fGlobalExtraLabels, fSymbolTableExportStartIndex+fExportedAtoms.size());
+ // re-sort combined range
+ std::sort( &fSymbolTable[fSymbolTableExportStartIndex],
+ &fSymbolTable[fSymbolTableExportStartIndex+fSymbolTableExportCount],
+ NListNameSorter<A>(fStringsAtom) );
+ }
+ setNlistRange(fImportedAtoms, fSymbolTableImportStartIndex, fSymbolTableImportCount);
+ addStabs(fSymbolTableStabsStartIndex);
+
+ // set up module table
+ if ( fModuleInfoAtom != NULL )
+ fModuleInfoAtom->setName();
+
+ // create atom to symbol index map
+ // imports
+ int i = 0;
+ for(std::vector<ObjectFile::Atom*>::iterator it=fImportedAtoms.begin(); it != fImportedAtoms.end(); ++it) {
+ fAtomToSymbolIndex[*it] = i + fSymbolTableImportStartIndex;
+ ++i;
+ }
+ // locals
+ i = 0;
+ for(std::vector<ObjectFile::Atom*>::iterator it=fLocalSymbolAtoms.begin(); it != fLocalSymbolAtoms.end(); ++it) {
+ fAtomToSymbolIndex[*it] = i + fSymbolTableLocalStartIndex;
+ ++i;
+ }
+ // exports
+ i = 0;
+ for(std::vector<ObjectFile::Atom*>::iterator it=fExportedAtoms.begin(); it != fExportedAtoms.end(); ++it) {
+ fAtomToSymbolIndex[*it] = i + fSymbolTableExportStartIndex;
+ ++i;
+ }
+
+}
+
+
+
+template <typename A>
+bool Writer<A>::shouldExport(const ObjectFile::Atom& atom) const
+{
+ switch ( atom.getSymbolTableInclusion() ) {
+ case ObjectFile::Atom::kSymbolTableNotIn:
+ return false;
+ case ObjectFile::Atom::kSymbolTableInAndNeverStrip:
+ return true;
+ case ObjectFile::Atom::kSymbolTableInAsAbsolute:
+ case ObjectFile::Atom::kSymbolTableIn:
+ switch ( atom.getScope() ) {
+ case ObjectFile::Atom::scopeGlobal:
+ return true;
+ case ObjectFile::Atom::scopeLinkageUnit:
+ return ( (fOptions.outputKind() == Options::kObjectFile) && fOptions.keepPrivateExterns() );
+ default:
+ return false;
+ }
+ break;
+ }
+ return false;
+}
+
+template <typename A>
+void Writer<A>::collectExportedAndImportedAndLocalAtoms()
+{
+ const int atomCount = fAllAtoms->size();
+ // guess at sizes of each bucket to minimize re-allocations
+ fImportedAtoms.reserve(100);
+ fExportedAtoms.reserve(atomCount/2);
+ fLocalSymbolAtoms.reserve(atomCount);
+ for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) {
+ ObjectFile::Atom* atom = *it;
+ // only named atoms go in symbol table
+ if ( atom->getName() != NULL ) {
+ // put atom into correct bucket: imports, exports, locals
+ //fprintf(stderr, "collectExportedAndImportedAndLocalAtoms() name=%s\n", atom->getDisplayName());
+ switch ( atom->getDefinitionKind() ) {
+ case ObjectFile::Atom::kExternalDefinition:
+ case ObjectFile::Atom::kExternalWeakDefinition:
+ fImportedAtoms.push_back(atom);
+ break;
+ case ObjectFile::Atom::kTentativeDefinition:
+ if ( (fOptions.outputKind() == Options::kObjectFile) && !fOptions.readerOptions().fMakeTentativeDefinitionsReal ) {
+ fImportedAtoms.push_back(atom);
+ break;
+ }
+ // else fall into
+ case ObjectFile::Atom::kWeakDefinition:
+ if ( stringsNeedLabelsInObjects()
+ && (fOptions.outputKind() == Options::kObjectFile)
+ && (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableIn)
+ && (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit)
+ && (atom->getContentType() == ObjectFile::Atom::kCStringType) ) {
+ fLocalSymbolAtoms.push_back(atom);
+ break;
+ }
+ // else fall into
+ case ObjectFile::Atom::kRegularDefinition:
+ case ObjectFile::Atom::kAbsoluteSymbol:
+ if ( this->shouldExport(*atom) )
+ fExportedAtoms.push_back(atom);
+ else if ( (atom->getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn)
+ && ((fOptions.outputKind() == Options::kObjectFile) || fOptions.keepLocalSymbol(atom->getName())) )
+ fLocalSymbolAtoms.push_back(atom);
+ break;
+ }
+ }
+ // when geneating a .o file, dtrace static probes become local labels
+ if ( (fOptions.outputKind() == Options::kObjectFile) && !fOptions.readerOptions().fForStatic ) {
+ std::vector<ObjectFile::Reference*>& references = atom->getReferences();
+ for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) {
+ ObjectFile::Reference* ref = *rit;
+ if ( ref->getKind() == A::kDtraceProbe ) {
+ // dtrace probe points to be add back into generated .o file
+ this->addLocalLabel(*atom, ref->getFixUpOffset(), ref->getTargetName());
+ }
+ }
+ }
+ // when linking kernel, old style dtrace static probes become global labels
+ else if ( fOptions.readerOptions().fForStatic ) {
+ std::vector<ObjectFile::Reference*>& references = atom->getReferences();
+ for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) {
+ ObjectFile::Reference* ref = *rit;
+ if ( ref->getKind() == A::kDtraceProbe ) {
+ // dtrace probe points to be add back into generated .o file
+ this->addGlobalLabel(*atom, ref->getFixUpOffset(), ref->getTargetName());
+ }
+ }
+ }
+ }
+
+ // sort exported atoms by name
+ std::sort(fExportedAtoms.begin(), fExportedAtoms.end(), AtomByNameSorter());
+ // sort imported atoms by name (not required by runtime, but helps make generated files binary diffable)
+ std::sort(fImportedAtoms.begin(), fImportedAtoms.end(), AtomByNameSorter());
+}
+
+
+template <typename A>
+uint64_t Writer<A>::valueForStab(const ObjectFile::Reader::Stab& stab)
+{
+ switch ( stab.type ) {
+ case N_FUN:
+ if ( (stab.string == NULL) || (strlen(stab.string) == 0) ) {
+ // end of function N_FUN has size
+ return stab.atom->getSize();
+ }
+ else {
+ // start of function N_FUN has address
+ return getAtomLoadAddress(stab.atom);
+ }
+ case N_LBRAC:
+ case N_RBRAC:
+ case N_SLINE:
+ if ( stab.atom == NULL )
+ // some weird assembly files have slines not associated with a function
+ return stab.value;
+ else
+ // all these stab types need their value changed from an offset in the atom to an address
+ return getAtomLoadAddress(stab.atom) + stab.value;
+ case N_STSYM:
+ case N_LCSYM:
+ case N_BNSYM:
+ // all these need address of atom
+ return getAtomLoadAddress(stab.atom);;
+ case N_ENSYM:
+ return stab.atom->getSize();
+ case N_SO:
+ 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>
+uint32_t Writer<A>::stringOffsetForStab(const ObjectFile::Reader::Stab& stab)
+{
+ switch (stab.type) {
+ case N_SO:
+ if ( (stab.string == NULL) || stab.string[0] == '\0' ) {
+ return this->fStringsAtom->emptyString();
+ break;
+ }
+ // fall into uniquing case
+ case N_SOL:
+ case N_BINCL:
+ case N_EXCL:
+ return this->fStringsAtom->addUnique(stab.string);
+ break;
+ default:
+ if ( stab.string == NULL )
+ return 0;
+ else if ( stab.string[0] == '\0' )
+ return this->fStringsAtom->emptyString();
+ else
+ return this->fStringsAtom->add(stab.string);
+ }
+ return 0;
+}
+
+template <typename A>
+uint8_t Writer<A>::sectionIndexForStab(const ObjectFile::Reader::Stab& stab)
+{
+ // 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 <typename A>
+void Writer<A>::addStabs(uint32_t startIndex)
+{
+ macho_nlist<P>* entry = &fSymbolTable[startIndex];
+ for(std::vector<ObjectFile::Reader::Stab>::iterator it = fStabs->begin(); it != fStabs->end(); ++it, ++entry) {
+ const ObjectFile::Reader::Stab& stab = *it;
+ entry->set_n_type(stab.type);
+ entry->set_n_sect(sectionIndexForStab(stab));
+ entry->set_n_desc(stab.desc);
+ entry->set_n_value(valueForStab(stab));
+ entry->set_n_strx(stringOffsetForStab(stab));
+ }
+}
+
+
+
+template <typename A>
+uint32_t Writer<A>::symbolIndex(ObjectFile::Atom& atom)
+{
+ std::map<ObjectFile::Atom*, uint32_t>::iterator pos = fAtomToSymbolIndex.find(&atom);
+ if ( pos != fAtomToSymbolIndex.end() )
+ return pos->second;
+ throwf("atom not found in symbolIndex(%s) for %s", atom.getDisplayName(), atom.getFile()->getPath());
+}
+
+
+template <>
+bool Writer<x86_64>::makesExternalRelocatableReference(ObjectFile::Atom& target) const
+{
+ switch ( target.getSymbolTableInclusion() ) {
+ case ObjectFile::Atom::kSymbolTableNotIn:
+ return false;
+ case ObjectFile::Atom::kSymbolTableInAsAbsolute:
+ case ObjectFile::Atom::kSymbolTableIn:
+ case ObjectFile::Atom::kSymbolTableInAndNeverStrip:
+ return true;
+ };
+ return false;
+}
+
+template <typename A>
+bool Writer<A>::makesExternalRelocatableReference(ObjectFile::Atom& target) const
+{
+ switch ( target.getDefinitionKind() ) {
+ case ObjectFile::Atom::kRegularDefinition:
+ case ObjectFile::Atom::kWeakDefinition:
+ case ObjectFile::Atom::kAbsoluteSymbol:
+ return false;
+ case ObjectFile::Atom::kTentativeDefinition:
+ if ( fOptions.readerOptions().fMakeTentativeDefinitionsReal )
+ return false;
+ else
+ return (target.getScope() != ObjectFile::Atom::scopeTranslationUnit);
+ case ObjectFile::Atom::kExternalDefinition:
+ case ObjectFile::Atom::kExternalWeakDefinition:
+ return shouldExport(target);
+ }
+ return false;
+}
+
+template <typename A>
+void Writer<A>::buildFixups()
+{
+ if ( fOptions.outputKind() == Options::kObjectFile ) {
+ this->buildObjectFileFixups();
+ }
+ else {
+ if ( fOptions.keepRelocations() )
+ this->buildObjectFileFixups();
+ this->buildExecutableFixups();
+ }
+}
+
+template <>
+uint32_t Writer<x86_64>::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref)
+{
+ ObjectFile::Atom& target = ref->getTarget();
+ bool external = this->makesExternalRelocatableReference(target);
+ 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::kGOTNoFixUp:
+ case x86_64::kFollowOn:
+ case x86_64::kGroupSubordinate:
+ 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.push_back(reloc1);
+ return 1;
+
+ case x86_64::kPointer32:
+ reloc1.set_r_address(address);
+ reloc1.set_r_symbolnum(symbolIndex);
+ reloc1.set_r_pcrel(false);
+ reloc1.set_r_length(2);
+ reloc1.set_r_extern(external);
+ reloc1.set_r_type(X86_64_RELOC_UNSIGNED);
+ fSectionRelocs.push_back(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.push_back(reloc1);
+ fSectionRelocs.push_back(reloc2);
+ return 2;
+ }
+
+ case x86_64::kBranchPCRel32:
+ case x86_64::kBranchPCRel32WeakImport:
+ case x86_64::kDtraceProbeSite:
+ case x86_64::kDtraceIsEnabledSite:
+ 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.push_back(reloc1);
+ return 1;
+
+ case x86_64::kPCRel32:
+ 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.push_back(reloc1);
+ return 1;
+
+ case x86_64::kPCRel32_1:
+ 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_1);
+ fSectionRelocs.push_back(reloc1);
+ return 1;
+
+ case x86_64::kPCRel32_2:
+ 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_2);
+ fSectionRelocs.push_back(reloc1);
+ return 1;
+
+ 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_4);
+ fSectionRelocs.push_back(reloc1);
+ return 1;
+
+ case x86_64::kBranchPCRel8:
+ reloc1.set_r_address(address);
+ reloc1.set_r_symbolnum(symbolIndex);
+ reloc1.set_r_pcrel(true);
+ reloc1.set_r_length(0);
+ reloc1.set_r_extern(external);
+ reloc1.set_r_type(X86_64_RELOC_BRANCH);
+ fSectionRelocs.push_back(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.push_back(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.push_back(reloc1);
+ return 1;
+
+ case x86_64::kPointerDiff24:
+ throw "internal linker error, kPointerDiff24 can't be encoded into object files";
+
+ case x86_64::kImageOffset32:
+ throw "internal linker error, kImageOffset32 can't be encoded into object files";
+
+ case x86_64::kSectionOffset24:
+ throw "internal linker error, kSectionOffset24 can't be encoded into object files";
+
+ case x86_64::kDtraceTypeReference:
+ case x86_64::kDtraceProbe:
+ // generates no relocs
+ return 0;
+ }
+ return 0;
+}
+
+
+template <>
+uint32_t Writer<x86>::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref)
+{
+ ObjectFile::Atom& target = ref->getTarget();
+ bool isExtern = this->makesExternalRelocatableReference(target);
+ uint32_t symbolIndex = 0;
+ if ( isExtern )
+ symbolIndex = this->symbolIndex(target);
+ uint32_t sectionNum = target.getSection()->getIndex();
+ uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset();
+ macho_relocation_info<P> reloc1;
+ macho_relocation_info<P> reloc2;
+ macho_scattered_relocation_info<P>* sreloc1 = (macho_scattered_relocation_info<P>*)&reloc1;
+ macho_scattered_relocation_info<P>* sreloc2 = (macho_scattered_relocation_info<P>*)&reloc2;
+ x86::ReferenceKinds kind = (x86::ReferenceKinds)ref->getKind();
+
+ if ( !isExtern && (sectionNum == 0) && (target.getDefinitionKind() != ObjectFile::Atom::kAbsoluteSymbol) )
+ warning("section index == 0 for %s (kind=%d, scope=%d, inclusion=%d) in %s",
+ target.getDisplayName(), target.getDefinitionKind(), target.getScope(), target.getSymbolTableInclusion(), target.getFile()->getPath());
+
+
+ switch ( kind ) {
+ case x86::kNoFixUp:
+ case x86::kFollowOn:
+ case x86::kGroupSubordinate:
+ return 0;
+
+ 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);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(GENERIC_RELOC_VANILLA);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(target.getAddress());
+ }
+ else {
+ reloc1.set_r_address(address);
+ reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum);
+ reloc1.set_r_pcrel(false);
+ reloc1.set_r_length(2);
+ reloc1.set_r_extern(isExtern);
+ reloc1.set_r_type(GENERIC_RELOC_VANILLA);
+ }
+ fSectionRelocs.push_back(reloc1);
+ return 1;
+
+ case x86::kPointerDiff16:
+ case x86::kPointerDiff:
+ {
+ //pint_t fromAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset();
+ //fprintf(stderr, "addObjectRelocs(): refFromTarget=%s, refTarget=%s, refFromTargetAddr=0x%llX, refFromTargetOffset=0x%llX\n",
+ // ref->getFromTarget().getDisplayName(), ref->getTarget().getDisplayName(),
+ // ref->getFromTarget().getAddress(), ref->getFromTargetOffset());
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length( (kind==x86::kPointerDiff) ? 2 : 1 );
+ if ( ref->getTarget().getScope() == ObjectFile::Atom::scopeTranslationUnit )
+ sreloc1->set_r_type(GENERIC_RELOC_LOCAL_SECTDIFF);
+ else
+ sreloc1->set_r_type(GENERIC_RELOC_SECTDIFF);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(target.getAddress());
+
+ sreloc2->set_r_scattered(true);
+ sreloc2->set_r_pcrel(false);
+ sreloc2->set_r_length( (kind==x86::kPointerDiff) ? 2 : 1 );
+ sreloc2->set_r_type(GENERIC_RELOC_PAIR);
+ sreloc2->set_r_address(0);
+ if ( &ref->getFromTarget() == atom )
+ sreloc2->set_r_value(ref->getFromTarget().getAddress()+ref->getFromTargetOffset());
+ else
+ sreloc2->set_r_value(ref->getFromTarget().getAddress());
+ fSectionRelocs.push_back(reloc2);
+ fSectionRelocs.push_back(reloc1);
+ return 2;
+ }
+
+ case x86::kPCRel32WeakImport:
+ case x86::kPCRel32:
+ case x86::kPCRel16:
+ case x86::kPCRel8:
+ case x86::kDtraceProbeSite:
+ case x86::kDtraceIsEnabledSite:
+ if ( !isExtern && (ref->getTargetOffset() != 0) ) {
+ // use scattered reloc is target offset is non-zero
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(true);
+ sreloc1->set_r_length( (kind==x86::kPCRel8) ? 0 : ((kind==x86::kPCRel16) ? 1 : 2) );
+ sreloc1->set_r_type(GENERIC_RELOC_VANILLA);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(target.getAddress());
+ }
+ else {
+ reloc1.set_r_address(address);
+ reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum);
+ reloc1.set_r_pcrel(true);
+ reloc1.set_r_length( (kind==x86::kPCRel8) ? 0 : ((kind==x86::kPCRel16) ? 1 : 2) );
+ reloc1.set_r_extern(isExtern);
+ reloc1.set_r_type(GENERIC_RELOC_VANILLA);
+ }
+ fSectionRelocs.push_back(reloc1);
+ return 1;
+
+ case x86::kPointerDiff24:
+ throw "internal linker error, kPointerDiff24 can't be encoded into object files";
+
+ case x86::kImageOffset32:
+ throw "internal linker error, kImageOffset32 can't be encoded into object files";
+
+ case x86::kSectionOffset24:
+ throw "internal linker error, kSectionOffset24 can't be encoded into object files";
+
+ case x86::kDtraceTypeReference:
+ case x86::kDtraceProbe:
+ // generates no relocs
+ return 0;
+
+ }
+ return 0;
+}
+
+template <>
+uint32_t Writer<arm>::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref)
+{
+ ObjectFile::Atom& target = ref->getTarget();
+ bool isExtern = this->makesExternalRelocatableReference(target);
+ uint32_t symbolIndex = 0;
+ if ( isExtern )
+ symbolIndex = this->symbolIndex(target);
+ uint32_t sectionNum = target.getSection()->getIndex();
+ uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset();
+ macho_relocation_info<P> reloc1;
+ macho_relocation_info<P> reloc2;
+ macho_scattered_relocation_info<P>* sreloc1 = (macho_scattered_relocation_info<P>*)&reloc1;
+ macho_scattered_relocation_info<P>* sreloc2 = (macho_scattered_relocation_info<P>*)&reloc2;
+ arm::ReferenceKinds kind = (arm::ReferenceKinds)ref->getKind();
+
+ if ( !isExtern && (sectionNum == 0) && (target.getDefinitionKind() != ObjectFile::Atom::kAbsoluteSymbol) )
+ warning("section index == 0 for %s (kind=%d, scope=%d, inclusion=%d) in %s",
+ target.getDisplayName(), target.getDefinitionKind(), target.getScope(), target.getSymbolTableInclusion(), target.getFile()->getPath());
+
+
+ switch ( kind ) {
+ case arm::kNoFixUp:
+ case arm::kFollowOn:
+ case arm::kGroupSubordinate:
+ return 0;
+
+ case arm::kPointer:
+ case arm::kReadOnlyPointer:
+ case arm::kPointerWeakImport:
+ if ( !isExtern && (ref->getTargetOffset() != 0) ) {
+ // use scattered reloc is target offset is non-zero
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(ARM_RELOC_VANILLA);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(target.getAddress());
+ }
+ else {
+ reloc1.set_r_address(address);
+ reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum);
+ reloc1.set_r_pcrel(false);
+ reloc1.set_r_length(2);
+ reloc1.set_r_extern(isExtern);
+ reloc1.set_r_type(ARM_RELOC_VANILLA);
+ }
+ fSectionRelocs.push_back(reloc1);
+ return 1;
+
+ case arm::kPointerDiff:
+ {
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(2);
+ if ( ref->getTarget().getScope() == ObjectFile::Atom::scopeTranslationUnit )
+ sreloc1->set_r_type(ARM_RELOC_LOCAL_SECTDIFF);
+ else
+ sreloc1->set_r_type(ARM_RELOC_SECTDIFF);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(target.getAddress());
+ sreloc2->set_r_scattered(true);
+ sreloc2->set_r_pcrel(false);
+ sreloc2->set_r_length(2);
+ sreloc2->set_r_type(ARM_RELOC_PAIR);
+ sreloc2->set_r_address(0);
+ if ( &ref->getFromTarget() == atom )
+ sreloc2->set_r_value(ref->getFromTarget().getAddress()+ref->getFromTargetOffset());
+ else
+ sreloc2->set_r_value(ref->getFromTarget().getAddress());
+ fSectionRelocs.push_back(reloc2);
+ fSectionRelocs.push_back(reloc1);
+ return 2;
+ }
+
+ case arm::kBranch24WeakImport:
+ case arm::kBranch24:
+ case arm::kDtraceProbeSite:
+ case arm::kDtraceIsEnabledSite:
+ if ( !isExtern && (ref->getTargetOffset() != 0) ) {
+ // use scattered reloc is target offset is non-zero
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(true);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(ARM_RELOC_BR24);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(target.getAddress());
+ }
+ else {
+ reloc1.set_r_address(address);
+ reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum);
+ reloc1.set_r_pcrel(true);
+ reloc1.set_r_length(2);
+ reloc1.set_r_extern(isExtern);
+ reloc1.set_r_type(ARM_RELOC_BR24);
+ }
+ fSectionRelocs.push_back(reloc1);
+ return 1;
+
+ case arm::kThumbBranch22WeakImport:
+ case arm::kThumbBranch22:
+ if ( !isExtern && (ref->getTargetOffset() != 0) ) {
+ // use scattered reloc if target offset is non-zero
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(true);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(ARM_THUMB_RELOC_BR22);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(target.getAddress());
+ }
+ else {
+ reloc1.set_r_address(address);
+ reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum);
+ reloc1.set_r_pcrel(true);
+ reloc1.set_r_length(2);
+ reloc1.set_r_extern(isExtern);
+ reloc1.set_r_type(ARM_THUMB_RELOC_BR22);
+ }
+ fSectionRelocs.push_back(reloc1);
+ return 1;
+
+ case arm::kDtraceTypeReference:
+ case arm::kDtraceProbe:
+ // generates no relocs
+ return 0;
+
+ }
+ return 0;
+}
+
+template <> uint64_t Writer<ppc>::maxAddress() { return 0xFFFFFFFFULL; }
+template <> uint64_t Writer<ppc64>::maxAddress() { return 0xFFFFFFFFFFFFFFFFULL; }
+template <> uint64_t Writer<x86>::maxAddress() { return 0xFFFFFFFFULL; }
+template <> uint64_t Writer<x86_64>::maxAddress() { return 0xFFFFFFFFFFFFFFFFULL; }
+template <> uint64_t Writer<arm>::maxAddress() { return 0xFFFFFFFFULL; }
+
+template <>
+uint8_t Writer<ppc>::getRelocPointerSize()
+{
+ return 2;
+}
+
+template <>
+uint8_t Writer<ppc64>::getRelocPointerSize()
+{
+ return 3;
+}
+
+template <>
+uint32_t Writer<ppc>::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref)
+{
+ return addObjectRelocs_powerpc(atom, ref);
+}
+
+template <>
+uint32_t Writer<ppc64>::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref)
+{
+ return addObjectRelocs_powerpc(atom, ref);
+}
+
+//
+// addObjectRelocs<ppc> and addObjectRelocs<ppc64> are almost exactly the same, so
+// they use a common addObjectRelocs_powerpc() method.
+//
+template <typename A>
+uint32_t Writer<A>::addObjectRelocs_powerpc(ObjectFile::Atom* atom, ObjectFile::Reference* ref)
+{
+ ObjectFile::Atom& target = ref->getTarget();
+ bool isExtern = this->makesExternalRelocatableReference(target);
+ uint32_t symbolIndex = 0;
+ if ( isExtern )
+ symbolIndex = this->symbolIndex(target);
+ uint32_t sectionNum = target.getSection()->getIndex();
+ uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset();
+ macho_relocation_info<P> reloc1;
+ macho_relocation_info<P> reloc2;
+ macho_scattered_relocation_info<P>* sreloc1 = (macho_scattered_relocation_info<P>*)&reloc1;
+ macho_scattered_relocation_info<P>* sreloc2 = (macho_scattered_relocation_info<P>*)&reloc2;
+ typename A::ReferenceKinds kind = (typename A::ReferenceKinds)ref->getKind();
+
+ switch ( kind ) {
+ case A::kNoFixUp:
+ case A::kFollowOn:
+ case A::kGroupSubordinate:
+ return 0;
+
+ case A::kPointer:
+ case A::kPointerWeakImport:
+ if ( !isExtern && (ref->getTargetOffset() >= target.getSize()) ) {
+ // use scattered reloc is target offset is outside target
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(getRelocPointerSize());
+ sreloc1->set_r_type(GENERIC_RELOC_VANILLA);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(target.getAddress());
+ }
+ else {
+ reloc1.set_r_address(address);
+ if ( isExtern )
+ reloc1.set_r_symbolnum(symbolIndex);
+ else
+ reloc1.set_r_symbolnum(sectionNum);
+ reloc1.set_r_pcrel(false);
+ reloc1.set_r_length(getRelocPointerSize());
+ reloc1.set_r_extern(isExtern);
+ reloc1.set_r_type(GENERIC_RELOC_VANILLA);
+ }
+ fSectionRelocs.push_back(reloc1);
+ return 1;
+
+ case A::kPointerDiff16:
+ case A::kPointerDiff32:
+ case A::kPointerDiff64:
+ {
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length( (kind == A::kPointerDiff32) ? 2 : ((kind == A::kPointerDiff64) ? 3 : 1));
+ if ( ref->getTarget().getScope() == ObjectFile::Atom::scopeTranslationUnit )
+ sreloc1->set_r_type(PPC_RELOC_LOCAL_SECTDIFF);
+ else
+ sreloc1->set_r_type(PPC_RELOC_SECTDIFF);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(target.getAddress());
+ sreloc2->set_r_scattered(true);
+ sreloc2->set_r_pcrel(false);
+ sreloc2->set_r_length(sreloc1->r_length());
+ sreloc2->set_r_type(PPC_RELOC_PAIR);
+ sreloc2->set_r_address(0);
+ sreloc2->set_r_value(ref->getFromTarget().getAddress()+ref->getFromTargetOffset());
+ fSectionRelocs.push_back(reloc2);
+ fSectionRelocs.push_back(reloc1);
+ return 2;
+ }
+
+ case A::kBranch24WeakImport:
+ case A::kBranch24:
+ case A::kDtraceProbeSite:
+ case A::kDtraceIsEnabledSite:
+ if ( (ref->getTargetOffset() == 0) || isExtern ) {
+ reloc1.set_r_address(address);
+ if ( isExtern )
+ reloc1.set_r_symbolnum(symbolIndex);
+ else
+ reloc1.set_r_symbolnum(sectionNum);
+ reloc1.set_r_pcrel(true);
+ reloc1.set_r_length(2);
+ reloc1.set_r_type(PPC_RELOC_BR24);
+ reloc1.set_r_extern(isExtern);
+ }
+ else {
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(true);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(PPC_RELOC_BR24);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(target.getAddress());
+ }
+ fSectionRelocs.push_back(reloc1);
+ return 1;
+
+ case A::kBranch14:
+ if ( (ref->getTargetOffset() == 0) || isExtern ) {
+ reloc1.set_r_address(address);
+ if ( isExtern )
+ reloc1.set_r_symbolnum(symbolIndex);
+ else
+ reloc1.set_r_symbolnum(sectionNum);
+ reloc1.set_r_pcrel(true);
+ reloc1.set_r_length(2);
+ reloc1.set_r_type(PPC_RELOC_BR14);
+ reloc1.set_r_extern(isExtern);
+ }
+ else {
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(true);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(PPC_RELOC_BR14);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(target.getAddress());
+ }
+ fSectionRelocs.push_back(reloc1);
+ return 1;
+
+ case A::kPICBaseLow16:
+ case A::kPICBaseLow14:
+ {
+ pint_t fromAddr = atom->getAddress() + ref->getFromTargetOffset();
+ pint_t toAddr = target.getAddress() + ref->getTargetOffset();
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(kind == A::kPICBaseLow16 ? PPC_RELOC_LO16_SECTDIFF : PPC_RELOC_LO14_SECTDIFF);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(target.getAddress());
+ sreloc2->set_r_scattered(true);
+ sreloc2->set_r_pcrel(false);
+ sreloc2->set_r_length(2);
+ sreloc2->set_r_type(PPC_RELOC_PAIR);
+ sreloc2->set_r_address(((toAddr-fromAddr) >> 16) & 0xFFFF);
+ sreloc2->set_r_value(fromAddr);
+ fSectionRelocs.push_back(reloc2);
+ fSectionRelocs.push_back(reloc1);
+ return 2;
+ }
+
+ case A::kPICBaseHigh16:
+ {
+ pint_t fromAddr = atom->getAddress() + ref->getFromTargetOffset();
+ pint_t toAddr = target.getAddress() + ref->getTargetOffset();
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(PPC_RELOC_HA16_SECTDIFF);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(target.getAddress());
+ sreloc2->set_r_scattered(true);
+ sreloc2->set_r_pcrel(false);
+ sreloc2->set_r_length(2);
+ sreloc2->set_r_type(PPC_RELOC_PAIR);
+ sreloc2->set_r_address((toAddr-fromAddr) & 0xFFFF);
+ sreloc2->set_r_value(fromAddr);
+ fSectionRelocs.push_back(reloc2);
+ fSectionRelocs.push_back(reloc1);
+ return 2;
+ }
+
+ case A::kAbsLow14:
+ case A::kAbsLow16:
+ {
+ pint_t toAddr = target.getAddress() + ref->getTargetOffset();
+ if ( (ref->getTargetOffset() == 0) || isExtern ) {
+ reloc1.set_r_address(address);
+ if ( isExtern )
+ reloc1.set_r_symbolnum(symbolIndex);
+ else
+ reloc1.set_r_symbolnum(sectionNum);
+ reloc1.set_r_pcrel(false);
+ reloc1.set_r_length(2);
+ reloc1.set_r_extern(isExtern);
+ reloc1.set_r_type(kind==A::kAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14);
+ }
+ else {
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(kind==A::kAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(target.getAddress());
+ }
+ if ( isExtern )
+ reloc2.set_r_address(ref->getTargetOffset() >> 16);
+ else
+ reloc2.set_r_address(toAddr >> 16);
+ reloc2.set_r_symbolnum(0);
+ reloc2.set_r_pcrel(false);
+ reloc2.set_r_length(2);
+ reloc2.set_r_extern(false);
+ reloc2.set_r_type(PPC_RELOC_PAIR);
+ fSectionRelocs.push_back(reloc2);
+ fSectionRelocs.push_back(reloc1);
+ return 2;
+ }
+
+ case A::kAbsHigh16:
+ {
+ pint_t toAddr = target.getAddress() + ref->getTargetOffset();
+ if ( (ref->getTargetOffset() == 0) || isExtern ) {
+ reloc1.set_r_address(address);
+ if ( isExtern )
+ reloc1.set_r_symbolnum(symbolIndex);
+ else
+ reloc1.set_r_symbolnum(sectionNum);
+ reloc1.set_r_pcrel(false);
+ reloc1.set_r_length(2);
+ reloc1.set_r_extern(isExtern);
+ reloc1.set_r_type(PPC_RELOC_HI16);
+ }
+ else {
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(PPC_RELOC_HI16);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(target.getAddress());
+ }
+ if ( isExtern )
+ reloc2.set_r_address(ref->getTargetOffset() & 0xFFFF);
+ else
+ reloc2.set_r_address(toAddr & 0xFFFF);
+ reloc2.set_r_symbolnum(0);
+ reloc2.set_r_pcrel(false);
+ reloc2.set_r_length(2);
+ reloc2.set_r_extern(false);
+ reloc2.set_r_type(PPC_RELOC_PAIR);
+ fSectionRelocs.push_back(reloc2);
+ fSectionRelocs.push_back(reloc1);
+ return 2;
+ }
+
+ case A::kAbsHigh16AddLow:
+ {
+ pint_t toAddr = target.getAddress() + ref->getTargetOffset();
+ uint32_t overflow = 0;
+ if ( (toAddr & 0x00008000) != 0 )
+ overflow = 0x10000;
+ if ( (ref->getTargetOffset() == 0) || isExtern ) {
+ reloc1.set_r_address(address);
+ if ( isExtern )
+ reloc1.set_r_symbolnum(symbolIndex);
+ else
+ reloc1.set_r_symbolnum(sectionNum);
+ reloc1.set_r_pcrel(false);
+ reloc1.set_r_length(2);
+ reloc1.set_r_extern(isExtern);
+ reloc1.set_r_type(PPC_RELOC_HA16);
+ }
+ else {
+ sreloc1->set_r_scattered(true);
+ sreloc1->set_r_pcrel(false);
+ sreloc1->set_r_length(2);
+ sreloc1->set_r_type(PPC_RELOC_HA16);
+ sreloc1->set_r_address(address);
+ sreloc1->set_r_value(target.getAddress());
+ }
+ if ( isExtern )
+ reloc2.set_r_address(ref->getTargetOffset() & 0xFFFF);
+ else
+ reloc2.set_r_address(toAddr & 0xFFFF);
+ reloc2.set_r_symbolnum(0);
+ reloc2.set_r_pcrel(false);
+ reloc2.set_r_length(2);
+ reloc2.set_r_extern(false);
+ reloc2.set_r_type(PPC_RELOC_PAIR);
+ fSectionRelocs.push_back(reloc2);
+ fSectionRelocs.push_back(reloc1);
+ return 2;
+ }
+
+ case A::kDtraceTypeReference:
+ case A::kDtraceProbe:
+ // generates no relocs
+ return 0;
+ }
+ return 0;
+}
+
+
+
+//
+// There are cases when an entry in the indirect symbol table is the magic value
+// INDIRECT_SYMBOL_LOCAL instead of being a symbol index. When that happens
+// the content of the corresponding part of the __nl_symbol_pointer section
+// must also change.
+//
+template <typename A>
+bool Writer<A>::indirectSymbolInRelocatableIsLocal(const ObjectFile::Reference* ref) const
+{
+ // cannot use INDIRECT_SYMBOL_LOCAL to tentative definitions in object files
+ // because tentative defs don't have addresses
+ if ( ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition )
+ return false;
+
+ // must use INDIRECT_SYMBOL_LOCAL if there is an addend
+ if ( ref->getTargetOffset() != 0 )
+ return true;
+
+ // don't use INDIRECT_SYMBOL_LOCAL for external symbols
+ return ! this->shouldExport(ref->getTarget());
+}
+
+
+template <typename A>
+void Writer<A>::buildObjectFileFixups()
+{
+ uint32_t relocIndex = 0;
+ std::vector<SegmentInfo*>& segmentInfos = fSegmentInfos;
+ const int segCount = segmentInfos.size();
+ for(int i=0; i < segCount; ++i) {
+ SegmentInfo* curSegment = segmentInfos[i];
+ std::vector<SectionInfo*>& sectionInfos = curSegment->fSections;
+ const int sectionCount = sectionInfos.size();
+ for(int j=0; j < sectionCount; ++j) {
+ SectionInfo* curSection = sectionInfos[j];
+ //fprintf(stderr, "buildObjectFileFixups(): starting section %s\n", curSection->fSectionName);
+ std::vector<ObjectFile::Atom*>& sectionAtoms = curSection->fAtoms;
+ if ( ! curSection->fAllZeroFill ) {
+ if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers
+ || curSection->fAllLazyDylibPointers || curSection->fAllStubs )
+ curSection->fIndirectSymbolOffset = fIndirectTableAtom->fTable.size();
+ curSection->fRelocOffset = relocIndex;
+ const int atomCount = sectionAtoms.size();
+ for (int k=0; k < atomCount; ++k) {
+ ObjectFile::Atom* atom = sectionAtoms[k];
+ //fprintf(stderr, "buildObjectFileFixups(): atom %s has %lu references\n", atom->getDisplayName(), atom->getReferences().size());
+ std::vector<ObjectFile::Reference*>& refs = atom->getReferences();
+ const int refCount = refs.size();
+ for (int l=0; l < refCount; ++l) {
+ ObjectFile::Reference* ref = refs[l];
+ if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers
+ || curSection->fAllLazyDylibPointers || curSection->fAllStubs ) {
+ uint32_t offsetInSection = atom->getSectionOffset();
+ uint32_t indexInSection = offsetInSection / atom->getSize();
+ uint32_t undefinedSymbolIndex;
+ if ( curSection->fAllStubs ) {
+ ObjectFile::Atom& stubTarget =ref->getTarget();
+ ObjectFile::Atom& stubTargetTarget = stubTarget.getReferences()[0]->getTarget();
+ undefinedSymbolIndex = this->symbolIndex(stubTargetTarget);
+ //fprintf(stderr, "stub %s ==> %s ==> %s ==> index:%u\n", atom->getDisplayName(), stubTarget.getDisplayName(), stubTargetTarget.getDisplayName(), undefinedSymbolIndex);
+ }
+ else if ( curSection->fAllNonLazyPointers) {
+ // only use INDIRECT_SYMBOL_LOCAL in non-lazy-pointers for atoms that won't be in symbol table or have an addend
+ if ( this->indirectSymbolInRelocatableIsLocal(ref) )
+ undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL;
+ else
+ undefinedSymbolIndex = this->symbolIndex(ref->getTarget());
+ }
+ else {
+ // should never get here, fAllLazyPointers not used in generated .o files
+ undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL;
+ }
+ uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset;
+ IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex };
+ //printf("fIndirectTableAtom->fTable.add(sectionIndex=%u, indirectTableIndex=%u => %u), size=%lld\n", indexInSection, indirectTableIndex, undefinedSymbolIndex, atom->getSize());
+ fIndirectTableAtom->fTable.push_back(entry);
+ if ( curSection->fAllLazyPointers ) {
+ ObjectFile::Atom& target = ref->getTarget();
+ ObjectFile::Atom& fromTarget = ref->getFromTarget();
+ if ( &fromTarget == NULL ) {
+ warning("lazy pointer %s missing initial binding", atom->getDisplayName());
+ }
+ else {
+ bool isExtern = ( ((target.getDefinitionKind() == ObjectFile::Atom::kExternalDefinition)
+ || (target.getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition))
+ && (target.getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn) );
+ macho_relocation_info<P> reloc1;
+ reloc1.set_r_address(atom->getSectionOffset());
+ reloc1.set_r_symbolnum(isExtern ? this->symbolIndex(target) : target.getSection()->getIndex());
+ reloc1.set_r_pcrel(false);
+ reloc1.set_r_length();
+ reloc1.set_r_extern(isExtern);
+ reloc1.set_r_type(GENERIC_RELOC_VANILLA);
+ fSectionRelocs.push_back(reloc1);
+ ++relocIndex;
+ }
+ }
+ else if ( curSection->fAllStubs ) {
+ relocIndex += this->addObjectRelocs(atom, ref);
+ }
+ }
+ else if ( (ref->getKind() != A::kNoFixUp) && (ref->getTargetBinding() != ObjectFile::Reference::kDontBind) ) {
+ relocIndex += this->addObjectRelocs(atom, ref);
+ }
+ }
+ }
+ curSection->fRelocCount = relocIndex - curSection->fRelocOffset;
+ }
+ }
+ }
+
+ // reverse the relocs
+ std::reverse(fSectionRelocs.begin(), fSectionRelocs.end());
+
+ // now reverse section reloc offsets
+ for(int i=0; i < segCount; ++i) {
+ SegmentInfo* curSegment = segmentInfos[i];
+ std::vector<SectionInfo*>& sectionInfos = curSegment->fSections;
+ const int sectionCount = sectionInfos.size();
+ for(int j=0; j < sectionCount; ++j) {
+ SectionInfo* curSection = sectionInfos[j];
+ curSection->fRelocOffset = relocIndex - curSection->fRelocOffset - curSection->fRelocCount;
+ }
+ }
+
+}
+
+
+template <>
+uint64_t Writer<x86_64>::relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const
+{
+ uint64_t result;
+ if ( fOptions.outputKind() == Options::kKextBundle ) {
+ // for x86_64 kext bundles, the r_address field in relocs
+ // is the offset from the start address of the first segment
+ result = address - fSegmentInfos[0]->fBaseAddress;
+ if ( result > 0xFFFFFFFF ) {
+ throwf("kext bundle too large: address can't fit in 31-bit r_address field in %s from %s",
+ atom->getDisplayName(), atom->getFile()->getPath());
+ }
+ }
+ else {
+ // 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
+ result = address - fFirstWritableSegment->fBaseAddress;
+ if ( result > 0xFFFFFFFF ) {
+ if ( strcmp(atom->getSegment().getName(), "__TEXT") == 0 )
+ throwf("text relocs not supported for x86_64 in %s from %s",
+ atom->getDisplayName(), atom->getFile()->getPath());
+ else
+ 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 <>
+bool Writer<ppc>::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref)
+{
+ switch ( ref.getKind() ) {
+ case ppc::kAbsLow16:
+ case ppc::kAbsLow14:
+ case ppc::kAbsHigh16:
+ case ppc::kAbsHigh16AddLow:
+ if ( fSlideable )
+ return true;
+ }
+ return false;
+}
+
+
+template <>
+bool Writer<ppc64>::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref)
+{
+ switch ( ref.getKind() ) {
+ case ppc::kAbsLow16:
+ case ppc::kAbsLow14:
+ case ppc::kAbsHigh16:
+ case ppc::kAbsHigh16AddLow:
+ if ( fSlideable )
+ return true;
+ }
+ return false;
+}
+
+template <>
+bool Writer<x86>::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref)
+{
+ if ( ref.getKind() == x86::kAbsolute32 ) {
+ switch ( ref.getTarget().getDefinitionKind() ) {
+ case ObjectFile::Atom::kTentativeDefinition:
+ case ObjectFile::Atom::kRegularDefinition:
+ case ObjectFile::Atom::kWeakDefinition:
+ // illegal in dylibs/bundles, until we support TEXT relocs
+ return fSlideable;
+ case ObjectFile::Atom::kExternalDefinition:
+ case ObjectFile::Atom::kExternalWeakDefinition:
+ // illegal until we support TEXT relocs
+ return true;
+ case ObjectFile::Atom::kAbsoluteSymbol:
+ // absolute symbbols only allowed in static executables
+ return ( fOptions.outputKind() != Options::kStaticExecutable);
+ }
+ }
+ return false;
+}
+
+template <>
+bool Writer<x86_64>::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref)
+{
+ if ( fOptions.outputKind() == Options::kKextBundle ) {
+ switch ( ref.getTarget().getDefinitionKind() ) {
+ case ObjectFile::Atom::kTentativeDefinition:
+ case ObjectFile::Atom::kRegularDefinition:
+ case ObjectFile::Atom::kWeakDefinition:
+ case ObjectFile::Atom::kAbsoluteSymbol:
+ return false;
+ case ObjectFile::Atom::kExternalDefinition:
+ case ObjectFile::Atom::kExternalWeakDefinition:
+ // true means we need a TEXT relocs
+ switch ( ref.getKind() ) {
+ case x86_64::kBranchPCRel32:
+ case x86_64::kBranchPCRel32WeakImport:
+ case x86_64::kPCRel32GOTLoad:
+ case x86_64::kPCRel32GOTLoadWeakImport:
+ case x86_64::kPCRel32GOT:
+ case x86_64::kPCRel32GOTWeakImport:
+ return true;
+ }
+ break;
+ }
+ }
+ return false;
+}
+
+template <>
+bool Writer<arm>::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref)
+{
+ if ( ref.getKind() == arm::kReadOnlyPointer ) {
+ switch ( ref.getTarget().getDefinitionKind() ) {
+ case ObjectFile::Atom::kTentativeDefinition:
+ case ObjectFile::Atom::kRegularDefinition:
+ case ObjectFile::Atom::kWeakDefinition:
+ // illegal in dylibs/bundles, until we support TEXT relocs
+ return fSlideable;
+ case ObjectFile::Atom::kExternalDefinition:
+ case ObjectFile::Atom::kExternalWeakDefinition:
+ // illegal until we support TEXT relocs
+ return true;
+ case ObjectFile::Atom::kAbsoluteSymbol:
+ // absolute symbbols only allowed in static executables
+ return ( fOptions.outputKind() != Options::kStaticExecutable);
+ }
+ }
+ return false;
+}
+
+template <>
+bool Writer<x86>::generatesLocalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection)
+{
+ if ( ref.getKind() == x86::kAbsolute32 ) {
+ switch ( ref.getTarget().getDefinitionKind() ) {
+ case ObjectFile::Atom::kTentativeDefinition:
+ case ObjectFile::Atom::kRegularDefinition:
+ case ObjectFile::Atom::kWeakDefinition:
+ // a reference to the absolute address of something in this same linkage unit can be
+ // encoded as a local text reloc in a dylib or bundle
+ if ( fSlideable ) {
+ macho_relocation_info<P> reloc;
+ SectionInfo* sectInfo = (SectionInfo*)(ref.getTarget().getSection());
+ reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom));
+ reloc.set_r_symbolnum(sectInfo->getIndex());
+ reloc.set_r_pcrel(false);
+ reloc.set_r_length();
+ reloc.set_r_extern(false);
+ reloc.set_r_type(GENERIC_RELOC_VANILLA);
+ fInternalRelocs.push_back(reloc);
+ atomSection->fHasTextLocalRelocs = true;
+ if ( fOptions.makeCompressedDyldInfo() ) {
+ fRebaseInfo.push_back(RebaseInfo(REBASE_TYPE_TEXT_ABSOLUTE32, atom.getAddress() + ref.getFixUpOffset()));
+ }
+ return true;
+ }
+ return false;
+ case ObjectFile::Atom::kExternalDefinition:
+ case ObjectFile::Atom::kExternalWeakDefinition:
+ case ObjectFile::Atom::kAbsoluteSymbol:
+ return false;
+ }
+ }
+ return false;
+}
+
+template <>
+bool Writer<ppc>::generatesLocalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection)
+{
+ macho_relocation_info<P> reloc1;
+ macho_relocation_info<P> reloc2;
+ switch ( ref.getTarget().getDefinitionKind() ) {
+ case ObjectFile::Atom::kTentativeDefinition:
+ case ObjectFile::Atom::kRegularDefinition:
+ case ObjectFile::Atom::kWeakDefinition:
+ switch ( ref.getKind() ) {
+ case ppc::kAbsLow16:
+ case ppc::kAbsLow14:
+ // a reference to the absolute address of something in this same linkage unit can be
+ // encoded as a local text reloc in a dylib or bundle
+ if ( fSlideable ) {
+ SectionInfo* sectInfo = (SectionInfo*)(ref.getTarget().getSection());
+ uint32_t targetAddr = ref.getTarget().getAddress() + ref.getTargetOffset();
+ reloc1.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom));
+ reloc1.set_r_symbolnum(sectInfo->getIndex());
+ reloc1.set_r_pcrel(false);
+ reloc1.set_r_length(2);
+ reloc1.set_r_extern(false);
+ reloc1.set_r_type(ref.getKind()==ppc::kAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14);
+ reloc2.set_r_address(targetAddr >> 16);
+ reloc2.set_r_symbolnum(0);
+ reloc2.set_r_pcrel(false);
+ reloc2.set_r_length(2);
+ reloc2.set_r_extern(false);
+ reloc2.set_r_type(PPC_RELOC_PAIR);
+ fInternalRelocs.push_back(reloc1);
+ fInternalRelocs.push_back(reloc2);
+ atomSection->fHasTextLocalRelocs = true;
+ return true;
+ }
+ break;
+ case ppc::kAbsHigh16:
+ case ppc::kAbsHigh16AddLow:
+ if ( fSlideable ) {
+ SectionInfo* sectInfo = (SectionInfo*)(ref.getTarget().getSection());
+ uint32_t targetAddr = ref.getTarget().getAddress() + ref.getTargetOffset();
+ reloc1.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom));
+ reloc1.set_r_symbolnum(sectInfo->getIndex());
+ reloc1.set_r_pcrel(false);
+ reloc1.set_r_length(2);
+ reloc1.set_r_extern(false);
+ reloc1.set_r_type(ref.getKind()==ppc::kAbsHigh16AddLow ? PPC_RELOC_HA16 : PPC_RELOC_HI16);
+ reloc2.set_r_address(targetAddr & 0xFFFF);
+ reloc2.set_r_symbolnum(0);
+ reloc2.set_r_pcrel(false);
+ reloc2.set_r_length(2);
+ reloc2.set_r_extern(false);
+ reloc2.set_r_type(PPC_RELOC_PAIR);
+ fInternalRelocs.push_back(reloc1);
+ fInternalRelocs.push_back(reloc2);
+ atomSection->fHasTextLocalRelocs = true;
+ return true;
+ }
+ }
+ break;
+ case ObjectFile::Atom::kExternalDefinition:
+ case ObjectFile::Atom::kExternalWeakDefinition:
+ case ObjectFile::Atom::kAbsoluteSymbol:
+ return false;
+ }
+ return false;
+}
+
+template <>
+bool Writer<arm>::generatesLocalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection)
+{
+ if ( ref.getKind() == arm::kReadOnlyPointer ) {
+ switch ( ref.getTarget().getDefinitionKind() ) {
+ case ObjectFile::Atom::kTentativeDefinition:
+ case ObjectFile::Atom::kRegularDefinition:
+ case ObjectFile::Atom::kWeakDefinition:
+ // a reference to the absolute address of something in this same linkage unit can be
+ // encoded as a local text reloc in a dylib or bundle
+ if ( fSlideable ) {
+ macho_relocation_info<P> reloc;
+ SectionInfo* sectInfo = (SectionInfo*)(ref.getTarget().getSection());
+ reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom));
+ reloc.set_r_symbolnum(sectInfo->getIndex());
+ reloc.set_r_pcrel(false);
+ reloc.set_r_length();
+ reloc.set_r_extern(false);
+ reloc.set_r_type(GENERIC_RELOC_VANILLA);
+ fInternalRelocs.push_back(reloc);
+ atomSection->fHasTextLocalRelocs = true;
+ return true;
+ }
+ return false;
+ case ObjectFile::Atom::kExternalDefinition:
+ case ObjectFile::Atom::kExternalWeakDefinition:
+ case ObjectFile::Atom::kAbsoluteSymbol:
+ return false;
+ }
+ }
+ return false;
+}
+
+
+template <>
+bool Writer<x86_64>::generatesLocalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection)
+{
+ // text relocs not supported (usually never needed because of RIP addressing)
+ return false;
+}
+
+template <>
+bool Writer<ppc64>::generatesLocalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection)
+{
+ // text relocs not supported
+ return false;
+}
+
+template <>
+bool Writer<x86>::generatesExternalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection)
+{
+ if ( ref.getKind() == x86::kAbsolute32 ) {
+ macho_relocation_info<P> reloc;
+ switch ( ref.getTarget().getDefinitionKind() ) {
+ case ObjectFile::Atom::kTentativeDefinition:
+ case ObjectFile::Atom::kRegularDefinition:
+ case ObjectFile::Atom::kWeakDefinition:
+ return false;
+ case ObjectFile::Atom::kExternalDefinition:
+ case ObjectFile::Atom::kExternalWeakDefinition:
+ // a reference to the absolute address of something in another linkage unit can be
+ // encoded as an external text reloc in a dylib or bundle
+ reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom));
+ reloc.set_r_symbolnum(this->symbolIndex(ref.getTarget()));
+ reloc.set_r_pcrel(false);
+ reloc.set_r_length();
+ reloc.set_r_extern(true);
+ reloc.set_r_type(GENERIC_RELOC_VANILLA);
+ fExternalRelocs.push_back(reloc);
+ atomSection->fHasTextExternalRelocs = true;
+ return true;
+ case ObjectFile::Atom::kAbsoluteSymbol:
+ return false;
+ }
+ }
+ return false;
+}
+
+template <>
+bool Writer<x86_64>::generatesExternalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection)
+{
+ if ( fOptions.outputKind() == Options::kKextBundle ) {
+ macho_relocation_info<P> reloc;
+ switch ( ref.getTarget().getDefinitionKind() ) {
+ case ObjectFile::Atom::kTentativeDefinition:
+ case ObjectFile::Atom::kRegularDefinition:
+ case ObjectFile::Atom::kWeakDefinition:
+ case ObjectFile::Atom::kAbsoluteSymbol:
+ return false;
+ case ObjectFile::Atom::kExternalDefinition:
+ case ObjectFile::Atom::kExternalWeakDefinition:
+ switch ( ref.getKind() ) {
+ case x86_64::kBranchPCRel32:
+ case x86_64::kBranchPCRel32WeakImport:
+ // a branch to something in another linkage unit is
+ // encoded as an external text reloc in a kext bundle
+ reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom));
+ reloc.set_r_symbolnum(this->symbolIndex(ref.getTarget()));
+ reloc.set_r_pcrel(true);
+ reloc.set_r_length(2);
+ reloc.set_r_extern(true);
+ reloc.set_r_type(X86_64_RELOC_BRANCH);
+ fExternalRelocs.push_back(reloc);
+ atomSection->fHasTextExternalRelocs = true;
+ return true;
+ case x86_64::kPCRel32GOTLoad:
+ case x86_64::kPCRel32GOTLoadWeakImport:
+ // a load of the GOT entry for a symbol in another linkage unit is
+ // encoded as an external text reloc in a kext bundle
+ reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom));
+ reloc.set_r_symbolnum(this->symbolIndex(ref.getTarget()));
+ reloc.set_r_pcrel(true);
+ reloc.set_r_length(2);
+ reloc.set_r_extern(true);
+ reloc.set_r_type(X86_64_RELOC_GOT_LOAD);
+ fExternalRelocs.push_back(reloc);
+ atomSection->fHasTextExternalRelocs = true;
+ return true;
+ case x86_64::kPCRel32GOT:
+ case x86_64::kPCRel32GOTWeakImport:
+ // a use of the GOT entry for a symbol in another linkage unit is
+ // encoded as an external text reloc in a kext bundle
+ reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom));
+ reloc.set_r_symbolnum(this->symbolIndex(ref.getTarget()));
+ reloc.set_r_pcrel(true);
+ reloc.set_r_length(2);
+ reloc.set_r_extern(true);
+ reloc.set_r_type(X86_64_RELOC_GOT);
+ fExternalRelocs.push_back(reloc);
+ atomSection->fHasTextExternalRelocs = true;
+ return true;
+ }
+ break;
+ }
+ }
+ return false;
+}
+
+
+template <typename A>
+bool Writer<A>::generatesExternalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection)
+{
+ return false;
+}
+
+
+
+
+template <typename A>
+typename Writer<A>::RelocKind Writer<A>::relocationNeededInFinalLinkedImage(const ObjectFile::Atom& target) const
+{
+ switch ( target.getDefinitionKind() ) {
+ case ObjectFile::Atom::kTentativeDefinition:
+ case ObjectFile::Atom::kRegularDefinition:
+ // in main executables, the only way regular symbols are indirected is if -interposable is used
+ if ( fOptions.outputKind() == Options::kDynamicExecutable ) {
+ if ( this->shouldExport(target) && fOptions.interposable(target.getName()) )
+ return kRelocExternal;
+ else if ( fSlideable )
+ return kRelocInternal;
+ else
+ return kRelocNone;
+ }
+ // for flat-namespace or interposable two-level-namespace
+ // all references to exported symbols get indirected
+ else if ( this->shouldExport(target) &&
+ ((fOptions.nameSpace() == Options::kFlatNameSpace)
+ || (fOptions.nameSpace() == Options::kForceFlatNameSpace)
+ || fOptions.interposable(target.getName()))
+ && (target.getName() != NULL)
+ && (strncmp(target.getName(), ".objc_class_", 12) != 0) ) // <rdar://problem/5254468>
+ return kRelocExternal;
+ else if ( fSlideable )
+ return kRelocInternal;
+ else
+ return kRelocNone;
+ case ObjectFile::Atom::kWeakDefinition:
+ // all calls to global weak definitions get indirected
+ if ( this->shouldExport(target) )
+ return kRelocExternal;
+ else if ( fSlideable )
+ return kRelocInternal;
+ else
+ return kRelocNone;
+ case ObjectFile::Atom::kExternalDefinition:
+ case ObjectFile::Atom::kExternalWeakDefinition:
+ return kRelocExternal;
+ case ObjectFile::Atom::kAbsoluteSymbol:
+ return kRelocNone;
+ }
+ 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 first segment
+ uint64_t result = address - fSegmentInfos[0]->fBaseAddress;
+ if ( fOptions.outputKind() == Options::kPreload ) {
+ // kPreload uses a virtual __HEADER segment to cover the load commands
+ result = address - fSegmentInfos[1]->fBaseAddress;
+ }
+ // or the offset from the first writable segment if built split-seg
+ if ( fOptions.splitSeg() )
+ result = address - fFirstWritableSegment->fBaseAddress;
+ 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<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() < ObjectFile::ReaderOptions::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 - fSegmentInfos[0]->fBaseAddress;
+ if ( (fOptions.macosxVersionMin() < ObjectFile::ReaderOptions::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 <> bool Writer<ppc>::preboundLazyPointerType(uint8_t* type) { *type = PPC_RELOC_PB_LA_PTR; return true; }
+template <> bool Writer<ppc64>::preboundLazyPointerType(uint8_t* type) { throw "prebinding not supported"; }
+template <> bool Writer<x86>::preboundLazyPointerType(uint8_t* type) { *type = GENERIC_RELOC_PB_LA_PTR; return true; }
+template <> bool Writer<x86_64>::preboundLazyPointerType(uint8_t* type) { throw "prebinding not supported"; }
+template <> bool Writer<arm>::preboundLazyPointerType(uint8_t* type) { *type = ARM_RELOC_PB_LA_PTR; return true; }
+
+template <typename A>
+void Writer<A>::buildExecutableFixups()
+{
+ if ( fIndirectTableAtom != NULL )
+ fIndirectTableAtom->fTable.reserve(50); // minimize reallocations
+ std::vector<SegmentInfo*>& segmentInfos = fSegmentInfos;
+ const int segCount = segmentInfos.size();
+ for(int i=0; i < segCount; ++i) {
+ SegmentInfo* curSegment = segmentInfos[i];
+ std::vector<SectionInfo*>& sectionInfos = curSegment->fSections;
+ const int sectionCount = sectionInfos.size();
+ for(int j=0; j < sectionCount; ++j) {
+ SectionInfo* curSection = sectionInfos[j];
+ //fprintf(stderr, "starting section %s\n", curSection->fSectionName);
+ std::vector<ObjectFile::Atom*>& sectionAtoms = curSection->fAtoms;
+ if ( ! curSection->fAllZeroFill ) {
+ if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers
+ || curSection->fAllStubs || curSection->fAllSelfModifyingStubs ) {
+ if ( fIndirectTableAtom != NULL )
+ curSection->fIndirectSymbolOffset = fIndirectTableAtom->fTable.size();
+ }
+ const int atomCount = sectionAtoms.size();
+ for (int k=0; k < atomCount; ++k) {
+ ObjectFile::Atom* atom = sectionAtoms[k];
+ std::vector<ObjectFile::Reference*>& refs = atom->getReferences();
+ const int refCount = refs.size();
+ //fprintf(stderr, "atom %s has %d references in section %s, %p\n", atom->getDisplayName(), refCount, curSection->fSectionName, atom->getSection());
+ if ( curSection->fAllNonLazyPointers && (refCount == 0) ) {
+ // handle imageloadercache GOT slot
+ uint32_t offsetInSection = atom->getSectionOffset();
+ uint32_t indexInSection = offsetInSection / sizeof(pint_t);
+ uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset;
+ // use INDIRECT_SYMBOL_ABS so 10.5 dyld will leave value as zero
+ IndirectEntry entry = { indirectTableIndex, INDIRECT_SYMBOL_ABS };
+ //fprintf(stderr,"fIndirectTableAtom->fTable.push_back(tableIndex=%d, symIndex=0x%X, section=%s)\n",
+ // indirectTableIndex, INDIRECT_SYMBOL_LOCAL, curSection->fSectionName);
+ fIndirectTableAtom->fTable.push_back(entry);
+ }
+ for (int l=0; l < refCount; ++l) {
+ ObjectFile::Reference* ref = refs[l];
+ if ( (fOptions.outputKind() != Options::kKextBundle) &&
+ (curSection->fAllNonLazyPointers || curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers) ) {
+ // if atom is in (non)lazy_pointer section, this is encoded as an indirect symbol
+ if ( atom->getSize() != sizeof(pint_t) ) {
+ warning("wrong size pointer atom %s from file %s", atom->getDisplayName(), atom->getFile()->getPath());
+ }
+ ObjectFile::Atom* pointerTarget = &(ref->getTarget());
+ if ( curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers ) {
+ pointerTarget = ((LazyPointerAtom<A>*)atom)->getTarget();
+ }
+ uint32_t offsetInSection = atom->getSectionOffset();
+ uint32_t indexInSection = offsetInSection / sizeof(pint_t);
+ uint32_t undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL;
+ if (atom == fFastStubGOTAtom)
+ undefinedSymbolIndex = INDIRECT_SYMBOL_ABS;
+ else if ( this->relocationNeededInFinalLinkedImage(*pointerTarget) == kRelocExternal )
+ undefinedSymbolIndex = this->symbolIndex(*pointerTarget);
+ uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset;
+ IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex };
+ //fprintf(stderr,"fIndirectTableAtom->fTable.push_back(tableIndex=%d, symIndex=0x%X, section=%s)\n",
+ // indirectTableIndex, undefinedSymbolIndex, curSection->fSectionName);
+ fIndirectTableAtom->fTable.push_back(entry);
+ if ( curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers ) {
+ uint8_t preboundLazyType;
+ if ( fOptions.prebind() && (fDyldClassicHelperAtom != NULL)
+ && curSection->fAllLazyPointers && preboundLazyPointerType(&preboundLazyType) ) {
+ // this is a prebound image, need special relocs for dyld to reset lazy pointers if prebinding is invalid
+ macho_scattered_relocation_info<P> pblaReloc;
+ pblaReloc.set_r_scattered(true);
+ pblaReloc.set_r_pcrel(false);
+ pblaReloc.set_r_length();
+ pblaReloc.set_r_type(preboundLazyType);
+ pblaReloc.set_r_address(relocAddressInFinalLinkedImage(atom->getAddress(), atom));
+ pblaReloc.set_r_value(fDyldClassicHelperAtom->getAddress());
+ fInternalRelocs.push_back(*((macho_relocation_info<P>*)&pblaReloc));
+ }
+ else if ( fSlideable ) {
+ // this is a non-prebound dylib/bundle, need vanilla internal relocation to fix up binding handler if image slides
+ macho_relocation_info<P> dyldHelperReloc;
+ uint32_t sectionNum = 1;
+ if ( fDyldClassicHelperAtom != NULL )
+ sectionNum = ((SectionInfo*)(fDyldClassicHelperAtom->getSection()))->getIndex();
+ //fprintf(stderr, "lazy pointer reloc, section index=%u, section name=%s\n", sectionNum, curSection->fSectionName);
+ dyldHelperReloc.set_r_address(relocAddressInFinalLinkedImage(atom->getAddress(), atom));
+ dyldHelperReloc.set_r_symbolnum(sectionNum);
+ dyldHelperReloc.set_r_pcrel(false);
+ dyldHelperReloc.set_r_length();
+ dyldHelperReloc.set_r_extern(false);
+ dyldHelperReloc.set_r_type(GENERIC_RELOC_VANILLA);
+ fInternalRelocs.push_back(dyldHelperReloc);
+ if ( fOptions.makeCompressedDyldInfo() ) {
+ fRebaseInfo.push_back(RebaseInfo(REBASE_TYPE_POINTER,atom->getAddress()));
+ }
+ }
+ if ( fOptions.makeCompressedDyldInfo() ) {
+ uint8_t type = BIND_TYPE_POINTER;
+ uint64_t addresss = atom->getAddress() + ref->getFixUpOffset();
+ if ( pointerTarget->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition ) {
+ // This is a referece to a weak def in some dylib (e.g. operator new)
+ // need to bind into to directly bind this
+ // later weak binding info may override
+ int ordinal = compressedOrdinalForImortedAtom(pointerTarget);
+ fBindingInfo.push_back(BindingInfo(type, ordinal, pointerTarget->getName(), false, addresss, 0));
+ }
+ if ( targetRequiresWeakBinding(*pointerTarget) ) {
+ // note: lazy pointers to weak symbols are not bound lazily
+ fWeakBindingInfo.push_back(BindingInfo(type, pointerTarget->getName(), false, addresss, 0));
+ }
+ }
+ }
+ if ( curSection->fAllNonLazyPointers && fOptions.makeCompressedDyldInfo() ) {
+ if ( pointerTarget != NULL ) {
+ switch ( this->relocationNeededInFinalLinkedImage(*pointerTarget) ) {
+ case kRelocNone:
+ // no rebase or binding info needed
+ break;
+ case kRelocInternal:
+ // a non-lazy pointer that has been optimized to LOCAL needs rebasing info
+ // but not the magic fFastStubGOTAtom atom
+ if (atom != fFastStubGOTAtom)
+ fRebaseInfo.push_back(RebaseInfo(REBASE_TYPE_POINTER,atom->getAddress()));
+ break;
+ case kRelocExternal:
+ {
+ uint8_t type = BIND_TYPE_POINTER;
+ uint64_t addresss = atom->getAddress();
+ if ( targetRequiresWeakBinding(ref->getTarget()) ) {
+ fWeakBindingInfo.push_back(BindingInfo(type, ref->getTarget().getName(), false, addresss, 0));
+ // if this is a non-lazy pointer to a weak definition within this linkage unit
+ // the pointer needs to initially point within linkage unit and have
+ // rebase command to slide it.
+ if ( ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) {
+ // unless if this is a hybrid format, in which case the non-lazy pointer
+ // is zero on disk. So use a bind instead of a rebase to set initial value
+ if ( fOptions.makeClassicDyldInfo() )
+ fBindingInfo.push_back(BindingInfo(type, BIND_SPECIAL_DYLIB_SELF, ref->getTarget().getName(), false, addresss, 0));
+ else
+ fRebaseInfo.push_back(RebaseInfo(REBASE_TYPE_POINTER,atom->getAddress()));
+ }
+ // if this is a non-lazy pointer to a weak definition in a dylib,
+ // the pointer needs to initially bind to the dylib
+ else if ( ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition ) {
+ int ordinal = compressedOrdinalForImortedAtom(pointerTarget);
+ fBindingInfo.push_back(BindingInfo(BIND_TYPE_POINTER, ordinal, pointerTarget->getName(), false, addresss, 0));
+ }
+ }
+ else {
+ int ordinal = compressedOrdinalForImortedAtom(pointerTarget);
+ bool weak_import = fWeakImportMap[pointerTarget];
+ fBindingInfo.push_back(BindingInfo(type, ordinal, ref->getTarget().getName(), weak_import, addresss, 0));
+ }
+ }
+ }
+ }
+ }
+ }
+ else if ( (ref->getKind() == A::kPointer) || (ref->getKind() == A::kPointerWeakImport) ) {
+ if ( fSlideable && ((curSegment->fInitProtection & VM_PROT_WRITE) == 0) ) {
+ if ( fOptions.allowTextRelocs() ) {
+ if ( fOptions.warnAboutTextRelocs() )
+ warning("text reloc in %s to %s", atom->getDisplayName(), ref->getTargetName());
+ }
+ else {
+ throwf("pointer in read-only segment not allowed in slidable image, used in %s from %s",
+ atom->getDisplayName(), atom->getFile()->getPath());
+ }
+ }
+ switch ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) ) {
+ case kRelocNone:
+ // no reloc needed
+ break;
+ case kRelocInternal:
+ {
+ macho_relocation_info<P> internalReloc;
+ SectionInfo* sectInfo = (SectionInfo*)ref->getTarget().getSection();
+ uint32_t sectionNum = sectInfo->getIndex();
+ // special case _mh_dylib_header and friends which are not in any real section
+ if ( (sectionNum ==0) && sectInfo->fVirtualSection && (strcmp(sectInfo->fSectionName, "._mach_header") == 0) )
+ sectionNum = 1;
+ internalReloc.set_r_address(this->relocAddressInFinalLinkedImage(atom->getAddress() + ref->getFixUpOffset(), atom));
+ internalReloc.set_r_symbolnum(sectionNum);
+ internalReloc.set_r_pcrel(false);
+ internalReloc.set_r_length();
+ internalReloc.set_r_extern(false);
+ internalReloc.set_r_type(GENERIC_RELOC_VANILLA);
+ fInternalRelocs.push_back(internalReloc);
+ if ( fOptions.makeCompressedDyldInfo() ) {
+ fRebaseInfo.push_back(RebaseInfo(REBASE_TYPE_POINTER, atom->getAddress() + ref->getFixUpOffset()));
+ }
+ }
+ break;
+ case kRelocExternal:
+ {
+ macho_relocation_info<P> externalReloc;
+ 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();
+ externalReloc.set_r_extern(true);
+ externalReloc.set_r_type(GENERIC_RELOC_VANILLA);
+ fExternalRelocs.push_back(externalReloc);
+ if ( fOptions.makeCompressedDyldInfo() ) {
+ int64_t addend = ref->getTargetOffset();
+ uint64_t addresss = atom->getAddress() + ref->getFixUpOffset();
+ if ( !fOptions.makeClassicDyldInfo() ) {
+ if ( ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) {
+ // pointers to internal weak defs need a rebase
+ fRebaseInfo.push_back(RebaseInfo(REBASE_TYPE_POINTER, addresss));
+ }
+ }
+ uint8_t type = BIND_TYPE_POINTER;
+ if ( targetRequiresWeakBinding(ref->getTarget()) ) {
+ fWeakBindingInfo.push_back(BindingInfo(type, ref->getTarget().getName(), false, addresss, addend));
+ if ( fOptions.makeClassicDyldInfo() && (ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition) ) {
+ // hybrid linkedit puts addend in data, so we need bind phase to reset pointer to local definifion
+ fBindingInfo.push_back(BindingInfo(type, BIND_SPECIAL_DYLIB_SELF, ref->getTarget().getName(), false, addresss, addend));
+ }
+ // if this is a pointer to a weak definition in a dylib,
+ // the pointer needs to initially bind to the dylib
+ else if ( ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition ) {
+ int ordinal = compressedOrdinalForImortedAtom(&ref->getTarget());
+ fBindingInfo.push_back(BindingInfo(BIND_TYPE_POINTER, ordinal, ref->getTarget().getName(), false, addresss, addend));
+ }
+ }
+ else {
+ int ordinal = compressedOrdinalForImortedAtom(&ref->getTarget());
+ bool weak_import = fWeakImportMap[&(ref->getTarget())];
+ fBindingInfo.push_back(BindingInfo(type, ordinal, ref->getTarget().getName(), weak_import, addresss, addend));
+ }
+ }
+ }
+ break;
+ }
+ }
+ else if ( this->illegalRelocInFinalLinkedImage(*ref) ) {
+ // new x86 stubs always require text relocs
+ if ( curSection->fAllStubs || curSection->fAllStubHelpers ) {
+ if ( this->generatesLocalTextReloc(*ref, *atom, curSection) ) {
+ // relocs added to fInternalRelocs
+ }
+ }
+ else if ( fOptions.allowTextRelocs() && !atom->getSegment().isContentWritable() ) {
+ if ( fOptions.warnAboutTextRelocs() )
+ warning("text reloc in %s to %s", atom->getDisplayName(), ref->getTargetName());
+ if ( this->generatesLocalTextReloc(*ref, *atom, curSection) ) {
+ // relocs added to fInternalRelocs
+ }
+ else if ( this->generatesExternalTextReloc(*ref, *atom, curSection) ) {
+ // relocs added to fExternalRelocs
+ }
+ else {
+ throwf("relocation used in %s from %s not allowed in slidable image", atom->getDisplayName(), atom->getFile()->getPath());
+ }
+ }
+ else {
+ throwf("absolute addressing (perhaps -mdynamic-no-pic) used in %s from %s not allowed in slidable image. "
+ "Use '-read_only_relocs suppress' to enable text relocs", atom->getDisplayName(), atom->getFile()->getPath());
+ }
+ }
+ }
+ if ( curSection->fAllSelfModifyingStubs || curSection->fAllStubs ) {
+ ObjectFile::Atom* stubTarget = ((StubAtom<A>*)atom)->getTarget();
+ uint32_t undefinedSymbolIndex = (stubTarget != NULL) ? this->symbolIndex(*stubTarget) : INDIRECT_SYMBOL_ABS;
+ uint32_t offsetInSection = atom->getSectionOffset();
+ uint32_t indexInSection = offsetInSection / atom->getSize();
+ uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset;
+ IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex };
+ //fprintf(stderr,"for stub: fIndirectTableAtom->fTable.add(%d-%d => 0x%X-%s), size=%lld\n", indexInSection, indirectTableIndex, undefinedSymbolIndex, stubTarget->getName(), atom->getSize());
+ fIndirectTableAtom->fTable.push_back(entry);
+ }
+ }
+ }
+ }
+ }
+ if ( fSplitCodeToDataContentAtom != NULL )
+ fSplitCodeToDataContentAtom->encode();
+ if ( fCompressedRebaseInfoAtom != NULL )
+ fCompressedRebaseInfoAtom->encode();
+ if ( fCompressedBindingInfoAtom != NULL )
+ fCompressedBindingInfoAtom->encode();
+ if ( fCompressedWeakBindingInfoAtom != NULL )
+ fCompressedWeakBindingInfoAtom->encode();
+ if ( fCompressedLazyBindingInfoAtom != NULL )
+ fCompressedLazyBindingInfoAtom->encode();
+ if ( fCompressedExportInfoAtom != NULL )
+ fCompressedExportInfoAtom->encode();
+}
+
+
+template <>
+void Writer<ppc>::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref)
+{
+ switch ( (ppc::ReferenceKinds)ref->getKind() ) {
+ case ppc::kPICBaseHigh16:
+ fSplitCodeToDataContentAtom->addPPCHi16Location(atom, ref->getFixUpOffset());
+ break;
+ case ppc::kPointerDiff32:
+ fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset());
+ break;
+ case ppc::kPointerDiff64:
+ fSplitCodeToDataContentAtom->add64bitPointerLocation(atom, ref->getFixUpOffset());
+ break;
+ case ppc::kNoFixUp:
+ case ppc::kGroupSubordinate:
+ case ppc::kPointer:
+ case ppc::kPointerWeakImport:
+ case ppc::kPICBaseLow16:
+ case ppc::kPICBaseLow14:
+ // ignore
+ break;
+ default:
+ warning("codegen with reference kind %d in %s prevents image from loading in dyld shared cache", ref->getKind(), atom->getDisplayName());
+ fSplitCodeToDataContentAtom->setCantEncode();
+ }
+}
+
+template <>
+void Writer<ppc64>::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref)
+{
+ switch ( (ppc64::ReferenceKinds)ref->getKind() ) {
+ case ppc64::kPICBaseHigh16:
+ fSplitCodeToDataContentAtom->addPPCHi16Location(atom, ref->getFixUpOffset());
+ break;
+ case ppc64::kPointerDiff32:
+ fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset());
+ break;
+ case ppc64::kPointerDiff64:
+ fSplitCodeToDataContentAtom->add64bitPointerLocation(atom, ref->getFixUpOffset());
+ break;
+ case ppc64::kNoFixUp:
+ case ppc64::kGroupSubordinate:
+ case ppc64::kPointer:
+ case ppc64::kPointerWeakImport:
+ case ppc64::kPICBaseLow16:
+ case ppc64::kPICBaseLow14:
+ // ignore
+ break;
+ default:
+ warning("codegen with reference kind %d in %s prevents image from loading in dyld shared cache", ref->getKind(), atom->getDisplayName());
+ fSplitCodeToDataContentAtom->setCantEncode();
+ }
+}
+
+template <>
+void Writer<x86>::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref)
+{
+ switch ( (x86::ReferenceKinds)ref->getKind() ) {
+ case x86::kPointerDiff:
+ case x86::kImageOffset32:
+ if ( strcmp(ref->getTarget().getSegment().getName(), "__IMPORT") == 0 )
+ fSplitCodeToDataContentAtom->add32bitImportLocation(atom, ref->getFixUpOffset());
+ else
+ fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset());
+ break;
+ case x86::kNoFixUp:
+ case x86::kGroupSubordinate:
+ case x86::kPointer:
+ case x86::kPointerWeakImport:
+ // ignore
+ break;
+ case x86::kPCRel32:
+ case x86::kPCRel32WeakImport:
+ if ( (&(ref->getTarget().getSegment()) == &Segment::fgImportSegment)
+ || (&(ref->getTarget().getSegment()) == &Segment::fgROImportSegment) ) {
+ fSplitCodeToDataContentAtom->add32bitImportLocation(atom, ref->getFixUpOffset());
+ break;
+ }
+ // fall into warning case
+ default:
+ if ( fOptions.makeCompressedDyldInfo() && (ref->getKind() == x86::kAbsolute32) ) {
+ // will be encoded in rebase info
+ }
+ else {
+ warning("codegen in %s (offset 0x%08llX) prevents image from loading in dyld shared cache", atom->getDisplayName(), ref->getFixUpOffset());
+ fSplitCodeToDataContentAtom->setCantEncode();
+ }
+ }
+}
+
+template <>
+void Writer<x86_64>::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref)
+{
+ switch ( (x86_64::ReferenceKinds)ref->getKind() ) {
+ case x86_64::kPCRel32:
+ case x86_64::kPCRel32_1:
+ case x86_64::kPCRel32_2:
+ case x86_64::kPCRel32_4:
+ case x86_64::kPCRel32GOTLoad:
+ case x86_64::kPCRel32GOTLoadWeakImport:
+ case x86_64::kPCRel32GOT:
+ case x86_64::kPCRel32GOTWeakImport:
+ case x86_64::kPointerDiff32:
+ case x86_64::kImageOffset32:
+ fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset());
+ break;
+ case x86_64::kPointerDiff:
+ fSplitCodeToDataContentAtom->add64bitPointerLocation(atom, ref->getFixUpOffset());
+ break;
+ case x86_64::kNoFixUp:
+ case x86_64::kGroupSubordinate:
+ case x86_64::kPointer:
+ case x86_64::kGOTNoFixUp:
+ // ignore
+ break;
+ default:
+ warning("codegen in %s with kind %d prevents image from loading in dyld shared cache", atom->getDisplayName(), ref->getKind());
+ fSplitCodeToDataContentAtom->setCantEncode();
+ }
+}
+
+template <>
+void Writer<arm>::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref)
+{
+ switch ( (arm::ReferenceKinds)ref->getKind() ) {
+ case arm::kPointerDiff:
+ fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset());
+ break;
+ case arm::kNoFixUp:
+ case arm::kGroupSubordinate:
+ case arm::kPointer:
+ case arm::kPointerWeakImport:
+ case arm::kReadOnlyPointer:
+ // ignore
+ break;
+ default:
+ warning("codegen in %s prevents image from loading in dyld shared cache", atom->getDisplayName());
+ fSplitCodeToDataContentAtom->setCantEncode();
+ }
+}
+
+template <typename A>
+bool Writer<A>::segmentsCanSplitApart(const ObjectFile::Atom& from, const ObjectFile::Atom& to)
+{
+ switch ( to.getDefinitionKind() ) {
+ case ObjectFile::Atom::kExternalDefinition:
+ case ObjectFile::Atom::kExternalWeakDefinition:
+ case ObjectFile::Atom::kAbsoluteSymbol:
+ return false;
+ case ObjectFile::Atom::kRegularDefinition:
+ case ObjectFile::Atom::kWeakDefinition:
+ case ObjectFile::Atom::kTentativeDefinition:
+ // segments with same permissions slide together
+ return ( (from.getSegment().isContentExecutable() != to.getSegment().isContentExecutable())
+ || (from.getSegment().isContentWritable() != to.getSegment().isContentWritable()) );
+ }
+ throw "ld64 internal error";
+}
+
+
+template <>
+void Writer<ppc>::writeNoOps(int fd, uint32_t from, uint32_t to)
+{
+ uint32_t ppcNop;
+ OSWriteBigInt32(&ppcNop, 0, 0x60000000);
+ for (uint32_t p=from; p < to; p += 4)
+ ::pwrite(fd, &ppcNop, 4, p);
+}
+
+template <>
+void Writer<ppc64>::writeNoOps(int fd, uint32_t from, uint32_t to)
+{
+ uint32_t ppcNop;
+ OSWriteBigInt32(&ppcNop, 0, 0x60000000);
+ for (uint32_t p=from; p < to; p += 4)
+ ::pwrite(fd, &ppcNop, 4, p);
+}
+
+template <>
+void Writer<x86>::writeNoOps(int fd, uint32_t from, uint32_t to)
+{
+ uint8_t x86Nop = 0x90;
+ for (uint32_t p=from; p < to; ++p)
+ ::pwrite(fd, &x86Nop, 1, p);
+}
+
+template <>
+void Writer<x86_64>::writeNoOps(int fd, uint32_t from, uint32_t to)
+{
+ uint8_t x86Nop = 0x90;
+ for (uint32_t p=from; p < to; ++p)
+ ::pwrite(fd, &x86Nop, 1, p);
+}
+
+template <>
+void Writer<arm>::writeNoOps(int fd, uint32_t from, uint32_t to)
+{
+ // FIXME: need thumb nop?
+ uint32_t armNop;
+ OSWriteLittleInt32(&armNop, 0, 0xe1a00000);
+ for (uint32_t p=from; p < to; p += 4)
+ ::pwrite(fd, &armNop, 4, p);
+}
+
+template <>
+void Writer<ppc>::copyNoOps(uint8_t* from, uint8_t* to)
+{
+ for (uint8_t* p=from; p < to; p += 4)
+ OSWriteBigInt32((uint32_t*)p, 0, 0x60000000);
+}
+
+template <>
+void Writer<ppc64>::copyNoOps(uint8_t* from, uint8_t* to)
+{
+ for (uint8_t* p=from; p < to; p += 4)
+ OSWriteBigInt32((uint32_t*)p, 0, 0x60000000);
+}
+
+template <>
+void Writer<x86>::copyNoOps(uint8_t* from, uint8_t* to)
+{
+ for (uint8_t* p=from; p < to; ++p)
+ *p = 0x90;
+}
+
+template <>
+void Writer<x86_64>::copyNoOps(uint8_t* from, uint8_t* to)
+{
+ for (uint8_t* p=from; p < to; ++p)
+ *p = 0x90;
+}
+
+template <>
+void Writer<arm>::copyNoOps(uint8_t* from, uint8_t* to)
+{
+ // fixme: need thumb nop?
+ for (uint8_t* p=from; p < to; p += 4)
+ OSWriteBigInt32((uint32_t*)p, 0, 0xe1a00000);
+}
+
+static const char* stringName(const char* str)
+{
+ if ( strncmp(str, "cstring=", 8) == 0) {
+ static char buffer[1024];
+ char* t = buffer;
+ *t++ = '\"';
+ for(const char*s = &str[8]; *s != '\0'; ++s) {
+ switch(*s) {
+ case '\n':
+ *t++ = '\\';
+ *t++ = 'n';
+ break;
+ case '\t':
+ *t++ = '\\';
+ *t++ = 't';
+ break;
+ default:
+ *t++ = *s;
+ break;
+ }
+ if ( t > &buffer[1020] ) {
+ *t++= '\"';
+ *t++= '.';
+ *t++= '.';
+ *t++= '.';
+ *t++= '\0';
+ return buffer;
+ }
+ }
+ *t++= '\"';
+ *t++= '\0';
+ return buffer;
+ }
+ else {
+ return str;
+ }
+}
+
+
+template <> const char* Writer<ppc>::getArchString() { return "ppc"; }
+template <> const char* Writer<ppc64>::getArchString() { return "ppc64"; }
+template <> const char* Writer<x86>::getArchString() { return "i386"; }
+template <> const char* Writer<x86_64>::getArchString() { return "x86_64"; }
+template <> const char* Writer<arm>::getArchString() { return "arm"; }
+
+template <typename A>
+void Writer<A>::writeMap()
+{
+ if ( fOptions.generatedMapPath() != NULL ) {
+ FILE* mapFile = fopen(fOptions.generatedMapPath(), "w");
+ if ( mapFile != NULL ) {
+ // write output path
+ fprintf(mapFile, "# Path: %s\n", fFilePath);
+ // write output architecure
+ fprintf(mapFile, "# Arch: %s\n", getArchString());
+ // write UUID
+ if ( fUUIDAtom != NULL ) {
+ const uint8_t* uuid = fUUIDAtom->getUUID();
+ fprintf(mapFile, "# UUID: %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X \n",
+ uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
+ uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]);
+ }
+ // write table of object files
+ std::map<ObjectFile::Reader*, uint32_t> readerToOrdinal;
+ std::map<uint32_t, ObjectFile::Reader*> ordinalToReader;
+ std::map<ObjectFile::Reader*, uint32_t> readerToFileOrdinal;
+ for (std::vector<SegmentInfo*>::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) {
+ std::vector<SectionInfo*>& sectionInfos = (*segit)->fSections;
+ for (std::vector<SectionInfo*>::iterator secit = sectionInfos.begin(); secit != sectionInfos.end(); ++secit) {
+ if ( ! (*secit)->fVirtualSection ) {
+ std::vector<ObjectFile::Atom*>& sectionAtoms = (*secit)->fAtoms;
+ for (std::vector<ObjectFile::Atom*>::iterator ait = sectionAtoms.begin(); ait != sectionAtoms.end(); ++ait) {
+ ObjectFile::Reader* reader = (*ait)->getFile();
+ uint32_t readerOrdinal = (*ait)->getOrdinal();
+ std::map<ObjectFile::Reader*, uint32_t>::iterator pos = readerToOrdinal.find(reader);
+ if ( pos == readerToOrdinal.end() ) {
+ readerToOrdinal[reader] = readerOrdinal;
+ ordinalToReader[readerOrdinal] = reader;
+ }
+ }
+ }
+ }
+ }
+ fprintf(mapFile, "# Object files:\n");
+ fprintf(mapFile, "[%3u] %s\n", 0, "linker synthesized");
+ uint32_t fileIndex = 0;
+ readerToFileOrdinal[this] = fileIndex++;
+ for(std::map<uint32_t, ObjectFile::Reader*>::iterator it = ordinalToReader.begin(); it != ordinalToReader.end(); ++it) {
+ if ( it->first != 0 ) {
+ fprintf(mapFile, "[%3u] %s\n", fileIndex, it->second->getPath());
+ readerToFileOrdinal[it->second] = fileIndex++;
+ }
+ }
+ // write table of sections
+ fprintf(mapFile, "# Sections:\n");
+ fprintf(mapFile, "# Address\tSize \tSegment\tSection\n");
+ for (std::vector<SegmentInfo*>::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) {
+ std::vector<SectionInfo*>& sectionInfos = (*segit)->fSections;
+ for (std::vector<SectionInfo*>::iterator secit = sectionInfos.begin(); secit != sectionInfos.end(); ++secit) {
+ if ( ! (*secit)->fVirtualSection ) {
+ SectionInfo* sect = *secit;
+ fprintf(mapFile, "0x%08llX\t0x%08llX\t%s\t%s\n", sect->getBaseAddress(), sect->fSize,
+ (*segit)->fName, sect->fSectionName);
+ }
+ }
+ }
+ // write table of symbols
+ fprintf(mapFile, "# Symbols:\n");
+ fprintf(mapFile, "# Address\tSize \tFile Name\n");
+ for (std::vector<SegmentInfo*>::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) {
+ std::vector<SectionInfo*>& sectionInfos = (*segit)->fSections;
+ for (std::vector<SectionInfo*>::iterator secit = sectionInfos.begin(); secit != sectionInfos.end(); ++secit) {
+ if ( ! (*secit)->fVirtualSection ) {
+ std::vector<ObjectFile::Atom*>& sectionAtoms = (*secit)->fAtoms;
+ bool isCstring = (strcmp((*secit)->fSectionName, "__cstring") == 0);
+ for (std::vector<ObjectFile::Atom*>::iterator ait = sectionAtoms.begin(); ait != sectionAtoms.end(); ++ait) {
+ ObjectFile::Atom* atom = *ait;
+ fprintf(mapFile, "0x%08llX\t0x%08llX\t[%3u] %s\n", atom->getAddress(), atom->getSize(),
+ readerToFileOrdinal[atom->getFile()], isCstring ? stringName(atom->getDisplayName()): atom->getDisplayName());
+ }
+ }
+ }
+ }
+ fclose(mapFile);
+ }
+ else {
+ warning("could not write map file: %s\n", fOptions.generatedMapPath());
+ }
+ }
+}
+
+static const char* sCleanupFile = NULL;
+static void cleanup(int sig)
+{
+ ::signal(sig, SIG_DFL);
+ if ( sCleanupFile != NULL ) {
+ ::unlink(sCleanupFile);
+ }
+ if ( sig == SIGINT )
+ ::exit(1);
+}
+
+
+template <typename A>
+uint64_t Writer<A>::writeAtoms()
+{
+ // for UNIX conformance, error if file exists and is not writable
+ if ( (access(fFilePath, F_OK) == 0) && (access(fFilePath, W_OK) == -1) )
+ throwf("can't write output file: %s", fFilePath);
+
+ int permissions = 0777;
+ if ( fOptions.outputKind() == Options::kObjectFile )
+ permissions = 0666;
+ // Calling unlink first assures the file is gone so that open creates it with correct permissions
+ // It also handles the case where fFilePath file is not writable but its directory is
+ // And it means we don't have to truncate the file when done writing (in case new is smaller than old)
+ (void)unlink(fFilePath);
+
+ // try to allocate buffer for entire output file content
+ int fd = -1;
+ SectionInfo* lastSection = fSegmentInfos.back()->fSections.back();
+ uint64_t fileBufferSize = (lastSection->fFileOffset + lastSection->fSize + 4095) & (-4096);
+ uint8_t* wholeBuffer = (uint8_t*)calloc(fileBufferSize, 1);
+ uint8_t* atomBuffer = NULL;
+ bool streaming = false;
+ if ( wholeBuffer == NULL ) {
+ fd = open(fFilePath, O_CREAT | O_WRONLY | O_TRUNC, permissions);
+ if ( fd == -1 )
+ throwf("can't open output file for writing: %s, errno=%d", fFilePath, errno);
+ atomBuffer = new uint8_t[(fLargestAtomSize+4095) & (-4096)];
+ streaming = true;
+ // install signal handlers to delete output file if program is killed
+ sCleanupFile = fFilePath;
+ ::signal(SIGINT, cleanup);
+ ::signal(SIGBUS, cleanup);
+ ::signal(SIGSEGV, cleanup);
+ }
+ uint32_t size = 0;
+ uint32_t end = 0;
+ try {
+ for (std::vector<SegmentInfo*>::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) {
+ SegmentInfo* curSegment = *segit;
+ std::vector<SectionInfo*>& sectionInfos = curSegment->fSections;
+ for (std::vector<SectionInfo*>::iterator secit = sectionInfos.begin(); secit != sectionInfos.end(); ++secit) {
+ SectionInfo* curSection = *secit;
+ std::vector<ObjectFile::Atom*>& sectionAtoms = curSection->fAtoms;
+ //printf("writing with max atom size 0x%X\n", fLargestAtomSize);
+ //fprintf(stderr, "writing %lu atoms for section %p %s at file offset 0x%08llX\n", sectionAtoms.size(), curSection, curSection->fSectionName, curSection->fFileOffset);
+ if ( ! curSection->fAllZeroFill ) {
+ bool needsNops = ((strcmp(curSection->fSegmentName, "__TEXT") == 0) && (strncmp(curSection->fSectionName, "__text", 6) == 0));
+ for (std::vector<ObjectFile::Atom*>::iterator ait = sectionAtoms.begin(); ait != sectionAtoms.end(); ++ait) {
+ ObjectFile::Atom* atom = *ait;
+ if ( (atom->getDefinitionKind() != ObjectFile::Atom::kExternalDefinition)
+ && (atom->getDefinitionKind() != ObjectFile::Atom::kExternalWeakDefinition)
+ && (atom->getDefinitionKind() != ObjectFile::Atom::kAbsoluteSymbol) ) {
+ uint32_t fileOffset = curSection->fFileOffset + atom->getSectionOffset();
+ if ( fileOffset != end ) {
+ //fprintf(stderr, "writing %d pad bytes, needsNops=%d\n", fileOffset-end, needsNops);
+ if ( needsNops ) {
+ // fill gaps with no-ops
+ if ( streaming )
+ writeNoOps(fd, end, fileOffset);
+ else
+ copyNoOps(&wholeBuffer[end], &wholeBuffer[fileOffset]);
+ }
+ else if ( streaming ) {
+ // zero fill gaps
+ if ( (fileOffset-end) == 4 ) {
+ uint32_t zero = 0;
+ ::pwrite(fd, &zero, 4, end);
+ }
+ else {
+ uint8_t zero = 0x00;
+ for (uint32_t p=end; p < fileOffset; ++p)
+ ::pwrite(fd, &zero, 1, p);
+ }
+ }
+ }
+ uint64_t atomSize = atom->getSize();
+ if ( streaming ) {
+ if ( atomSize > fLargestAtomSize )
+ throwf("ld64 internal error: atom \"%s\"is larger than expected 0x%X > 0x%llX",
+ atom->getDisplayName(), atomSize, fLargestAtomSize);
+ }
+ else {
+ if ( fileOffset > fileBufferSize )
+ throwf("ld64 internal error: atom \"%s\" has file offset greater thatn expceted 0x%X > 0x%llX",
+ atom->getDisplayName(), fileOffset, fileBufferSize);
+ }
+ uint8_t* buffer = streaming ? atomBuffer : &wholeBuffer[fileOffset];
+ end = fileOffset+atomSize;
+ // copy raw bytes
+ atom->copyRawContent(buffer);
+ // apply any fix-ups
+ try {
+ std::vector<ObjectFile::Reference*>& references = atom->getReferences();
+ for (std::vector<ObjectFile::Reference*>::iterator it=references.begin(); it != references.end(); it++) {
+ ObjectFile::Reference* ref = *it;
+ if ( fOptions.outputKind() == Options::kObjectFile ) {
+ // doing ld -r
+ // skip fix-ups for undefined targets
+ if ( &(ref->getTarget()) != NULL )
+ this->fixUpReferenceRelocatable(ref, atom, buffer);
+ }
+ else {
+ // producing final linked image
+ this->fixUpReferenceFinal(ref, atom, buffer);
+ }
+ }
+ }
+ catch (const char* msg) {
+ throwf("%s in %s from %s", msg, atom->getDisplayName(), atom->getFile()->getPath());
+ }
+ //fprintf(stderr, "writing 0x%08X -> 0x%08X (addr=0x%llX, size=0x%llX), atom %p %s from %s\n",
+ // fileOffset, end, atom->getAddress(), atom->getSize(), atom, atom->getDisplayName(), atom->getFile()->getPath());
+ if ( streaming ) {
+ // write out
+ ::pwrite(fd, buffer, atomSize, fileOffset);
+ }
+ else {
+ if ( (fileOffset + atomSize) > size )
+ size = fileOffset + atomSize;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // update content based UUID
+ if ( fOptions.getUUIDMode() == Options::kUUIDContent ) {
+ uint8_t digest[CC_MD5_DIGEST_LENGTH];
+ if ( streaming ) {
+ // if output file file did not fit in memory, re-read file to generate md5 hash
+ uint32_t kMD5BufferSize = 16*1024;
+ uint8_t* md5Buffer = (uint8_t*)::malloc(kMD5BufferSize);
+ if ( md5Buffer != NULL ) {
+ CC_MD5_CTX md5State;
+ CC_MD5_Init(&md5State);
+ ::lseek(fd, 0, SEEK_SET);
+ ssize_t len;
+ while ( (len = ::read(fd, md5Buffer, kMD5BufferSize)) > 0 )
+ CC_MD5_Update(&md5State, md5Buffer, len);
+ CC_MD5_Final(digest, &md5State);
+ ::free(md5Buffer);
+ }
+ else {
+ // if malloc fails, fall back to random uuid
+ ::uuid_generate_random(digest);
+ }
+ fUUIDAtom->setContent(digest);
+ uint32_t uuidOffset = ((SectionInfo*)fUUIDAtom->getSection())->fFileOffset + fUUIDAtom->getSectionOffset();
+ fUUIDAtom->copyRawContent(atomBuffer);
+ ::pwrite(fd, atomBuffer, fUUIDAtom->getSize(), uuidOffset);
+ }
+ else {
+ // if output file fit in memory, just genrate an md5 hash in memory
+ #if 1
+ // temp hack for building on Tiger
+ CC_MD5_CTX md5State;
+ CC_MD5_Init(&md5State);
+ CC_MD5_Update(&md5State, wholeBuffer, size);
+ CC_MD5_Final(digest, &md5State);
+ #else
+ CC_MD5(wholeBuffer, size, digest);
+ #endif
+ fUUIDAtom->setContent(digest);
+ uint32_t uuidOffset = ((SectionInfo*)fUUIDAtom->getSection())->fFileOffset + fUUIDAtom->getSectionOffset();
+ fUUIDAtom->copyRawContent(&wholeBuffer[uuidOffset]);
+ }
+ }
+ }
+ catch (...) {
+ if ( sCleanupFile != NULL )
+ ::unlink(sCleanupFile);
+ throw;
+ }
+
+ // finish up
+ if ( streaming ) {
+ delete [] atomBuffer;
+ close(fd);
+ // restore default signal handlers
+ sCleanupFile = NULL;
+ ::signal(SIGINT, SIG_DFL);
+ ::signal(SIGBUS, SIG_DFL);
+ ::signal(SIGSEGV, SIG_DFL);
+ }
+ else {
+ // write whole output file in one chunk
+ fd = open(fFilePath, O_CREAT | O_WRONLY | O_TRUNC, permissions);
+ if ( fd == -1 )
+ throwf("can't open output file for writing: %s, errno=%d", fFilePath, errno);
+ ::pwrite(fd, wholeBuffer, size, 0);
+ close(fd);
+ delete [] wholeBuffer;
+ }
+
+ return end;
+}
+
+template <>
+void Writer<arm>::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const
+{
+ int64_t displacement;
+ int64_t baseAddr;
+ uint32_t instruction;
+ uint32_t newInstruction;
+ uint64_t targetAddr = 0;
+ uint32_t firstDisp;
+ uint32_t nextDisp;
+ uint32_t opcode = 0;
+ bool relocateableExternal = false;
+ bool is_bl;
+ bool is_blx;
+ bool targetIsThumb;
+
+ if ( ref->getTargetBinding() != ObjectFile::Reference::kDontBind ) {
+ targetAddr = ref->getTarget().getAddress() + ref->getTargetOffset();
+ relocateableExternal = (relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal);
+ }
+
+ uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()];
+ switch ( (arm::ReferenceKinds)(ref->getKind()) ) {
+ case arm::kNoFixUp:
+ case arm::kFollowOn:
+ case arm::kGroupSubordinate:
+ // do nothing
+ break;
+ case arm::kPointerWeakImport:
+ case arm::kPointer:
+ // If this is the lazy pointers section, then set all lazy pointers to
+ // point to the dyld stub binding helper.
+ if ( ((SectionInfo*)inAtom->getSection())->fAllLazyPointers
+ || ((SectionInfo*)inAtom->getSection())->fAllLazyDylibPointers ) {
+ switch (ref->getTarget().getDefinitionKind()) {
+ case ObjectFile::Atom::kExternalDefinition:
+ case ObjectFile::Atom::kExternalWeakDefinition:
+ // prebound lazy pointer to another dylib ==> pointer contains zero
+ LittleEndian::set32(*fixUp, 0);
+ break;
+ case ObjectFile::Atom::kTentativeDefinition:
+ case ObjectFile::Atom::kRegularDefinition:
+ case ObjectFile::Atom::kWeakDefinition:
+ case ObjectFile::Atom::kAbsoluteSymbol:
+ // prebound lazy pointer to withing this dylib ==> pointer contains address
+ if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0) )
+ targetAddr |= 1;
+ LittleEndian::set32(*fixUp, targetAddr);
+ break;
+ }
+ }
+ else if ( relocateableExternal ) {
+ if ( fOptions.prebind() ) {
+ switch (ref->getTarget().getDefinitionKind()) {
+ case ObjectFile::Atom::kExternalDefinition:
+ case ObjectFile::Atom::kExternalWeakDefinition:
+ // prebound external relocation ==> pointer contains addend
+ LittleEndian::set32(*fixUp, ref->getTargetOffset());
+ break;
+ case ObjectFile::Atom::kTentativeDefinition:
+ case ObjectFile::Atom::kRegularDefinition:
+ case ObjectFile::Atom::kWeakDefinition:
+ // prebound external relocation to internal atom ==> pointer contains target address + addend
+ if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0) )
+ targetAddr |= 1;
+ LittleEndian::set32(*fixUp, targetAddr);
+ break;
+ case ObjectFile::Atom::kAbsoluteSymbol:
+ break;
+ }
+ }
+ else {
+ // external relocation ==> pointer contains addend
+ LittleEndian::set32(*fixUp, ref->getTargetOffset());
+ }
+ }
+ else {
+ // pointer contains target address
+ if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0))
+ targetAddr |= 1;
+ LittleEndian::set32(*fixUp, targetAddr);
+ }
+ break;
+ case arm::kPointerDiff:
+ LittleEndian::set32(*fixUp,
+ (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) );
+ break;
+ case arm::kReadOnlyPointer:
+ if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0))
+ targetAddr |= 1;
+ switch ( ref->getTarget().getDefinitionKind() ) {
+ case ObjectFile::Atom::kRegularDefinition:
+ case ObjectFile::Atom::kWeakDefinition:
+ case ObjectFile::Atom::kTentativeDefinition:
+ // pointer contains target address
+ LittleEndian::set32(*fixUp, targetAddr);
+ break;
+ case ObjectFile::Atom::kExternalDefinition:
+ case ObjectFile::Atom::kExternalWeakDefinition:
+ // external relocation ==> pointer contains addend
+ LittleEndian::set32(*fixUp, ref->getTargetOffset());
+ break;
+ case ObjectFile::Atom::kAbsoluteSymbol:
+ // pointer contains target address
+ LittleEndian::set32(*fixUp, targetAddr);
+ break;
+ }
+ break;
+ case arm::kBranch24WeakImport:
+ case arm::kBranch24:
+ displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset());
+ // The pc added will be +8 from the pc
+ displacement -= 8;
+ // fprintf(stderr, "bl/blx fixup to %s at 0x%08llX, displacement = 0x%08llX\n", ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), displacement);
+ // max positive displacement is 0x007FFFFF << 2
+ // max negative displacement is 0xFF800000 << 2
+ if ( (displacement > 33554428LL) || (displacement < (-33554432LL)) ) {
+ throwf("b/bl/blx out of range (%lld max is +/-32M) from %s in %s to %s in %s",
+ displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(),
+ ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath());
+ }
+ instruction = LittleEndian::get32(*fixUp);
+ // Make sure we are calling arm with bl, thumb with blx
+ is_bl = ((instruction & 0xFF000000) == 0xEB000000);
+ is_blx = ((instruction & 0xFE000000) == 0xFA000000);
+ if ( is_bl && ref->getTarget().isThumb() ) {
+ uint32_t opcode = 0xFA000000;
+ uint32_t disp = (uint32_t)(displacement >> 2) & 0x00FFFFFF;
+ uint32_t h_bit = (uint32_t)(displacement << 23) & 0x01000000;
+ newInstruction = opcode | h_bit | disp;
+ }
+ else if ( is_blx && !ref->getTarget().isThumb() ) {
+ uint32_t opcode = 0xEB000000;
+ uint32_t disp = (uint32_t)(displacement >> 2) & 0x00FFFFFF;
+ newInstruction = opcode | disp;
+ }
+ else if ( !is_bl && !is_blx && ref->getTarget().isThumb() ) {
+ throwf("don't know how to convert instruction %x referencing %s to thumb",
+ instruction, ref->getTarget().getDisplayName());
+ }
+ else {
+ newInstruction = (instruction & 0xFF000000) | ((uint32_t)(displacement >> 2) & 0x00FFFFFF);
+ }
+ LittleEndian::set32(*fixUp, newInstruction);
+ break;
+ case arm::kThumbBranch22WeakImport:
+ case arm::kThumbBranch22:
+ instruction = LittleEndian::get32(*fixUp);
+ is_bl = ((instruction & 0xD000F800) == 0xD000F000);
+ is_blx = ((instruction & 0xD000F800) == 0xC000F000);
+ targetIsThumb = ref->getTarget().isThumb();
+
+ // The pc added will be +4 from the pc
+ baseAddr = inAtom->getAddress() + ref->getFixUpOffset() + 4;
+ // If the target is not thumb, we will be generating a blx instruction
+ // Since blx cannot have the low bit set, set bit[1] of the target to
+ // bit[1] of the base address, so that the difference is a multiple of
+ // 4 bytes.
+ if ( !targetIsThumb ) {
+ targetAddr &= -3ULL;
+ targetAddr |= (baseAddr & 2LL);
+ }
+ displacement = targetAddr - baseAddr;
+
+ // max positive displacement is 0x003FFFFE
+ // max negative displacement is 0xFFC00000
+ if ( (displacement > 4194302LL) || (displacement < (-4194304LL)) ) {
+ // armv7 supports a larger displacement
+ if ( fOptions.preferSubArchitecture() && fOptions.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) {
+ if ( (displacement > 16777214) || (displacement < (-16777216LL)) ) {
+ throwf("thumb bl/blx out of range (%lld max is +/-16M) from %s in %s to %s in %s",
+ displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(),
+ ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath());
+ }
+ else {
+ // The instruction is really two instructions:
+ // The lower 16 bits are the first instruction, which contains the high
+ // 11 bits of the displacement.
+ // The upper 16 bits are the second instruction, which contains the low
+ // 11 bits of the displacement, as well as differentiating bl and blx.
+ uint32_t s = (uint32_t)(displacement >> 24) & 0x1;
+ uint32_t i1 = (uint32_t)(displacement >> 23) & 0x1;
+ uint32_t i2 = (uint32_t)(displacement >> 22) & 0x1;
+ uint32_t imm10 = (uint32_t)(displacement >> 12) & 0x3FF;
+ uint32_t imm11 = (uint32_t)(displacement >> 1) & 0x7FF;
+ uint32_t j1 = (i1 == s);
+ uint32_t j2 = (i2 == s);
+ if ( is_bl ) {
+ if ( targetIsThumb )
+ opcode = 0xD000F000; // keep bl
+ else
+ opcode = 0xC000F000; // change to blx
+ }
+ else if ( is_blx ) {
+ if ( targetIsThumb )
+ opcode = 0xD000F000; // change to bl
+ else
+ opcode = 0xC000F000; // keep blx
+ }
+ else if ( !is_bl && !is_blx && !targetIsThumb ) {
+ throwf("don't know how to convert instruction %x referencing %s to arm",
+ instruction, ref->getTarget().getDisplayName());
+ }
+ nextDisp = (j1 << 13) | (j2 << 11) | imm11;
+ firstDisp = (s << 10) | imm10;
+ newInstruction = opcode | (nextDisp << 16) | firstDisp;
+ //warning("s=%d, j1=%d, j2=%d, imm10=0x%0X, imm11=0x%0X, opcode=0x%08X, first=0x%04X, next=0x%04X, new=0x%08X, disp=0x%llX for %s to %s\n",
+ // s, j1, j2, imm10, imm11, opcode, firstDisp, nextDisp, newInstruction, displacement, inAtom->getDisplayName(), ref->getTarget().getDisplayName());
+ LittleEndian::set32(*fixUp, newInstruction);
+ }
+ }
+ else {
+ throwf("thumb bl/blx out of range (%lld max is +/-4M) from %s in %s to %s in %s",
+ displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(),
+ ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath());
+ }
+ }
+ else {
+ // The instruction is really two instructions:
+ // The lower 16 bits are the first instruction, which contains the high
+ // 11 bits of the displacement.
+ // The upper 16 bits are the second instruction, which contains the low
+ // 11 bits of the displacement, as well as differentiating bl and blx.
+ firstDisp = (uint32_t)(displacement >> 12) & 0x7FF;
+ nextDisp = (uint32_t)(displacement >> 1) & 0x7FF;
+ if ( is_bl && !targetIsThumb ) {
+ opcode = 0xE800F000;
+ }
+ else if ( is_blx && targetIsThumb ) {
+ opcode = 0xF800F000;
+ }
+ else if ( !is_bl && !is_blx && !targetIsThumb ) {
+ throwf("don't know how to convert instruction %x referencing %s to arm",
+ instruction, ref->getTarget().getDisplayName());
+ }
+ else {
+ opcode = instruction & 0xF800F800;
+ }
+ newInstruction = opcode | (nextDisp << 16) | firstDisp;
+ LittleEndian::set32(*fixUp, newInstruction);
+ }
+ break;
+ case arm::kDtraceProbeSite:
+ if ( inAtom->isThumb() ) {
+ // change 32-bit blx call site to two thumb NOPs
+ LittleEndian::set32(*fixUp, 0x46C046C0);
+ }
+ else {
+ // change call site to a NOP
+ LittleEndian::set32(*fixUp, 0xE1A00000);
+ }
+ break;
+ case arm::kDtraceIsEnabledSite:
+ if ( inAtom->isThumb() ) {
+ // change 32-bit blx call site to 'nop', 'eor r0, r0'
+ LittleEndian::set32(*fixUp, 0x46C04040);
+ }
+ else {
+ // change call site to 'eor r0, r0, r0'
+ LittleEndian::set32(*fixUp, 0xE0200000);
+ }
+ break;
+ case arm::kDtraceTypeReference:
+ case arm::kDtraceProbe:
+ // nothing to fix up
+ break;
+ default:
+ throw "boom shaka laka";
+ }
+}
+
+template <>
+void Writer<arm>::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const
+{
+ int64_t displacement;
+ uint32_t instruction;
+ uint32_t newInstruction;
+ uint64_t targetAddr = 0;
+ int64_t baseAddr;
+ uint32_t firstDisp;
+ uint32_t nextDisp;
+ uint32_t opcode = 0;
+ bool relocateableExternal = false;
+ bool is_bl;
+ bool is_blx;
+ bool targetIsThumb;
+
+ if ( ref->getTargetBinding() != ObjectFile::Reference::kDontBind ) {
+ targetAddr = ref->getTarget().getAddress() + ref->getTargetOffset();
+ relocateableExternal = this->makesExternalRelocatableReference(ref->getTarget());
+ }
+
+ uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()];
+ switch ( (arm::ReferenceKinds)(ref->getKind()) ) {
+ case arm::kNoFixUp:
+ case arm::kFollowOn:
+ case arm::kGroupSubordinate:
+ // do nothing
+ break;
+ case arm::kPointer:
+ case arm::kReadOnlyPointer:
+ case arm::kPointerWeakImport:
+ {
+ if ( ((SectionInfo*)inAtom->getSection())->fAllNonLazyPointers ) {
+ // indirect symbol table has INDIRECT_SYMBOL_LOCAL, so we must put address in content
+ if ( this->indirectSymbolInRelocatableIsLocal(ref) )
+ LittleEndian::set32(*fixUp, targetAddr);
+ else
+ LittleEndian::set32(*fixUp, 0);
+ }
+ else if ( relocateableExternal ) {
+ if ( fOptions.prebind() ) {
+ switch (ref->getTarget().getDefinitionKind()) {
+ case ObjectFile::Atom::kExternalDefinition:
+ case ObjectFile::Atom::kExternalWeakDefinition:
+ // prebound external relocation ==> pointer contains addend
+ LittleEndian::set32(*fixUp, ref->getTargetOffset());
+ break;
+ case ObjectFile::Atom::kTentativeDefinition:
+ case ObjectFile::Atom::kRegularDefinition:
+ case ObjectFile::Atom::kWeakDefinition:
+ // prebound external relocation to internal atom ==> pointer contains target address + addend
+ LittleEndian::set32(*fixUp, targetAddr);
+ break;
+ case ObjectFile::Atom::kAbsoluteSymbol:
+ break;
+ }
+ }
+ }
+ else {
+ // internal relocation
+ if ( ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition ) {
+ // pointer contains target address
+ if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0))
+ targetAddr |= 1;
+ LittleEndian::set32(*fixUp, targetAddr);
+ }
+ else {
+ // pointer contains addend
+ LittleEndian::set32(*fixUp, ref->getTargetOffset());
+ }
+ }
+ }
+ break;
+ case arm::kPointerDiff:
+ LittleEndian::set32(*fixUp,
+ (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) );
+ break;
+ case arm::kDtraceProbeSite:
+ case arm::kDtraceIsEnabledSite:
+ case arm::kBranch24WeakImport:
+ case arm::kBranch24:
+ displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset());
+ // The pc added will be +8 from the pc
+ displacement -= 8;
+ // fprintf(stderr, "b/bl/blx fixup to %s at 0x%08llX, displacement = 0x%08llX\n", ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), displacement);
+ if ( relocateableExternal ) {
+ // doing "ld -r" to an external symbol
+ // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target
+ displacement -= ref->getTarget().getAddress();
+ }
+ else {
+ // max positive displacement is 0x007FFFFF << 2
+ // max negative displacement is 0xFF800000 << 2
+ if ( (displacement > 33554428LL) || (displacement < (-33554432LL)) ) {
+ throwf("arm b/bl/blx out of range (%lld max is +/-32M) from %s in %s to %s in %s",
+ displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(),
+ ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath());
+ }
+ }
+ instruction = LittleEndian::get32(*fixUp);
+ // Make sure we are calling arm with bl, thumb with blx
+ is_bl = ((instruction & 0xFF000000) == 0xEB000000);
+ is_blx = ((instruction & 0xFE000000) == 0xFA000000);
+ if ( is_bl && ref->getTarget().isThumb() ) {
+ uint32_t opcode = 0xFA000000;
+ uint32_t disp = (uint32_t)(displacement >> 2) & 0x00FFFFFF;
+ uint32_t h_bit = (uint32_t)(displacement << 23) & 0x01000000;
+ newInstruction = opcode | h_bit | disp;
+ }
+ else if ( is_blx && !ref->getTarget().isThumb() ) {
+ uint32_t opcode = 0xEB000000;
+ uint32_t disp = (uint32_t)(displacement >> 2) & 0x00FFFFFF;
+ newInstruction = opcode | disp;
+ }
+ else if ( !is_bl && !is_blx && ref->getTarget().isThumb() ) {
+ throwf("don't know how to convert instruction %x referencing %s to thumb",
+ instruction, ref->getTarget().getDisplayName());
+ }
+ else {
+ newInstruction = (instruction & 0xFF000000) | ((uint32_t)(displacement >> 2) & 0x00FFFFFF);
+ }
+ LittleEndian::set32(*fixUp, newInstruction);
+ break;
+ case arm::kThumbBranch22WeakImport:
+ case arm::kThumbBranch22:
+ instruction = LittleEndian::get32(*fixUp);
+ is_bl = ((instruction & 0xF8000000) == 0xF8000000);
+ is_blx = ((instruction & 0xF8000000) == 0xE8000000);
+ targetIsThumb = ref->getTarget().isThumb();
+
+ // The pc added will be +4 from the pc
+ baseAddr = inAtom->getAddress() + ref->getFixUpOffset() + 4;
+ // If the target is not thumb, we will be generating a blx instruction
+ // Since blx cannot have the low bit set, set bit[1] of the target to
+ // bit[1] of the base address, so that the difference is a multiple of
+ // 4 bytes.
+ if (!targetIsThumb) {
+ targetAddr &= -3ULL;
+ targetAddr |= (baseAddr & 2LL);
+ }
+ displacement = targetAddr - baseAddr;
+
+ //fprintf(stderr, "thumb %s fixup to %s at 0x%08llX, baseAddr = 0x%08llX, displacement = 0x%08llX, %d\n", is_blx ? "blx" : "bl", ref->getTarget().getDisplayName(), targetAddr, baseAddr, displacement, targetIsThumb);
+ if ( relocateableExternal ) {
+ // doing "ld -r" to an external symbol
+ // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target
+ displacement -= ref->getTarget().getAddress();
+ }
+
+ if ( (displacement > 4194302LL) || (displacement < (-4194304LL)) ) {
+ // armv7 supports a larger displacement
+ if ( fOptions.preferSubArchitecture() && fOptions.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) {
+ if ( (displacement > 16777214) || (displacement < (-16777216LL)) ) {
+ throwf("thumb bl/blx out of range (%lld max is +/-16M) from %s in %s to %s in %s",
+ displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(),
+ ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath());
+ }
+ else {
+ // The instruction is really two instructions:
+ // The lower 16 bits are the first instruction, which contains the high
+ // 11 bits of the displacement.
+ // The upper 16 bits are the second instruction, which contains the low
+ // 11 bits of the displacement, as well as differentiating bl and blx.
+ uint32_t s = (uint32_t)(displacement >> 24) & 0x1;
+ uint32_t i1 = (uint32_t)(displacement >> 23) & 0x1;
+ uint32_t i2 = (uint32_t)(displacement >> 22) & 0x1;
+ uint32_t imm10 = (uint32_t)(displacement >> 12) & 0x3FF;
+ uint32_t imm11 = (uint32_t)(displacement >> 1) & 0x7FF;
+ uint32_t j1 = (i1 == s);
+ uint32_t j2 = (i2 == s);
+ if ( is_bl ) {
+ if ( targetIsThumb )
+ opcode = 0xD000F000; // keep bl
+ else
+ opcode = 0xC000F000; // change to blx
+ }
+ else if ( is_blx ) {
+ if ( targetIsThumb )
+ opcode = 0xD000F000; // change to bl
+ else
+ opcode = 0xC000F000; // keep blx
+ }
+ else if ( !is_bl && !is_blx && !targetIsThumb ) {
+ throwf("don't know how to convert instruction %x referencing %s to arm",
+ instruction, ref->getTarget().getDisplayName());
+ }
+ nextDisp = (j1 << 13) | (j2 << 11) | imm11;
+ firstDisp = (s << 10) | imm10;
+ newInstruction = opcode | (nextDisp << 16) | firstDisp;
+ //warning("s=%d, j1=%d, j2=%d, imm10=0x%0X, imm11=0x%0X, opcode=0x%08X, first=0x%04X, next=0x%04X, new=0x%08X, disp=0x%llX for %s to %s\n",
+ // s, j1, j2, imm10, imm11, opcode, firstDisp, nextDisp, newInstruction, displacement, inAtom->getDisplayName(), ref->getTarget().getDisplayName());
+ LittleEndian::set32(*fixUp, newInstruction);
+ break;
+ }
+ }
+ else {
+ throwf("thumb bl/blx out of range (%lld max is +/-4M) from %s in %s to %s in %s",
+ displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(),
+ ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath());
+ }
+ }
+ // The instruction is really two instructions:
+ // The lower 16 bits are the first instruction, which contains the first
+ // 11 bits of the displacement.
+ // The upper 16 bits are the second instruction, which contains the next
+ // 11 bits of the displacement, as well as differentiating bl and blx.
+ firstDisp = (uint32_t)(displacement >> 12) & 0x7FF;
+ nextDisp = (uint32_t)(displacement >> 1) & 0x7FF;
+ if ( is_bl && !targetIsThumb ) {
+ opcode = 0xE800F000;
+ }
+ else if ( is_blx && targetIsThumb ) {
+ opcode = 0xF800F000;
+ }
+ else if ( !is_bl && !is_blx && !targetIsThumb ) {
+ throwf("don't know how to convert instruction %x referencing %s to arm",
+ instruction, ref->getTarget().getDisplayName());
+ }
+ else {
+ opcode = instruction & 0xF800F800;
+ }
+ newInstruction = opcode | (nextDisp << 16) | firstDisp;
+ LittleEndian::set32(*fixUp, newInstruction);
+ break;
+ case arm::kDtraceProbe:
+ case arm::kDtraceTypeReference:
+ // nothing to fix up
+ break;
+ }
+}
+
+template <>
+void Writer<x86>::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const
+{
+ uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()];
+ uint8_t* dtraceProbeSite;
+ const int64_t kTwoGigLimit = 0x7FFFFFFF;
+ const int64_t kSixteenMegLimit = 0x00FFFFFF;
+ const int64_t kSixtyFourKiloLimit = 0x7FFF;
+ const int64_t kOneTwentyEightLimit = 0x7F;
+ int64_t displacement;
+ uint32_t temp;
+ x86::ReferenceKinds kind = (x86::ReferenceKinds)(ref->getKind());
+ switch ( kind ) {
+ case x86::kNoFixUp:
+ case x86::kFollowOn:
+ case x86::kGroupSubordinate:
+ // do nothing
+ break;
+ case x86::kPointerWeakImport:
+ case x86::kPointer:
+ {
+ if ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal ) {
+ if ( fOptions.prebind() ) {
+ switch (ref->getTarget().getDefinitionKind()) {
+ case ObjectFile::Atom::kExternalDefinition:
+ case ObjectFile::Atom::kExternalWeakDefinition:
+ // prebound external relocation ==> pointer contains addend
+ LittleEndian::set32(*fixUp, ref->getTargetOffset());
+ break;
+ case ObjectFile::Atom::kTentativeDefinition:
+ case ObjectFile::Atom::kRegularDefinition:
+ case ObjectFile::Atom::kWeakDefinition:
+ // prebound external relocation to internal atom ==> pointer contains target address + addend
+ LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset());
+ break;
+ case ObjectFile::Atom::kAbsoluteSymbol:
+ break;
+ }
+ }
+ else if ( !fOptions.makeClassicDyldInfo()
+ && (ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition) ) {
+ // when using only compressed dyld info, pointer is initially set to point directly to weak definition
+ LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset());
+ }
+ else {
+ // external relocation ==> pointer contains addend
+ LittleEndian::set32(*fixUp, ref->getTargetOffset());
+ }
+ }
+ else {
+ // pointer contains target address
+ //printf("Atom::fixUpReferenceFinal() target.name=%s, target.address=0x%08llX\n", target.getDisplayName(), target.getAddress());
+ LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset());
+ }
+ }
+ break;
+ case x86::kPointerDiff:
+ displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset());
+ LittleEndian::set32(*fixUp, (uint32_t)displacement);
+ break;
+ case x86::kPointerDiff16:
+ displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset());
+ if ( (displacement > kSixtyFourKiloLimit) || (displacement < -(kSixtyFourKiloLimit)) )
+ throwf("16-bit pointer diff out of range in %s", inAtom->getDisplayName());
+ LittleEndian::set16(*((uint16_t*)fixUp), (uint16_t)displacement);
+ break;
+ case x86::kPointerDiff24:
+ displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset());
+ if ( (displacement > kSixteenMegLimit) || (displacement < 0) )
+ throwf("24-bit pointer diff out of range in %s", inAtom->getDisplayName());
+ temp = LittleEndian::get32(*fixUp);
+ temp &= 0xFF000000;
+ temp |= (displacement & 0x00FFFFFF);
+ LittleEndian::set32(*fixUp, temp);
+ break;
+ case x86::kSectionOffset24:
+ displacement = ref->getTarget().getSectionOffset();
+ if ( (displacement > kSixteenMegLimit) || (displacement < 0) )
+ throwf("24-bit pointer diff out of range in %s", inAtom->getDisplayName());
+ temp = LittleEndian::get32(*fixUp);
+ temp &= 0xFF000000;
+ temp |= (displacement & 0x00FFFFFF);
+ LittleEndian::set32(*fixUp, temp);
+ break;
+ case x86::kDtraceProbeSite:
+ // change call site to a NOP
+ dtraceProbeSite = (uint8_t*)fixUp;
+ dtraceProbeSite[-1] = 0x90; // 1-byte nop
+ dtraceProbeSite[0] = 0x0F; // 4-byte nop
+ dtraceProbeSite[1] = 0x1F;
+ dtraceProbeSite[2] = 0x40;
+ dtraceProbeSite[3] = 0x00;
+ break;
+ case x86::kDtraceIsEnabledSite:
+ // change call site to a clear eax
+ dtraceProbeSite = (uint8_t*)fixUp;
+ dtraceProbeSite[-1] = 0x33; // xorl eax,eax
+ dtraceProbeSite[0] = 0xC0;
+ dtraceProbeSite[1] = 0x90; // 1-byte nop
+ dtraceProbeSite[2] = 0x90; // 1-byte nop
+ dtraceProbeSite[3] = 0x90; // 1-byte nop
+ break;
+ case x86::kPCRel32WeakImport:
+ case x86::kPCRel32:
+ case x86::kPCRel16:
+ case x86::kPCRel8:
+ displacement = 0;
+ switch ( ref->getTarget().getDefinitionKind() ) {
+ case ObjectFile::Atom::kRegularDefinition:
+ case ObjectFile::Atom::kWeakDefinition:
+ displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4);
+ break;
+ case ObjectFile::Atom::kExternalDefinition:
+ case ObjectFile::Atom::kExternalWeakDefinition:
+ throw "codegen problem, can't use rel32 to external symbol";
+ case ObjectFile::Atom::kTentativeDefinition:
+ displacement = 0;
+ break;
+ case ObjectFile::Atom::kAbsoluteSymbol:
+ displacement = (ref->getTarget().getSectionOffset() + ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4);
+ break;
+ }
+ if ( kind == x86::kPCRel8 ) {
+ displacement += 3;
+ if ( (displacement > kOneTwentyEightLimit) || (displacement < -(kOneTwentyEightLimit)) ) {
+ //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());
+ throwf("rel8 out of range in %s", inAtom->getDisplayName());
+ }
+ *(int8_t*)fixUp = (int8_t)displacement;
+ }
+ else if ( kind == x86::kPCRel16 ) {
+ displacement += 2;
+ if ( (displacement > kSixtyFourKiloLimit) || (displacement < -(kSixtyFourKiloLimit)) ) {
+ //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());
+ throwf("rel16 out of range in %s", inAtom->getDisplayName());
+ }
+ LittleEndian::set16(*((uint16_t*)fixUp), (uint16_t)displacement);
+ }
+ else {
+ if ( (displacement > kTwoGigLimit) || (displacement < (-kTwoGigLimit)) ) {
+ //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());
+ throwf("rel32 out of range in %s", inAtom->getDisplayName());
+ }
+ 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 relocation ==> pointer contains addend
+ LittleEndian::set32(*fixUp, ref->getTargetOffset());
+ break;
+ case ObjectFile::Atom::kAbsoluteSymbol:
+ // pointer contains target address
+ LittleEndian::set32(*fixUp, ref->getTarget().getSectionOffset() + ref->getTargetOffset());
+ break;
+ }
+ break;
+ case x86::kImageOffset32:
+ // offset of target atom from mach_header
+ displacement = ref->getTarget().getAddress() + ref->getTargetOffset() - fMachHeaderAtom->getAddress();
+ LittleEndian::set32(*fixUp, (int32_t)displacement);
+ break;
+ case x86::kDtraceTypeReference:
+ case x86::kDtraceProbe:
+ // nothing to fix up
+ break;
+ }
+}
+
+
+
+template <>
+void Writer<x86>::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const
+{
+ const int64_t kTwoGigLimit = 0x7FFFFFFF;
+ const int64_t kSixtyFourKiloLimit = 0x7FFF;
+ const int64_t kOneTwentyEightLimit = 0x7F;
+ uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()];
+ bool isExtern = this->makesExternalRelocatableReference(ref->getTarget());
+ int64_t displacement;
+ x86::ReferenceKinds kind = (x86::ReferenceKinds)(ref->getKind());
+ switch ( kind ) {
+ case x86::kNoFixUp:
+ case x86::kFollowOn:
+ case x86::kGroupSubordinate:
+ // do nothing
+ break;
+ case x86::kPointer:
+ case x86::kPointerWeakImport:
+ case x86::kAbsolute32:
+ {
+ if ( ((SectionInfo*)inAtom->getSection())->fAllNonLazyPointers ) {
+ // if INDIRECT_SYMBOL_LOCAL the content is pointer, else it is zero
+ if ( this->indirectSymbolInRelocatableIsLocal(ref) )
+ LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset());
+ else
+ LittleEndian::set32(*fixUp, 0);
+ }
+ else if ( isExtern ) {
+ // external relocation ==> pointer contains addend
+ LittleEndian::set32(*fixUp, ref->getTargetOffset());
+ }
+ else if ( ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition ) {
+ // internal relocation => pointer contains target address
+ LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset());
+ }
+ else {
+ // internal relocation to tentative ==> pointer contains addend
+ LittleEndian::set32(*fixUp, ref->getTargetOffset());
+ }
+ }
+ break;
+ case x86::kPointerDiff:
+ displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset());
+ LittleEndian::set32(*fixUp, (uint32_t)displacement);
+ break;
+ case x86::kPointerDiff16:
+ displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset());
+ if ( (displacement > kSixtyFourKiloLimit) || (displacement < -(kSixtyFourKiloLimit)) )
+ throwf("16-bit pointer diff out of range in %s", inAtom->getDisplayName());
+ LittleEndian::set16(*((uint16_t*)fixUp), (uint16_t)displacement);
+ break;
+ case x86::kPCRel8:
+ case x86::kPCRel16:
+ case x86::kPCRel32:
+ case x86::kPCRel32WeakImport:
+ case x86::kDtraceProbeSite:
+ case x86::kDtraceIsEnabledSite:
+ {
+ if ( isExtern )
+ displacement = ref->getTargetOffset() - (inAtom->getAddress() + ref->getFixUpOffset() + 4);
+ else
+ displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4);
+ if ( kind == x86::kPCRel8 ) {
+ displacement += 3;
+ if ( (displacement > kOneTwentyEightLimit) || (displacement < -(kOneTwentyEightLimit)) ) {
+ //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());
+ throwf("rel8 out of range (%lld)in %s", displacement, inAtom->getDisplayName());
+ }
+ int8_t byte = (int8_t)displacement;
+ *((int8_t*)fixUp) = byte;
+ }
+ else if ( kind == x86::kPCRel16 ) {
+ displacement += 2;
+ if ( (displacement > kSixtyFourKiloLimit) || (displacement < -(kSixtyFourKiloLimit)) ) {
+ //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());
+ throwf("rel16 out of range in %s", inAtom->getDisplayName());
+ }
+ int16_t word = (int16_t)displacement;
+ LittleEndian::set16(*((uint16_t*)fixUp), word);
+ }
+ else {
+ if ( (displacement > kTwoGigLimit) || (displacement < (-kTwoGigLimit)) ) {
+ //fprintf(stderr, "call out of range, displacement=ox%llX, from %s in %s to %s in %s\n", displacement,
+ // inAtom->getDisplayName(), inAtom->getFile()->getPath(), ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath());
+ throwf("rel32 out of range in %s", inAtom->getDisplayName());
+ }
+ LittleEndian::set32(*fixUp, (int32_t)displacement);
+ }
+ }
+ break;
+ case x86::kPointerDiff24:
+ throw "internal linker error, kPointerDiff24 can't be encoded into object files";
+ case x86::kImageOffset32:
+ throw "internal linker error, kImageOffset32 can't be encoded into object files";
+ case x86::kSectionOffset24:
+ throw "internal linker error, kSectionOffset24 can't be encoded into object files";
+ case x86::kDtraceProbe:
+ case x86::kDtraceTypeReference:
+ // nothing to fix up
+ break;
+ }
+}
+
+template <>
+void Writer<x86_64>::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const
+{
+ const int64_t twoGigLimit = 0x7FFFFFFF;
+ const int64_t kSixteenMegLimit = 0x00FFFFFF;
+ uint64_t* fixUp = (uint64_t*)&buffer[ref->getFixUpOffset()];
+ uint8_t* dtraceProbeSite;
+ int64_t displacement = 0;
+ uint32_t temp;
+ switch ( (x86_64::ReferenceKinds)(ref->getKind()) ) {
+ case x86_64::kNoFixUp:
+ case x86_64::kGOTNoFixUp:
+ case x86_64::kFollowOn:
+ case x86_64::kGroupSubordinate:
+ // do nothing
+ break;
+ case x86_64::kPointerWeakImport:
+ case x86_64::kPointer:
+ {
+ if ( &ref->getTarget() != NULL ) {
+ //fprintf(stderr, "fixUpReferenceFinal: %s reference to %s\n", this->getDisplayName(), target.getDisplayName());
+ if ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal) {
+ if ( !fOptions.makeClassicDyldInfo()
+ && (ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition) ) {
+ // when using only compressed dyld info, pointer is initially set to point directly to weak definition
+ LittleEndian::set64(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset());
+ }
+ else {
+ // external relocation ==> 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::kPointer32:
+ {
+ //fprintf(stderr, "fixUpReferenceFinal: %s reference to %s\n", this->getDisplayName(), target.getDisplayName());
+ if ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal ) {
+ // external relocation
+ throwf("32-bit pointer to dylib or weak symbol %s not supported for x86_64",ref->getTarget().getDisplayName());
+ }
+ else {
+ // internal relocation
+ // pointer contains target address
+ //printf("Atom::fixUpReferenceFinal) target.name=%s, target.address=0x%08llX\n", target.getDisplayName(), target.getAddress());
+ displacement = ref->getTarget().getAddress() + ref->getTargetOffset();
+ switch ( fOptions.outputKind() ) {
+ case Options::kObjectFile:
+ case Options::kPreload:
+ case Options::kDyld:
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ case Options::kKextBundle:
+ throwf("32-bit pointer to symbol %s not supported for x86_64",ref->getTarget().getDisplayName());
+ case Options::kDynamicExecutable:
+ // <rdar://problem/5855588> allow x86_64 main executables to use 32-bit pointers if program loads in load 2GB
+ if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) )
+ throw "32-bit pointer out of range";
+ break;
+ case Options::kStaticExecutable:
+ // <rdar://problem/5855588> allow x86_64 mach_kernel to truncate pointers
+ break;
+ }
+ LittleEndian::set32(*((uint32_t*)fixUp), (uint32_t)displacement);
+ }
+ }
+ 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::kPointerDiff24:
+ displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset());
+ if ( (displacement > kSixteenMegLimit) || (displacement < 0) )
+ throwf("24-bit pointer diff out of range in %s", inAtom->getDisplayName());
+ temp = LittleEndian::get32(*((uint32_t*)fixUp));
+ temp &= 0xFF000000;
+ temp |= (displacement & 0x00FFFFFF);
+ LittleEndian::set32(*((uint32_t*)fixUp), temp);
+ break;
+ case x86_64::kSectionOffset24:
+ displacement = ref->getTarget().getSectionOffset();
+ if ( (displacement > kSixteenMegLimit) || (displacement < 0) )
+ throwf("24-bit pointer diff out of range in %s", inAtom->getDisplayName());
+ temp = LittleEndian::get32(*((uint32_t*)fixUp));
+ temp &= 0xFF000000;
+ temp |= (displacement & 0x00FFFFFF);
+ LittleEndian::set32(*((uint32_t*)fixUp), temp);
+ break;
+ case x86_64::kPCRel32GOTLoad:
+ case x86_64::kPCRel32GOTLoadWeakImport:
+ // if GOT entry was optimized away, change movq instruction to a leaq
+ if ( std::find(fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end(), &(ref->getTarget())) == fAllSynthesizedNonLazyPointers.end() ) {
+ //fprintf(stderr, "GOT for %s optimized away\n", ref->getTarget().getDisplayName());
+ uint8_t* opcodes = (uint8_t*)fixUp;
+ if ( opcodes[-2] != 0x8B )
+ throw "GOT load reloc does not point to a movq instruction";
+ opcodes[-2] = 0x8D;
+ }
+ // fall into general rel32 case
+ case x86_64::kBranchPCRel32WeakImport:
+ case x86_64::kBranchPCRel32:
+ case x86_64::kBranchPCRel8:
+ 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:
+ 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::kAbsoluteSymbol:
+ displacement = (ref->getTarget().getSectionOffset() + (int32_t)ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4);
+ break;
+ case ObjectFile::Atom::kExternalDefinition:
+ case ObjectFile::Atom::kExternalWeakDefinition:
+ if ( fOptions.outputKind() == Options::kKextBundle )
+ displacement = 0;
+ else
+ throwf("codegen problem, can't use rel32 to external symbol %s", ref->getTarget().getDisplayName());
+ 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;
+ case x86_64::kBranchPCRel8:
+ displacement += 3;
+ break;
+ }
+ if ( ref->getKind() == x86_64::kBranchPCRel8 ) {
+ if ( (displacement > 127) || (displacement < (-128)) ) {
+ fprintf(stderr, "branch out of range from %s (%llX) in %s to %s (%llX) in %s\n",
+ inAtom->getDisplayName(), inAtom->getAddress(), inAtom->getFile()->getPath(), ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), ref->getTarget().getFile()->getPath());
+ throw "rel8 out of range";
+ }
+ *((int8_t*)fixUp) = (int8_t)displacement;
+ }
+ else {
+ if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) {
+ fprintf(stderr, "call out of range from %s (%llX) in %s to %s (%llX) in %s\n",
+ inAtom->getDisplayName(), inAtom->getAddress(), inAtom->getFile()->getPath(), ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), ref->getTarget().getFile()->getPath());
+ throw "rel32 out of range";
+ }
+ LittleEndian::set32(*((uint32_t*)fixUp), (int32_t)displacement);
+ }
+ break;
+ case x86_64::kImageOffset32:
+ // offset of target atom from mach_header
+ displacement = ref->getTarget().getAddress() + ref->getTargetOffset() - fMachHeaderAtom->getAddress();
+ LittleEndian::set32(*((uint32_t*)fixUp), (int32_t)displacement);
+ break;
+ case x86_64::kDtraceProbeSite:
+ // change call site to a NOP
+ dtraceProbeSite = (uint8_t*)fixUp;
+ dtraceProbeSite[-1] = 0x90; // 1-byte nop
+ dtraceProbeSite[0] = 0x0F; // 4-byte nop
+ dtraceProbeSite[1] = 0x1F;
+ dtraceProbeSite[2] = 0x40;
+ dtraceProbeSite[3] = 0x00;
+ break;
+ case x86_64::kDtraceIsEnabledSite:
+ // change call site to a clear eax
+ dtraceProbeSite = (uint8_t*)fixUp;
+ dtraceProbeSite[-1] = 0x48; // xorq eax,eax
+ dtraceProbeSite[0] = 0x33;
+ dtraceProbeSite[1] = 0xC0;
+ dtraceProbeSite[2] = 0x90; // 1-byte nop
+ dtraceProbeSite[3] = 0x90; // 1-byte nop
+ break;
+ case x86_64::kDtraceTypeReference:
+ case x86_64::kDtraceProbe:
+ // nothing to fix up
+ 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 = this->makesExternalRelocatableReference(ref->getTarget());
+ 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::kGOTNoFixUp:
+ case x86_64::kFollowOn:
+ case x86_64::kGroupSubordinate:
+ // do nothing
+ break;
+ case x86_64::kPointer:
+ case x86_64::kPointerWeakImport:
+ {
+ if ( external ) {
+ // external relocation ==> 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::kPointer32:
+ {
+ if ( external ) {
+ // external relocation ==> pointer contains addend
+ LittleEndian::set32(*((uint32_t*)fixUp), ref->getTargetOffset());
+ }
+ else {
+ // internal relocation ==> pointer contains target address
+ LittleEndian::set32(*((uint32_t*)fixUp), ref->getTarget().getAddress() + ref->getTargetOffset());
+ }
+ }
+ break;
+ case x86_64::kPointerDiff32:
+ displacement = ref->getTargetOffset() - ref->getFromTargetOffset();
+ if ( ref->getTarget().getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn )
+ displacement += ref->getTarget().getAddress();
+ if ( ref->getFromTarget().getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn )
+ displacement -= ref->getFromTarget().getAddress();
+ LittleEndian::set32(*((uint32_t*)fixUp), displacement);
+ break;
+ case x86_64::kPointerDiff:
+ displacement = ref->getTargetOffset() - ref->getFromTargetOffset();
+ if ( ref->getTarget().getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn )
+ displacement += ref->getTarget().getAddress();
+ if ( ref->getFromTarget().getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn )
+ displacement -= ref->getFromTarget().getAddress();
+ LittleEndian::set64(*fixUp, displacement);
+ break;
+ case x86_64::kBranchPCRel32:
+ case x86_64::kBranchPCRel32WeakImport:
+ case x86_64::kDtraceProbeSite:
+ case x86_64::kDtraceIsEnabledSite:
+ 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::kBranchPCRel8:
+ // 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() + 1);
+ }
+ if ( (displacement > 127) || (displacement < (-128)) ) {
+ //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 "rel8 out of range";
+ }
+ *((int8_t*)fixUp) = (int8_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;
+ case x86_64::kPointerDiff24:
+ throw "internal linker error, kPointerDiff24 can't be encoded into object files";
+ case x86_64::kImageOffset32:
+ throw "internal linker error, kImageOffset32 can't be encoded into object files";
+ case x86_64::kSectionOffset24:
+ throw "internal linker error, kSectionOffset24 can't be encoded into object files";
+ case x86_64::kDtraceTypeReference:
+ case x86_64::kDtraceProbe:
+ // nothing to fix up
+ break;
+ }
+}
+
+template <>
+void Writer<ppc>::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const
+{
+ fixUpReference_powerpc(ref, inAtom, buffer, true);
+}
+
+template <>
+void Writer<ppc64>::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const
+{
+ fixUpReference_powerpc(ref, inAtom, buffer, true);
+}
+
+template <>
+void Writer<ppc>::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const
+{
+ fixUpReference_powerpc(ref, inAtom, buffer, false);
+}
+
+template <>
+void Writer<ppc64>::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const
+{
+ fixUpReference_powerpc(ref, inAtom, buffer, false);
+}
+
+//
+// ppc and ppc64 are mostly the same, so they share a template specialzation
+//
+template <typename A>
+void Writer<A>::fixUpReference_powerpc(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[], bool finalLinkedImage) const
+{
+ uint32_t instruction;
+ uint32_t newInstruction;
+ int64_t displacement;
+ uint64_t targetAddr = 0;
+ uint64_t picBaseAddr;
+ uint16_t instructionLowHalf;
+ uint16_t instructionHighHalf;
+ uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()];
+ pint_t* fixUpPointer = (pint_t*)&buffer[ref->getFixUpOffset()];
+ bool relocateableExternal = false;
+ const int64_t picbase_twoGigLimit = 0x80000000;
+
+ if ( ref->getTargetBinding() != ObjectFile::Reference::kDontBind ) {
+ targetAddr = ref->getTarget().getAddress() + ref->getTargetOffset();
+ if ( finalLinkedImage )
+ relocateableExternal = (relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal);
+ else
+ relocateableExternal = this->makesExternalRelocatableReference(ref->getTarget());
+ }
+
+ switch ( (typename A::ReferenceKinds)(ref->getKind()) ) {
+ case A::kNoFixUp:
+ case A::kFollowOn:
+ case A::kGroupSubordinate:
+ // do nothing
+ break;
+ case A::kPointerWeakImport:
+ case A::kPointer:
+ {
+ //fprintf(stderr, "fixUpReferenceFinal: %s reference to %s\n", this->getDisplayName(), target.getDisplayName());
+ if ( finalLinkedImage && (((SectionInfo*)inAtom->getSection())->fAllLazyPointers
+ || ((SectionInfo*)inAtom->getSection())->fAllLazyDylibPointers) ) {
+ switch (ref->getTarget().getDefinitionKind()) {
+ case ObjectFile::Atom::kExternalDefinition:
+ case ObjectFile::Atom::kExternalWeakDefinition:
+ // prebound lazy pointer to another dylib ==> pointer contains zero
+ P::setP(*fixUpPointer, 0);
+ break;
+ case ObjectFile::Atom::kTentativeDefinition:
+ case ObjectFile::Atom::kRegularDefinition:
+ case ObjectFile::Atom::kWeakDefinition:
+ case ObjectFile::Atom::kAbsoluteSymbol:
+ // prebound lazy pointer to withing this dylib ==> pointer contains address
+ P::setP(*fixUpPointer, targetAddr);
+ break;
+ }
+ }
+ else if ( !finalLinkedImage && ((SectionInfo*)inAtom->getSection())->fAllNonLazyPointers ) {
+ // if INDIRECT_SYMBOL_LOCAL the content is pointer, else it is zero
+ if ( this->indirectSymbolInRelocatableIsLocal(ref) )
+ P::setP(*fixUpPointer, targetAddr);
+ else
+ P::setP(*fixUpPointer, 0);
+ }
+ else if ( relocateableExternal ) {
+ if ( fOptions.prebind() ) {
+ switch (ref->getTarget().getDefinitionKind()) {
+ case ObjectFile::Atom::kExternalDefinition:
+ case ObjectFile::Atom::kExternalWeakDefinition:
+ // prebound external relocation ==> pointer contains addend
+ P::setP(*fixUpPointer, ref->getTargetOffset());
+ break;
+ case ObjectFile::Atom::kTentativeDefinition:
+ case ObjectFile::Atom::kRegularDefinition:
+ case ObjectFile::Atom::kWeakDefinition:
+ // prebound external relocation to internal atom ==> pointer contains target address + addend
+ P::setP(*fixUpPointer, targetAddr);
+ break;
+ case ObjectFile::Atom::kAbsoluteSymbol:
+ break;
+ }
+ }
+ else {
+ // external relocation ==> pointer contains addend
+ P::setP(*fixUpPointer, ref->getTargetOffset());
+ }
+ }
+ else {
+ // internal relocation
+ if ( finalLinkedImage || (ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition) ) {
+ // pointer contains target address
+ //printf("Atom::fixUpReference_powerpc() target.name=%s, target.address=0x%08llX\n", ref->getTarget().getDisplayName(), targetAddr);
+ P::setP(*fixUpPointer, targetAddr);
+ }
+ else {
+ // pointer contains addend
+ P::setP(*fixUpPointer, ref->getTargetOffset());
+ }
+ }
+ }
+ break;
+ case A::kPointerDiff64:
+ P::setP(*fixUpPointer, targetAddr - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) );
+ break;
+ case A::kPointerDiff32:
+ P::E::set32(*fixUp, targetAddr - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) );
+ break;
+ case A::kPointerDiff16:
+ P::E::set16(*((uint16_t*)fixUp), targetAddr - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) );
+ break;
+ case A::kDtraceProbeSite:
+ if ( finalLinkedImage ) {
+ // change call site to a NOP
+ BigEndian::set32(*fixUp, 0x60000000);
+ }
+ else {
+ // set bl instuction to branch to address zero in .o file
+ int64_t displacement = ref->getTargetOffset() - (inAtom->getAddress() + ref->getFixUpOffset());
+ instruction = BigEndian::get32(*fixUp);
+ newInstruction = (instruction & 0xFC000003) | ((uint32_t)displacement & 0x03FFFFFC);
+ BigEndian::set32(*fixUp, newInstruction);
+ }
+ break;
+ case A::kDtraceIsEnabledSite:
+ if ( finalLinkedImage ) {
+ // change call site to a li r3,0
+ BigEndian::set32(*fixUp, 0x38600000);
+ }
+ else {
+ // set bl instuction to branch to address zero in .o file
+ int64_t displacement = ref->getTargetOffset() - (inAtom->getAddress() + ref->getFixUpOffset());
+ instruction = BigEndian::get32(*fixUp);
+ newInstruction = (instruction & 0xFC000003) | ((uint32_t)displacement & 0x03FFFFFC);
+ BigEndian::set32(*fixUp, newInstruction);
+ }
+ break;
+ case A::kBranch24WeakImport:
+ case A::kBranch24:
+ {
+ //fprintf(stderr, "bl fixup to %s at 0x%08llX, ", target.getDisplayName(), target.getAddress());
+ int64_t displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset());
+ if ( relocateableExternal ) {
+ // doing "ld -r" to an external symbol
+ // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target
+ displacement -= ref->getTarget().getAddress();
+ }
+ else {
+ const int64_t bl_eightMegLimit = 0x00FFFFFF;
+ if ( (displacement > bl_eightMegLimit) || (displacement < (-bl_eightMegLimit)) ) {
+ //fprintf(stderr, "bl out of range (%lld max is +/-16M) from %s in %s to %s in %s\n", displacement, this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath());
+ throwf("bl out of range (%lld max is +/-16M) from %s at 0x%08llX in %s of %s to %s at 0x%08llX in %s of %s",
+ displacement, inAtom->getDisplayName(), inAtom->getAddress(), inAtom->getSectionName(), inAtom->getFile()->getPath(),
+ ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), ref->getTarget().getSectionName(), ref->getTarget().getFile()->getPath());
+ }
+ }
+ instruction = BigEndian::get32(*fixUp);
+ newInstruction = (instruction & 0xFC000003) | ((uint32_t)displacement & 0x03FFFFFC);
+ //fprintf(stderr, "bl fixup: 0x%08X -> 0x%08X\n", instruction, newInstruction);
+ BigEndian::set32(*fixUp, newInstruction);
+ }
+ break;
+ case A::kBranch14:
+ {
+ int64_t displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset());
+ if ( relocateableExternal ) {
+ // doing "ld -r" to an external symbol
+ // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target
+ displacement -= ref->getTarget().getAddress();
+ }
+ const int64_t b_sixtyFourKiloLimit = 0x0000FFFF;
+ if ( (displacement > b_sixtyFourKiloLimit) || (displacement < (-b_sixtyFourKiloLimit)) ) {
+ //fprintf(stderr, "bl out of range (%lld max is +/-16M) from %s in %s to %s in %s\n", displacement, this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath());
+ throwf("bcc out of range (%lld max is +/-64K) from %s in %s to %s in %s",
+ displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(),
+ ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath());
+ }
+
+ //fprintf(stderr, "bcc fixup displacement=0x%08llX, atom.addr=0x%08llX, atom.offset=0x%08X\n", displacement, inAtom->getAddress(), (uint32_t)ref->getFixUpOffset());
+ instruction = BigEndian::get32(*fixUp);
+ newInstruction = (instruction & 0xFFFF0003) | ((uint32_t)displacement & 0x0000FFFC);
+ //fprintf(stderr, "bc fixup: 0x%08X -> 0x%08X\n", instruction, newInstruction);
+ BigEndian::set32(*fixUp, newInstruction);
+ }
+ break;
+ case A::kPICBaseLow16:
+ picBaseAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset();
+ displacement = targetAddr - picBaseAddr;
+ if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) )
+ throw "32-bit pic-base out of range";
+ instructionLowHalf = (displacement & 0xFFFF);
+ instruction = BigEndian::get32(*fixUp);
+ newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf;
+ BigEndian::set32(*fixUp, newInstruction);
+ break;
+ case A::kPICBaseLow14:
+ picBaseAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset();
+ displacement = targetAddr - picBaseAddr;
+ if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) )
+ throw "32-bit pic-base out of range";
+ if ( (displacement & 0x3) != 0 )
+ throwf("bad offset (0x%08X) for lo14 instruction pic-base fix-up", (uint32_t)displacement);
+ instructionLowHalf = (displacement & 0xFFFC);
+ instruction = BigEndian::get32(*fixUp);
+ newInstruction = (instruction & 0xFFFF0003) | instructionLowHalf;
+ BigEndian::set32(*fixUp, newInstruction);
+ break;
+ case A::kPICBaseHigh16:
+ picBaseAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset();
+ displacement = targetAddr - picBaseAddr;
+ if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) )
+ throw "32-bit pic-base out of range";
+ instructionLowHalf = displacement >> 16;
+ if ( (displacement & 0x00008000) != 0 )
+ ++instructionLowHalf;
+ instruction = BigEndian::get32(*fixUp);
+ newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf;
+ BigEndian::set32(*fixUp, newInstruction);
+ break;
+ case A::kAbsLow16:
+ if ( relocateableExternal && !finalLinkedImage )
+ targetAddr -= ref->getTarget().getAddress();
+ instructionLowHalf = (targetAddr & 0xFFFF);
+ instruction = BigEndian::get32(*fixUp);
+ newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf;
+ BigEndian::set32(*fixUp, newInstruction);
+ break;
+ case A::kAbsLow14:
+ if ( relocateableExternal && !finalLinkedImage )
+ targetAddr -= ref->getTarget().getAddress();
+ if ( (targetAddr & 0x3) != 0 )
+ throw "bad address for absolute lo14 instruction fix-up";
+ instructionLowHalf = (targetAddr & 0xFFFF);
+ instruction = BigEndian::get32(*fixUp);
+ newInstruction = (instruction & 0xFFFF0003) | instructionLowHalf;
+ BigEndian::set32(*fixUp, newInstruction);
+ break;
+ case A::kAbsHigh16:
+ if ( relocateableExternal ) {
+ if ( finalLinkedImage ) {
+ switch (ref->getTarget().getDefinitionKind()) {
+ case ObjectFile::Atom::kExternalDefinition:
+ case ObjectFile::Atom::kExternalWeakDefinition:
+ throwf("absolute address to symbol %s in a different linkage unit not supported", ref->getTargetName());
+ break;
+ case ObjectFile::Atom::kTentativeDefinition:
+ case ObjectFile::Atom::kRegularDefinition:
+ case ObjectFile::Atom::kWeakDefinition:
+ // use target address
+ break;
+ case ObjectFile::Atom::kAbsoluteSymbol:
+ targetAddr = ref->getTarget().getSectionOffset();
+ break;
+ }
+ }
+ else {
+ targetAddr -= ref->getTarget().getAddress();
+ }
+ }
+ instructionHighHalf = (targetAddr >> 16);
+ instruction = BigEndian::get32(*fixUp);
+ newInstruction = (instruction & 0xFFFF0000) | instructionHighHalf;
+ BigEndian::set32(*fixUp, newInstruction);
+ break;
+ case A::kAbsHigh16AddLow:
+ if ( relocateableExternal ) {
+ if ( finalLinkedImage ) {
+ switch (ref->getTarget().getDefinitionKind()) {
+ case ObjectFile::Atom::kExternalDefinition:
+ case ObjectFile::Atom::kExternalWeakDefinition:
+ throwf("absolute address to symbol %s in a different linkage unit not supported", ref->getTargetName());
+ break;
+ case ObjectFile::Atom::kTentativeDefinition:
+ case ObjectFile::Atom::kRegularDefinition:
+ case ObjectFile::Atom::kWeakDefinition:
+ // use target address
+ break;
+ case ObjectFile::Atom::kAbsoluteSymbol:
+ targetAddr = ref->getTarget().getSectionOffset();
+ break;
+ }
+ }
+ else {
+ targetAddr -= ref->getTarget().getAddress();
+ }
+ }
+ if ( targetAddr & 0x00008000 )
+ targetAddr += 0x00010000;
+ instruction = BigEndian::get32(*fixUp);
+ newInstruction = (instruction & 0xFFFF0000) | (targetAddr >> 16);
+ BigEndian::set32(*fixUp, newInstruction);
+ break;
+ case A::kDtraceTypeReference:
+ case A::kDtraceProbe:
+ // nothing to fix up
+ break;
+ }
+}
+
+template <>
+bool Writer<ppc>::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref)
+{
+ uint8_t kind = ref->getKind();
+ switch ( (ppc::ReferenceKinds)kind ) {
+ case ppc::kNoFixUp:
+ case ppc::kFollowOn:
+ case ppc::kGroupSubordinate:
+ case ppc::kPointer:
+ case ppc::kPointerWeakImport:
+ case ppc::kPointerDiff16:
+ case ppc::kPointerDiff32:
+ case ppc::kPointerDiff64:
+ case ppc::kDtraceProbe:
+ case ppc::kDtraceProbeSite:
+ case ppc::kDtraceIsEnabledSite:
+ case ppc::kDtraceTypeReference:
+ // these are never used to call external functions
+ return false;
+ case ppc::kBranch24:
+ case ppc::kBranch24WeakImport:
+ case ppc::kBranch14:
+ // these are used to call external functions
+ return true;
+ case ppc::kPICBaseLow16:
+ case ppc::kPICBaseLow14:
+ case ppc::kPICBaseHigh16:
+ case ppc::kAbsLow16:
+ case ppc::kAbsLow14:
+ case ppc::kAbsHigh16:
+ case ppc::kAbsHigh16AddLow:
+ // these are only used to call external functions
+ // in -mlong-branch stubs
+ switch ( ref->getTarget().getDefinitionKind() ) {
+ case ObjectFile::Atom::kExternalDefinition:
+ case ObjectFile::Atom::kExternalWeakDefinition:
+ // if the .o file this atom came from has long-branch stubs,
+ // then assume these instructions in a stub.
+ // Otherwise, these are a direct reference to something (maybe a runtime text reloc)
+ return ( inAtom->getFile()->hasLongBranchStubs() );
+ case ObjectFile::Atom::kTentativeDefinition:
+ case ObjectFile::Atom::kRegularDefinition:
+ case ObjectFile::Atom::kWeakDefinition:
+ case ObjectFile::Atom::kAbsoluteSymbol:
+ return false;
+ }
+ break;
+ }
+ return false;
+}
+
+template <>
+bool Writer<arm>::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref)
+{
+ uint8_t kind = ref->getKind();
+ switch ( (arm::ReferenceKinds)kind ) {
+ case arm::kBranch24:
+ case arm::kBranch24WeakImport:
+ case arm::kThumbBranch22:
+ case arm::kThumbBranch22WeakImport:
+ return true;
+ case arm::kNoFixUp:
+ case arm::kFollowOn:
+ case arm::kGroupSubordinate:
+ case arm::kPointer:
+ case arm::kReadOnlyPointer:
+ case arm::kPointerWeakImport:
+ case arm::kPointerDiff:
+ case arm::kDtraceProbe:
+ case arm::kDtraceProbeSite:
+ case arm::kDtraceIsEnabledSite:
+ case arm::kDtraceTypeReference:
+ return false;
+ }
+ return false;
+}
+
+template <>
+bool Writer<ppc64>::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref)
+{
+ uint8_t kind = ref->getKind();
+ switch ( (ppc64::ReferenceKinds)kind ) {
+ case ppc::kNoFixUp:
+ case ppc::kFollowOn:
+ case ppc::kGroupSubordinate:
+ case ppc::kPointer:
+ case ppc::kPointerWeakImport:
+ case ppc::kPointerDiff16:
+ case ppc::kPointerDiff32:
+ case ppc::kPointerDiff64:
+ case ppc::kPICBaseLow16:
+ case ppc::kPICBaseLow14:
+ case ppc::kPICBaseHigh16:
+ case ppc::kAbsLow16:
+ case ppc::kAbsLow14:
+ case ppc::kAbsHigh16:
+ case ppc::kAbsHigh16AddLow:
+ case ppc::kDtraceProbe:
+ case ppc::kDtraceProbeSite:
+ case ppc::kDtraceIsEnabledSite:
+ case ppc::kDtraceTypeReference:
+ // these are never used to call external functions
+ return false;
+ case ppc::kBranch24:
+ case ppc::kBranch24WeakImport:
+ case ppc::kBranch14:
+ // these are used to call external functions
+ return true;
+ }
+ return false;
+}
+
+template <>
+bool Writer<x86>::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref)
+{
+ uint8_t kind = ref->getKind();
+ return (kind == x86::kPCRel32 || kind == x86::kPCRel32WeakImport);
+}
+
+template <>
+bool Writer<x86_64>::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref)
+{
+ uint8_t kind = ref->getKind();
+ return (kind == x86_64::kBranchPCRel32 || kind == x86_64::kBranchPCRel32WeakImport);
+}
+
+
+template <>
+bool Writer<ppc>::weakImportReferenceKind(uint8_t kind)
+{
+ return (kind == ppc::kBranch24WeakImport || kind == ppc::kPointerWeakImport);
+}
+
+template <>
+bool Writer<ppc64>::weakImportReferenceKind(uint8_t kind)
+{
+ return (kind == ppc64::kBranch24WeakImport || kind == ppc64::kPointerWeakImport);
+}
+
+template <>
+bool Writer<x86>::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 <>
+bool Writer<arm>::weakImportReferenceKind(uint8_t kind)
+{
+ return (kind == arm::kBranch24WeakImport || kind == arm::kThumbBranch22WeakImport ||
+ kind == arm::kPointerWeakImport);
+}
+
+template <>
+bool Writer<ppc>::GOTReferenceKind(uint8_t kind)
+{
+ return false;
+}
+
+template <>
+bool Writer<ppc64>::GOTReferenceKind(uint8_t kind)
+{
+ return false;
+}
+
+template <>
+bool Writer<x86>::GOTReferenceKind(uint8_t kind)
+{
+ 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:
+ case x86_64::kGOTNoFixUp:
+ return true;
+ }
+ return false;
+}
+
+template <>
+bool Writer<arm>::GOTReferenceKind(uint8_t kind)
+{
+ return false;
+}
+
+template <>
+bool Writer<ppc>::optimizableGOTReferenceKind(uint8_t kind)
+{
+ return false;
+}
+
+template <>
+bool Writer<ppc64>::optimizableGOTReferenceKind(uint8_t kind)
+{
+ return false;
+}
+
+template <>
+bool Writer<x86>::optimizableGOTReferenceKind(uint8_t kind)
+{
+ return false;
+}
+
+template <>
+bool Writer<x86_64>::optimizableGOTReferenceKind(uint8_t kind)
+{
+ switch ( kind ) {
+ case x86_64::kPCRel32GOTLoad:
+ case x86_64::kPCRel32GOTLoadWeakImport:
+ return true;
+ }
+ return false;
+}
+
+template <>
+bool Writer<arm>::optimizableGOTReferenceKind(uint8_t kind)
+{
+ return false;
+}
+
+// 64-bit architectures never need module table, 32-bit sometimes do for backwards compatiblity
+template <typename A> bool Writer<A>::needsModuleTable() {return fOptions.needsModuleTable(); }
+template <> bool Writer<ppc64>::needsModuleTable() { return false; }
+template <> bool Writer<x86_64>::needsModuleTable() { return false; }
+
+
+template <typename A>
+void Writer<A>::optimizeDylibReferences()
+{
+ //fprintf(stderr, "original ordinals table:\n");
+ //for (std::map<class ObjectFile::Reader*, uint32_t>::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) {
+ // fprintf(stderr, "%u <== %p/%s\n", it->second, it->first, it->first->getPath());
+ //}
+ // find unused dylibs that can be removed
+ std::map<uint32_t, ObjectFile::Reader*> ordinalToReader;
+ std::map<ObjectFile::Reader*, ObjectFile::Reader*> readerAliases;
+ for (std::map<ObjectFile::Reader*, uint32_t>::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) {
+ ObjectFile::Reader* reader = it->first;
+ std::map<ObjectFile::Reader*, ObjectFile::Reader*>::iterator aliasPos = fLibraryAliases.find(reader);
+ if ( aliasPos != fLibraryAliases.end() ) {
+ // already noticed that this reader has same install name as another reader
+ readerAliases[reader] = aliasPos->second;
+ }
+ else if ( !reader->providedExportAtom() && (reader->implicitlyLinked() || reader->deadStrippable() || fOptions.deadStripDylibs()) ) {
+ // this reader can be optimized away
+ it->second = 0xFFFFFFFF;
+ typename std::map<class ObjectFile::Reader*, class DylibLoadCommandsAtom<A>* >::iterator pos = fLibraryToLoadCommand.find(reader);
+ if ( pos != fLibraryToLoadCommand.end() )
+ pos->second->optimizeAway();
+ }
+ else {
+ // mark this reader as using it ordinal
+ std::map<uint32_t, ObjectFile::Reader*>::iterator pos = ordinalToReader.find(it->second);
+ if ( pos == ordinalToReader.end() )
+ ordinalToReader[it->second] = reader;
+ else
+ readerAliases[reader] = pos->second;
+ }
+ }
+ // renumber ordinals (depends on iterator walking in ordinal order)
+ // all LC_LAZY_LOAD_DYLIB load commands must have highest ordinals
+ uint32_t newOrdinal = 0;
+ for (std::map<uint32_t, ObjectFile::Reader*>::iterator it = ordinalToReader.begin(); it != ordinalToReader.end(); ++it) {
+ if ( it->first <= fLibraryToOrdinal.size() ) {
+ if ( ! it->second->isLazyLoadedDylib() )
+ fLibraryToOrdinal[it->second] = ++newOrdinal;
+ }
+ }
+ for (std::map<uint32_t, ObjectFile::Reader*>::iterator it = ordinalToReader.begin(); it != ordinalToReader.end(); ++it) {
+ if ( it->first <= fLibraryToOrdinal.size() ) {
+ if ( it->second->isLazyLoadedDylib() ) {
+ fLibraryToOrdinal[it->second] = ++newOrdinal;
+ }
+ }
+ }
+
+ // <rdar://problem/5504954> linker does not error when dylib ordinal exceeds 250
+ if ( (newOrdinal >= MAX_LIBRARY_ORDINAL) && (fOptions.nameSpace() == Options::kTwoLevelNameSpace) )
+ throwf("two level namespace mach-o files can link with at most %d dylibs, this link would use %d dylibs", MAX_LIBRARY_ORDINAL, newOrdinal);
+
+ // add aliases (e.g. -lm points to libSystem.dylib)
+ for (std::map<ObjectFile::Reader*, ObjectFile::Reader*>::iterator it = readerAliases.begin(); it != readerAliases.end(); ++it) {
+ fLibraryToOrdinal[it->first] = fLibraryToOrdinal[it->second];
+ }
+
+ //fprintf(stderr, "new ordinals table:\n");
+ //for (std::map<class ObjectFile::Reader*, uint32_t>::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) {
+ // fprintf(stderr, "%u <== %p/%s\n", it->second, it->first, it->first->getPath());
+ //}
+}
+
+
+template <>
+void Writer<arm>::scanForAbsoluteReferences()
+{
+ // arm codegen never has absolute references. FIXME: Is this correct?
+}
+
+template <>
+void Writer<x86_64>::scanForAbsoluteReferences()
+{
+ // x86_64 codegen never has absolute references
+}
+
+template <>
+void Writer<x86>::scanForAbsoluteReferences()
+{
+ // when linking -pie verify there are no absolute addressing, unless -read_only_relocs is also used
+ if ( fOptions.positionIndependentExecutable() && !fOptions.allowTextRelocs() ) {
+ 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 x86::kAbsolute32:
+ throwf("cannot link -pie: -mdynamic-no-pic codegen found in %s from %s", atom->getDisplayName(), atom->getFile()->getPath());
+ return;
+ }
+ }
+ }
+ }
+}
+
+template <>
+void Writer<ppc>::scanForAbsoluteReferences()
+{
+ // when linking -pie verify there are no absolute addressing, unless -read_only_relocs is also used
+ if ( fOptions.positionIndependentExecutable() && !fOptions.allowTextRelocs() ) {
+ 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 ppc::kAbsLow16:
+ case ppc::kAbsLow14:
+ case ppc::kAbsHigh16:
+ case ppc::kAbsHigh16AddLow:
+ throwf("cannot link -pie: -mdynamic-no-pic codegen found in %s from %s", atom->getDisplayName(), atom->getFile()->getPath());
+ return;
+ }
+ }
+ }
+ }
+}
+
+
+// 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 -mdynamic-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(4096);
+ strcpy(fPadSegmentInfo->fName, "__4GBFILL");
+ fPageZeroAtom->setSize(0x1000);
+ return;
+ }
+ }
+ }
+ }
+}
+
+
+template <typename A>
+void Writer<A>::insertDummyStubs()
+{
+ // only needed for x86
+}
+
+template <>
+void Writer<x86>::insertDummyStubs()
+{
+ // any 5-byte stubs that cross a 32-byte cache line may update incorrectly
+ std::vector<class StubAtom<x86>*> betterStubs;
+ for (std::vector<class StubAtom<x86>*>::iterator it=fAllSynthesizedStubs.begin(); it != fAllSynthesizedStubs.end(); it++) {
+ switch (betterStubs.size() % 64 ) {
+ case 12:// stub would occupy 0x3C->0x41
+ case 25:// stub would occupy 0x7D->0x82
+ case 38:// stub would occupy 0xBE->0xC3
+ case 51:// stub would occupy 0xFF->0x04
+ betterStubs.push_back(new StubAtom<x86>(*this, *((ObjectFile::Atom*)NULL), false)); //pad with dummy stub
+ break;
+ }
+ betterStubs.push_back(*it);
+ }
+ // replace
+ fAllSynthesizedStubs.clear();
+ fAllSynthesizedStubs.insert(fAllSynthesizedStubs.begin(), betterStubs.begin(), betterStubs.end());
+}
+
+
+template <typename A>
+void Writer<A>::synthesizeKextGOT()
+{
+ // walk every atom and reference
+ 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->getTargetBinding()) {
+ case ObjectFile::Reference::kUnboundByName:
+ case ObjectFile::Reference::kDontBind:
+ break;
+ case ObjectFile::Reference::kBoundByName:
+ case ObjectFile::Reference::kBoundDirectly:
+ ObjectFile::Atom& target = ref->getTarget();
+ // create GOT slots (non-lazy pointers) as needed
+ if ( this->GOTReferenceKind(ref->getKind()) ) {
+ bool useGOT = ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal );
+ // if this GOT usage cannot be optimized away then make a GOT enry
+ if ( ! this->optimizableGOTReferenceKind(ref->getKind()) )
+ useGOT = true;
+ if ( useGOT ) {
+ ObjectFile::Atom* nlp = NULL;
+ std::map<ObjectFile::Atom*,ObjectFile::Atom*>::iterator pos = fGOTMap.find(&target);
+ if ( pos == fGOTMap.end() ) {
+ nlp = new NonLazyPointerAtom<A>(*this, target);
+ fGOTMap[&target] = nlp;
+ }
+ else {
+ nlp = pos->second;
+ }
+ // alter reference to use non lazy pointer instead
+ ref->setTarget(*nlp, ref->getTargetOffset());
+ }
+ }
+ // build map of which symbols need weak importing
+ if ( (target.getDefinitionKind() == ObjectFile::Atom::kExternalDefinition)
+ || (target.getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) {
+ if ( this->weakImportReferenceKind(ref->getKind()) ) {
+ fWeakImportMap[&target] = true;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ // add non-lazy pointers to fAllAtoms
+ if ( fAllSynthesizedNonLazyPointers.size() != 0 ) {
+ ObjectFile::Section* curSection = NULL;
+ ObjectFile::Atom* prevAtom = NULL;
+ bool inserted = false;
+ for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) {
+ ObjectFile::Atom* atom = *it;
+ ObjectFile::Section* nextSection = atom->getSection();
+ if ( nextSection != curSection ) {
+ if ( (prevAtom != NULL) && (strcmp(prevAtom->getSectionName(), "__data") == 0) ) {
+ // found end of __data section, insert lazy pointers here
+ fAllAtoms->insert(it, fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end());
+ inserted = true;
+ break;
+ }
+ curSection = nextSection;
+ }
+ prevAtom = atom;
+ }
+ if ( !inserted ) {
+ throw "can't insert non-lazy pointers, __data section not found";
+ }
+ }
+
+}
+
+
+template <typename A>
+void Writer<A>::synthesizeStubs()
+{
+ switch ( fOptions.outputKind() ) {
+ case Options::kObjectFile:
+ case Options::kPreload:
+ // these output kinds never have stubs
+ return;
+ case Options::kKextBundle:
+ // new kext need a synthesized GOT only
+ synthesizeKextGOT();
+ return;
+ case Options::kStaticExecutable:
+ case Options::kDyld:
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ case Options::kDynamicExecutable:
+ // try to synthesize stubs for these
+ break;
+ }
+
+ // walk every atom and reference
+ 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->getTargetBinding()) {
+ case ObjectFile::Reference::kUnboundByName:
+ case ObjectFile::Reference::kDontBind:
+ break;
+ case ObjectFile::Reference::kBoundByName:
+ case ObjectFile::Reference::kBoundDirectly:
+ ObjectFile::Atom& target = ref->getTarget();
+ // build map of which symbols need weak importing
+ if ( (target.getDefinitionKind() == ObjectFile::Atom::kExternalDefinition)
+ || (target.getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) {
+ bool weakImport = this->weakImportReferenceKind(ref->getKind());
+ // <rdar://problem/5633081> Obj-C Symbols in Leopard Can't Be Weak Linked
+ // dyld in Mac OS X 10.3 and earlier need N_WEAK_REF bit set on undefines to objc symbols
+ // in dylibs that are weakly linked.
+ if ( (ref->getKind() == A::kNoFixUp) && (strncmp(target.getName(), ".objc_class_name_", 17) == 0) ) {
+ typename std::map<class ObjectFile::Reader*, class DylibLoadCommandsAtom<A>* >::iterator pos;
+ pos = fLibraryToLoadCommand.find(target.getFile());
+ if ( pos != fLibraryToLoadCommand.end() ) {
+ if ( pos->second->linkedWeak() )
+ weakImport = true;
+ }
+ }
+ // <rdar://problem/6186838> -weak_library no longer forces uses to be weak_import
+ if ( fForcedWeakImportReaders.count(target.getFile()) != 0 ) {
+ fWeakImportMap[&target] = true;
+ weakImport = true;
+ }
+
+ std::map<const ObjectFile::Atom*,bool>::iterator pos = fWeakImportMap.find(&target);
+ if ( pos == fWeakImportMap.end() ) {
+ // target not in fWeakImportMap, so add
+ fWeakImportMap[&target] = weakImport;
+ }
+ else {
+ // target in fWeakImportMap, check for weakness mismatch
+ if ( pos->second != weakImport ) {
+ // found mismatch
+ switch ( fOptions.weakReferenceMismatchTreatment() ) {
+ case Options::kWeakReferenceMismatchError:
+ throwf("mismatching weak references for symbol: %s", target.getName());
+ case Options::kWeakReferenceMismatchWeak:
+ pos->second = true;
+ break;
+ case Options::kWeakReferenceMismatchNonWeak:
+ pos->second = false;
+ break;
+ }
+ }
+ }
+ // update if we use a weak_import or a strong import from this dylib
+ if ( fWeakImportMap[&target] )
+ fDylibReadersWithWeakImports.insert(target.getFile());
+ else
+ fDylibReadersWithNonWeakImports.insert(target.getFile());
+ }
+ // create stubs as needed
+ if ( this->stubableReference(atom, ref)
+ && (ref->getTargetOffset() == 0)
+ && this->relocationNeededInFinalLinkedImage(target) == kRelocExternal ) {
+ ObjectFile::Atom* stub = NULL;
+ std::map<const ObjectFile::Atom*,ObjectFile::Atom*>::iterator pos = fStubsMap.find(&target);
+ if ( pos == fStubsMap.end() ) {
+ bool forLazyDylib = false;
+ switch ( target.getDefinitionKind() ) {
+ case ObjectFile::Atom::kRegularDefinition:
+ case ObjectFile::Atom::kWeakDefinition:
+ case ObjectFile::Atom::kAbsoluteSymbol:
+ case ObjectFile::Atom::kTentativeDefinition:
+ break;
+ case ObjectFile::Atom::kExternalDefinition:
+ case ObjectFile::Atom::kExternalWeakDefinition:
+ if ( target.getFile()->isLazyLoadedDylib() )
+ forLazyDylib = true;
+ break;
+ }
+ // just-in-time, create GOT slot to dyld_stub_binder
+ if ( fOptions.makeCompressedDyldInfo() && (fFastStubGOTAtom == NULL) ) {
+ if ( fDyldCompressedHelperAtom == NULL )
+ throw "missing symbol dyld_stub_binder";
+ fFastStubGOTAtom = new NonLazyPointerAtom<A>(*this, *fDyldCompressedHelperAtom);
+ }
+ stub = new StubAtom<A>(*this, target, forLazyDylib);
+ fStubsMap[&target] = stub;
+ }
+ else {
+ stub = pos->second;
+ }
+ // alter reference to use stub instead
+ ref->setTarget(*stub, 0);
+ }
+ else if ( fOptions.usingLazyDylibLinking() && target.getFile()->isLazyLoadedDylib() ) {
+ throwf("illegal reference to %s in lazy loaded dylib from %s in %s",
+ target.getDisplayName(), atom->getDisplayName(),
+ atom->getFile()->getPath());
+ }
+ // create GOT slots (non-lazy pointers) as needed
+ else if ( this->GOTReferenceKind(ref->getKind()) ) {
+ //
+ bool mustUseGOT = ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal );
+ bool useGOT;
+ if ( fBiggerThanTwoGigs ) {
+ // in big images use GOT for all zero fill atoms
+ // this is just a heuristic and may need to be re-examined
+ useGOT = mustUseGOT || ref->getTarget().isZeroFill();
+ }
+ else {
+ // < 2GB image so remove all GOT entries that we can
+ useGOT = mustUseGOT;
+ }
+ // if this GOT usage cannot be optimized away then make a GOT enry
+ if ( ! this->optimizableGOTReferenceKind(ref->getKind()) )
+ useGOT = true;
+ if ( useGOT ) {
+ ObjectFile::Atom* nlp = NULL;
+ std::map<ObjectFile::Atom*,ObjectFile::Atom*>::iterator pos = fGOTMap.find(&target);
+ if ( pos == fGOTMap.end() ) {
+ nlp = new NonLazyPointerAtom<A>(*this, target);
+ fGOTMap[&target] = nlp;
+ }
+ else {
+ nlp = pos->second;
+ }
+ // alter reference to use non lazy pointer instead
+ ref->setTarget(*nlp, ref->getTargetOffset());
+ }
+ }
+ }
+ }
+ }
+
+ // sort stubs
+ std::sort(fAllSynthesizedStubs.begin(), fAllSynthesizedStubs.end(), AtomByNameSorter());
+ std::sort(fAllSynthesizedStubHelpers.begin(), fAllSynthesizedStubHelpers.end(), AtomByNameSorter());
+
+ // add dummy self-modifying stubs (x86 only)
+ if ( ! fOptions.makeCompressedDyldInfo() )
+ this->insertDummyStubs();
+
+ // sort lazy pointers
+ std::sort(fAllSynthesizedLazyPointers.begin(), fAllSynthesizedLazyPointers.end(), AtomByNameSorter());
+ std::sort(fAllSynthesizedLazyDylibPointers.begin(), fAllSynthesizedLazyDylibPointers.end(), AtomByNameSorter());
+
+
+ // add stubs to fAllAtoms
+ if ( fAllSynthesizedStubs.size() != 0 ) {
+ std::vector<ObjectFile::Atom*> textStubs;
+ std::vector<ObjectFile::Atom*> importStubs;
+ for (typename std::vector<class StubAtom<A>*>::iterator sit=fAllSynthesizedStubs.begin(); sit != fAllSynthesizedStubs.end(); ++sit) {
+ ObjectFile::Atom* stubAtom = *sit;
+ if ( strcmp(stubAtom->getSegment().getName(), "__TEXT") == 0 )
+ textStubs.push_back(stubAtom);
+ else
+ importStubs.push_back(stubAtom);
+ }
+ // any helper stubs go right after regular stubs
+ if ( fAllSynthesizedStubHelpers.size() != 0 )
+ textStubs.insert(textStubs.end(), fAllSynthesizedStubHelpers.begin(), fAllSynthesizedStubHelpers.end());
+ // insert text stubs right after __text section
+ ObjectFile::Section* curSection = NULL;
+ ObjectFile::Atom* prevAtom = NULL;
+ for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) {
+ ObjectFile::Atom* atom = *it;
+ ObjectFile::Section* nextSection = atom->getSection();
+ if ( nextSection != curSection ) {
+ if ( (prevAtom != NULL) && (strcmp(prevAtom->getSectionName(), "__text") == 0) ) {
+ // found end of __text section, insert stubs here
+ fAllAtoms->insert(it, textStubs.begin(), textStubs.end());
+ break;
+ }
+ curSection = nextSection;
+ }
+ prevAtom = atom;
+ }
+ if ( importStubs.size() != 0 ) {
+ // insert __IMPORTS stubs right before __LINKEDIT
+ for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) {
+ ObjectFile::Atom* atom = *it;
+ ObjectFile::Section* nextSection = atom->getSection();
+ if ( nextSection != curSection ) {
+ // for i386 where stubs are not in __TEXT segment
+ if ( ((prevAtom != NULL) && (strcmp(prevAtom->getSegment().getName(), "__IMPORT") == 0))
+ || (strcmp(atom->getSegment().getName(), "__LINKEDIT") == 0) ) {
+ // insert stubs at end of __IMPORT segment, or before __LINKEDIT
+ fAllAtoms->insert(it, importStubs.begin(), importStubs.end());
+ break;
+ }
+ curSection = nextSection;
+ }
+ prevAtom = atom;
+ }
+ }
+ }
+
+
+ // add non-lazy pointers to fAllAtoms
+ if ( fAllSynthesizedNonLazyPointers.size() != 0 ) {
+ ObjectFile::Section* curSection = NULL;
+ ObjectFile::Atom* prevAtom = NULL;
+ bool inserted = false;
+ // first try to insert at end of __nl_symbol_ptr
+ for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) {
+ ObjectFile::Atom* atom = *it;
+ ObjectFile::Section* nextSection = atom->getSection();
+ if ( nextSection != curSection ) {
+ if ( (prevAtom != NULL) && (strcmp(prevAtom->getSectionName(), "__nl_symbol_ptr") == 0) ) {
+ // found end of __nl_symbol_ptr section, insert non-lazy pointers at end of it
+ fAllAtoms->insert(it, fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end());
+ inserted = true;
+ break;
+ }
+ curSection = nextSection;
+ }
+ prevAtom = atom;
+ }
+ if ( !inserted ) {
+ // next try to insert after __dyld section
+ for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) {
+ ObjectFile::Atom* atom = *it;
+ ObjectFile::Section* nextSection = atom->getSection();
+ if ( nextSection != curSection ) {
+ if ( strcmp(atom->getSegment().getName(), "__DATA") == 0 ) {
+ const char* prevSectionName = (prevAtom != NULL) ? prevAtom->getSectionName() : "";
+ if ( (strcmp(prevSectionName, "__dyld") != 0)
+ && (strcmp(prevSectionName, "__program_vars") != 0)
+ && (strcmp(prevSectionName, "__mod_init_func") != 0) ) {
+ // found end of __dyld section, insert non-lazy pointers here
+ fAllAtoms->insert(it, fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end());
+ inserted = true;
+ break;
+ }
+ }
+ }
+ prevAtom = atom;
+ }
+ if ( !inserted ) {
+ // might not be any __DATA sections, insert after end of __TEXT
+ for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) {
+ ObjectFile::Atom* atom = *it;
+ ObjectFile::Section* nextSection = atom->getSection();
+ if ( nextSection != curSection ) {
+ if ( (prevAtom != NULL) && (strcmp(prevAtom->getSegment().getName(), "__TEXT") == 0) && (strcmp(atom->getSegment().getName(), "__TEXT") != 0)) {
+ // found end of __TEXT segment, insert non-lazy pointers at end of it
+ fAllAtoms->insert(it, fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end());
+ inserted = true;
+ break;
+ }
+ curSection = nextSection;
+ }
+ prevAtom = atom;
+ }
+ }
+ if ( !inserted )
+ throw "can't insert non-lazy pointers, __dyld section not found";
+ }
+ }
+
+ // add lazy dylib pointers to fAllAtoms
+ if ( fAllSynthesizedLazyDylibPointers.size() != 0 ) {
+ ObjectFile::Section* curSection = NULL;
+ ObjectFile::Atom* prevAtom = NULL;
+ bool inserted = false;
+ for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) {
+ ObjectFile::Atom* atom = *it;
+ ObjectFile::Section* nextSection = atom->getSection();
+ if ( nextSection != curSection ) {
+ if ( (prevAtom != NULL) &&
+ ( (strcmp(prevAtom->getSectionName(), "__dyld") == 0)
+ || (strcmp(prevAtom->getSectionName(), "__program_vars") == 0)
+ || (strcmp(prevAtom->getSectionName(), "__nl_symbol_ptr") == 0) ) ) {
+ // found end of __dyld section, insert lazy pointers here
+ fAllAtoms->insert(it, fAllSynthesizedLazyDylibPointers.begin(), fAllSynthesizedLazyDylibPointers.end());
+ inserted = true;
+ break;
+ }
+ curSection = nextSection;
+ }
+ prevAtom = atom;
+ }
+ if ( !inserted ) {
+ throw "can't insert lazy pointers, __dyld section not found";
+ }
+ }
+
+ // add lazy pointers to fAllAtoms
+ if ( fAllSynthesizedLazyPointers.size() != 0 ) {
+ ObjectFile::Section* curSection = NULL;
+ ObjectFile::Atom* prevAtom = NULL;
+ bool inserted = false;
+ for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) {
+ ObjectFile::Atom* atom = *it;
+ ObjectFile::Section* nextSection = atom->getSection();
+ if ( nextSection != curSection ) {
+ if ( (prevAtom != NULL) &&
+ ( (strcmp(prevAtom->getSectionName(), "__dyld") == 0)
+ || (strcmp(prevAtom->getSectionName(), "__program_vars") == 0)
+ || (strcmp(prevAtom->getSectionName(), "__nl_symbol_ptr") == 0) ) ) {
+ // found end of __dyld section, insert lazy pointers here
+ fAllAtoms->insert(it, fAllSynthesizedLazyPointers.begin(), fAllSynthesizedLazyPointers.end());
+ inserted = true;
+ break;
+ }
+ curSection = nextSection;
+ }
+ prevAtom = atom;
+ }
+ if ( !inserted ) {
+ throw "can't insert lazy pointers, __dyld section not found";
+ }
+ }
+
+
+}
+
+template <typename A>
+void Writer<A>::createSplitSegContent()
+{
+ // build LC_SEGMENT_SPLIT_INFO once all atoms exist
+ if ( fSplitCodeToDataContentAtom != 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->getTargetBinding()) {
+ case ObjectFile::Reference::kUnboundByName:
+ case ObjectFile::Reference::kDontBind:
+ break;
+ case ObjectFile::Reference::kBoundByName:
+ case ObjectFile::Reference::kBoundDirectly:
+ if ( this->segmentsCanSplitApart(*atom, ref->getTarget()) ) {
+ this->addCrossSegmentRef(atom, ref);
+ }
+ break;
+ }
+ }
+ }
+ // bad codegen may cause LC_SEGMENT_SPLIT_INFO to be removed
+ adjustLoadCommandsAndPadding();
+ }
+
+}
+
+
+template <typename A>
+void Writer<A>::synthesizeUnwindInfoTable()
+{
+ if ( fUnwindInfoAtom != NULL ) {
+ // walk every atom and gets its unwind info
+ for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) {
+ ObjectFile::Atom* atom = *it;
+ if ( atom->beginUnwind() == atom->endUnwind() ) {
+ // be sure to mark that we have no unwind info for stuff in the TEXT segment without unwind info
+ if ( strcmp(atom->getSegment().getName(), "__TEXT") == 0 )
+ fUnwindInfoAtom->addUnwindInfo(atom, 0, 0, NULL, NULL, NULL);
+ }
+ else {
+ // atom has unwind
+ for ( ObjectFile::UnwindInfo::iterator uit = atom->beginUnwind(); uit != atom->endUnwind(); ++uit ) {
+ fUnwindInfoAtom->addUnwindInfo(atom, uit->startOffset, uit->unwindInfo, atom->getFDE(), atom->getLSDA(), atom->getPersonalityPointer());
+ }
+ }
+ }
+ }
+}
+
+
+
+template <typename A>
+void Writer<A>::partitionIntoSections()
+{
+ const bool oneSegmentCommand = (fOptions.outputKind() == Options::kObjectFile);
+
+ // for every atom, set its sectionInfo object and section offset
+ // build up fSegmentInfos along the way
+ ObjectFile::Section* curSection = (ObjectFile::Section*)(-1);
+ SectionInfo* currentSectionInfo = NULL;
+ SegmentInfo* currentSegmentInfo = NULL;
+ SectionInfo* cstringSectionInfo = NULL;
+ unsigned int sectionIndex = 1;
+ fSegmentInfos.reserve(8);
+ for (unsigned int i=0; i < fAllAtoms->size(); ++i) {
+ ObjectFile::Atom* atom = (*fAllAtoms)[i];
+ if ( ((atom->getSection() != curSection) || (curSection==NULL))
+ && ((currentSectionInfo == NULL)
+ || (strcmp(atom->getSectionName(),currentSectionInfo->fSectionName) != 0)
+ || (strcmp(atom->getSegment().getName(),currentSectionInfo->fSegmentName) != 0)) ) {
+ if ( oneSegmentCommand ) {
+ if ( currentSegmentInfo == NULL ) {
+ currentSegmentInfo = new SegmentInfo(fOptions.segmentAlignment());
+ currentSegmentInfo->fInitProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE;
+ currentSegmentInfo->fMaxProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE;
+ this->fSegmentInfos.push_back(currentSegmentInfo);
+ }
+ currentSectionInfo = new SectionInfo();
+ strcpy(currentSectionInfo->fSectionName, atom->getSectionName());
+ strcpy(currentSectionInfo->fSegmentName, atom->getSegment().getName());
+ currentSectionInfo->fAlignment = atom->getAlignment().powerOf2;
+ currentSectionInfo->fAllZeroFill = atom->isZeroFill();
+ currentSectionInfo->fVirtualSection = (currentSectionInfo->fSectionName[0] == '.');
+ if ( !currentSectionInfo->fVirtualSection || fEmitVirtualSections )
+ currentSectionInfo->setIndex(sectionIndex++);
+ currentSegmentInfo->fSections.push_back(currentSectionInfo);
+ if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__cstring") == 0) )
+ cstringSectionInfo = currentSectionInfo;
+ }
+ else {
+ if ( (currentSegmentInfo == NULL) || (strcmp(currentSegmentInfo->fName, atom->getSegment().getName()) != 0) ) {
+ currentSegmentInfo = new SegmentInfo(fOptions.segmentAlignment());
+ strcpy(currentSegmentInfo->fName, atom->getSegment().getName());
+ uint32_t initprot = 0;
+ if ( atom->getSegment().isContentReadable() )
+ initprot |= VM_PROT_READ;
+ if ( atom->getSegment().isContentWritable() )
+ initprot |= VM_PROT_WRITE;
+ if ( atom->getSegment().isContentExecutable() )
+ initprot |= VM_PROT_EXECUTE;
+ if ( fOptions.readOnlyx86Stubs() && (strcmp(atom->getSegment().getName(), "__IMPORT") == 0) )
+ initprot &= ~VM_PROT_WRITE; // hack until i386 __pointers section is synthesized by linker
+ currentSegmentInfo->fInitProtection = initprot;
+ if ( initprot == 0 )
+ currentSegmentInfo->fMaxProtection = 0; // pagezero should have maxprot==initprot==0
+ else if ( fOptions.architecture() == CPU_TYPE_ARM )
+ currentSegmentInfo->fMaxProtection = currentSegmentInfo->fInitProtection; // iPhoneOS wants max==init
+ else
+ currentSegmentInfo->fMaxProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE;
+ std::vector<Options::SegmentProtect>& customSegProtections = fOptions.customSegmentProtections();
+ for(std::vector<Options::SegmentProtect>::iterator it = customSegProtections.begin(); it != customSegProtections.end(); ++it) {
+ if ( strcmp(it->name, currentSegmentInfo->fName) == 0 ) {
+ currentSegmentInfo->fInitProtection = it->init;
+ currentSegmentInfo->fMaxProtection = it->max;
+ }
+ }
+ currentSegmentInfo->fBaseAddress = atom->getSegment().getBaseAddress();
+ currentSegmentInfo->fFixedAddress = atom->getSegment().hasFixedAddress();
+ if ( currentSegmentInfo->fFixedAddress && (&(atom->getSegment()) == &Segment::fgStackSegment) )
+ currentSegmentInfo->fIndependentAddress = true;
+ if ( (fOptions.outputKind() == Options::kPreload) && (strcmp(currentSegmentInfo->fName, "__LINKEDIT")==0) )
+ currentSegmentInfo->fHasLoadCommand = false;
+ if ( strcmp(currentSegmentInfo->fName, "__HEADER")==0 )
+ currentSegmentInfo->fHasLoadCommand = false;
+ this->fSegmentInfos.push_back(currentSegmentInfo);
+ }
+ currentSectionInfo = new SectionInfo();
+ currentSectionInfo->fAtoms.reserve(fAllAtoms->size()/4); // reduce reallocations by starting large
+ strcpy(currentSectionInfo->fSectionName, atom->getSectionName());
+ strcpy(currentSectionInfo->fSegmentName, atom->getSegment().getName());
+ currentSectionInfo->fAlignment = atom->getAlignment().powerOf2;
+ // check for -sectalign override
+ std::vector<Options::SectionAlignment>& alignmentOverrides = fOptions.sectionAlignments();
+ for(std::vector<Options::SectionAlignment>::iterator it=alignmentOverrides.begin(); it != alignmentOverrides.end(); ++it) {
+ if ( (strcmp(it->segmentName, currentSectionInfo->fSegmentName) == 0) && (strcmp(it->sectionName, currentSectionInfo->fSectionName) == 0) )
+ currentSectionInfo->fAlignment = it->alignment;
+ }
+ currentSectionInfo->fAllZeroFill = atom->isZeroFill();
+ currentSectionInfo->fVirtualSection = ( currentSectionInfo->fSectionName[0] == '.');
+ if ( !currentSectionInfo->fVirtualSection || fEmitVirtualSections )
+ currentSectionInfo->setIndex(sectionIndex++);
+ currentSegmentInfo->fSections.push_back(currentSectionInfo);
+ }
+ //fprintf(stderr, "new section %s for atom %s\n", atom->getSectionName(), atom->getDisplayName());
+ if ( strcmp(currentSectionInfo->fSectionName, "._load_commands") == 0 ) {
+ fLoadCommandsSection = currentSectionInfo;
+ fLoadCommandsSegment = currentSegmentInfo;
+ }
+ if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__la_symbol_ptr") == 0) )
+ currentSectionInfo->fAllLazyPointers = true;
+ if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__la_sym_ptr2") == 0) )
+ currentSectionInfo->fAllLazyPointers = true;
+ if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__ld_symbol_ptr") == 0) )
+ currentSectionInfo->fAllLazyDylibPointers = true;
+ if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__nl_symbol_ptr") == 0) )
+ currentSectionInfo->fAllNonLazyPointers = true;
+ if ( (strcmp(currentSectionInfo->fSegmentName, "__IMPORT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__pointers") == 0) )
+ currentSectionInfo->fAllNonLazyPointers = true;
+ if ( (fOptions.outputKind() == Options::kDyld) && (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__pointers") == 0) )
+ currentSectionInfo->fAllNonLazyPointers = true;
+ if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__picsymbolstub1") == 0) )
+ currentSectionInfo->fAllStubs = true;
+ if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__symbol_stub1") == 0) )
+ currentSectionInfo->fAllStubs = true;
+ if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__picsymbolstub2") == 0) )
+ currentSectionInfo->fAllStubs = true;
+ if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__symbol_stub") == 0) )
+ currentSectionInfo->fAllStubs = true;
+ if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__picsymbolstub4") == 0) )
+ currentSectionInfo->fAllStubs = true;
+ if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__symbol_stub4") == 0) )
+ currentSectionInfo->fAllStubs = true;
+ if ( (strcmp(currentSectionInfo->fSegmentName, "__IMPORT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__jump_table") == 0) ) {
+ currentSectionInfo->fAllSelfModifyingStubs = true;
+ currentSectionInfo->fAlignment = 6; // force x86 fast stubs to start on 64-byte boundary
+ }
+ if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__stub_helper") == 0) )
+ currentSectionInfo->fAllStubHelpers = true;
+ if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__eh_frame") == 0) )
+ currentSectionInfo->fAlignment = __builtin_ctz(sizeof(pint_t)); // always start CFI info pointer aligned
+ curSection = atom->getSection();
+ if ( currentSectionInfo->fAllNonLazyPointers || currentSectionInfo->fAllLazyPointers || currentSectionInfo->fAllLazyDylibPointers
+ || currentSectionInfo->fAllStubs || currentSectionInfo->fAllSelfModifyingStubs ) {
+ fSymbolTableCommands->needDynamicTable();
+ }
+ }
+ // any non-zero fill atoms make whole section marked not-zero-fill
+ if ( currentSectionInfo->fAllZeroFill && ! atom->isZeroFill() )
+ currentSectionInfo->fAllZeroFill = false;
+ // change section object to be Writer's SectionInfo object
+ atom->setSection(currentSectionInfo);
+ // section alignment is that of a contained atom with the greatest alignment
+ uint8_t atomAlign = atom->getAlignment().powerOf2;
+ if ( currentSectionInfo->fAlignment < atomAlign )
+ currentSectionInfo->fAlignment = atomAlign;
+ // calculate section offset for this atom
+ uint64_t offset = currentSectionInfo->fSize;
+ uint64_t alignment = 1 << atomAlign;
+ uint64_t currentModulus = (offset % alignment);
+ uint64_t requiredModulus = atom->getAlignment().modulus;
+ if ( currentModulus != requiredModulus ) {
+ if ( requiredModulus > currentModulus )
+ offset += requiredModulus-currentModulus;
+ else
+ offset += requiredModulus+alignment-currentModulus;
+ }
+ atom->setSectionOffset(offset);
+ uint64_t curAtomSize = atom->getSize();
+ currentSectionInfo->fSize = offset + curAtomSize;
+ // add atom to section vector
+ currentSectionInfo->fAtoms.push_back(atom);
+ //fprintf(stderr, " adding atom %p %s size=0x%0llX to section %p %s from %s\n", atom, atom->getDisplayName(), atom->getSize(),
+ // currentSectionInfo, currentSectionInfo->fSectionName, atom->getFile()->getPath());
+ // update largest size
+ if ( !currentSectionInfo->fAllZeroFill && (curAtomSize > fLargestAtomSize) )
+ fLargestAtomSize = curAtomSize;
+ }
+ if ( (cstringSectionInfo != NULL) && (cstringSectionInfo->fAlignment > 0) ) {
+ // when merging cstring sections in .o files, all strings need to use the max alignment
+ uint64_t offset = 0;
+ uint64_t cstringAlignment = 1 << cstringSectionInfo->fAlignment;
+ for (std::vector<ObjectFile::Atom*>::iterator it=cstringSectionInfo->fAtoms.begin(); it != cstringSectionInfo->fAtoms.end(); it++) {
+ offset = (offset + (cstringAlignment-1)) & (-cstringAlignment);
+ ObjectFile::Atom* atom = *it;
+ atom->setSectionOffset(offset);
+ offset += atom->getSize();
+ }
+ cstringSectionInfo->fSize = offset;
+ }
+}
+
+
+struct TargetAndOffset { ObjectFile::Atom* atom; uint32_t offset; };
+class TargetAndOffsetComparor
+{
+public:
+ bool operator()(const TargetAndOffset& left, const TargetAndOffset& right) const
+ {
+ if ( left.atom != right.atom )
+ return ( left.atom < right.atom );
+ return ( left.offset < right.offset );
+ }
+};
+
+template <>
+bool Writer<ppc>::addBranchIslands()
+{
+ return this->addPPCBranchIslands();
+}
+
+template <>
+bool Writer<ppc64>::addBranchIslands()
+{
+ return this->addPPCBranchIslands();
+}
+
+template <>
+bool Writer<x86>::addBranchIslands()
+{
+ // x86 branches can reach entire 4G address space, so no need for branch islands
+ return false;
+}
+
+template <>
+bool Writer<x86_64>::addBranchIslands()
+{
+ // x86 branches can reach entire 4G size of largest image
+ return false;
+}
+
+template <>
+bool Writer<arm>::addBranchIslands()
+{
+ // arm branch islands not (yet) supported
+ // you can instead compile with -mlong-call
+ return false;
+}
+
+template <>
+bool Writer<ppc>::isBranch24Reference(uint8_t kind)
+{
+ switch (kind) {
+ case ppc::kBranch24:
+ case ppc::kBranch24WeakImport:
+ return true;
+ }
+ return false;
+}
+
+template <>
+bool Writer<ppc64>::isBranch24Reference(uint8_t kind)
+{
+ switch (kind) {
+ case ppc64::kBranch24:
+ case ppc64::kBranch24WeakImport:
+ return true;
+ }
+ return false;
+}
+
+//
+// PowerPC can do PC relative branches as far as +/-16MB.
+// If a branch target is >16MB then we insert one or more
+// "branch islands" between the branch and its target that
+// allows island hoping to the target.
+//
+// Branch Island Algorithm
+//
+// If the __TEXT segment < 16MB, then no branch islands needed
+// Otherwise, every 14MB into the __TEXT segment a region is
+// added which can contain branch islands. Every out of range
+// bl instruction is checked. If it crosses a region, an island
+// is added to that region with the same target and the bl is
+// adjusted to target the island instead.
+//
+// In theory, if too many islands are added to one region, it
+// could grow the __TEXT enough that other previously in-range
+// bl branches could be pushed out of range. We reduce the
+// probability this could happen by placing the ranges every
+// 15MB which means the region would have to be 1MB (256K islands)
+// before any branches could be pushed out of range.
+//
+template <typename A>
+bool Writer<A>::addPPCBranchIslands()
+{
+ bool log = false;
+ bool result = false;
+ // Can only possibly need branch islands if __TEXT segment > 16M
+ if ( fLoadCommandsSegment->fSize > 16000000 ) {
+ if ( log) fprintf(stderr, "ld: checking for branch islands, __TEXT segment size=%llu\n", fLoadCommandsSegment->fSize);
+ const uint32_t kBetweenRegions = 14*1024*1024; // place regions of islands every 14MB in __text section
+ SectionInfo* textSection = NULL;
+ for (std::vector<SectionInfo*>::iterator it=fLoadCommandsSegment->fSections.begin(); it != fLoadCommandsSegment->fSections.end(); it++) {
+ if ( strcmp((*it)->fSectionName, "__text") == 0 ) {
+ textSection = *it;
+ if ( log) fprintf(stderr, "ld: checking for branch islands, __text section size=%llu\n", textSection->fSize);
+ break;
+ }
+ }
+ const int kIslandRegionsCount = fLoadCommandsSegment->fSize / kBetweenRegions;
+ typedef std::map<TargetAndOffset,ObjectFile::Atom*, TargetAndOffsetComparor> AtomToIsland;
+ AtomToIsland regionsMap[kIslandRegionsCount];
+ std::vector<ObjectFile::Atom*> regionsIslands[kIslandRegionsCount];
+ unsigned int islandCount = 0;
+ if ( log) fprintf(stderr, "ld: will use %u branch island regions\n", kIslandRegionsCount);
+
+ // create islands for branch references that are out of range
+ 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;
+ if ( this->isBranch24Reference(ref->getKind()) ) {
+ ObjectFile::Atom& target = ref->getTarget();
+ int64_t srcAddr = atom->getAddress() + ref->getFixUpOffset();
+ int64_t dstAddr = target.getAddress() + ref->getTargetOffset();
+ int64_t displacement = dstAddr - srcAddr;
+ TargetAndOffset finalTargetAndOffset = { &target, ref->getTargetOffset() };
+ const int64_t kFifteenMegLimit = kBetweenRegions;
+ if ( displacement > kFifteenMegLimit ) {
+ // create forward branch chain
+ ObjectFile::Atom* nextTarget = ⌖
+ uint64_t nextTargetOffset = ref->getTargetOffset();
+ for (int i=kIslandRegionsCount-1; i >=0 ; --i) {
+ AtomToIsland* region = ®ionsMap[i];
+ int64_t islandRegionAddr = kBetweenRegions * (i+1) + textSection->getBaseAddress();
+ if ( (srcAddr < islandRegionAddr) && (islandRegionAddr <= dstAddr) ) {
+ AtomToIsland::iterator pos = region->find(finalTargetAndOffset);
+ if ( pos == region->end() ) {
+ BranchIslandAtom<A>* island = new BranchIslandAtom<A>(*this, target.getDisplayName(), i, *nextTarget, nextTargetOffset);
+ island->setSection(textSection);
+ (*region)[finalTargetAndOffset] = island;
+ if (log) fprintf(stderr, "added island %s to region %d for %s\n", island->getDisplayName(), i, atom->getDisplayName());
+ regionsIslands[i].push_back(island);
+ ++islandCount;
+ nextTarget = island;
+ nextTargetOffset = 0;
+ }
+ else {
+ nextTarget = pos->second;
+ nextTargetOffset = 0;
+ }
+ }
+ }
+ if (log) fprintf(stderr, "using island %s for branch to %s from %s\n", nextTarget->getDisplayName(), target.getDisplayName(), atom->getDisplayName());
+ ref->setTarget(*nextTarget, nextTargetOffset);
+ }
+ else if ( displacement < (-kFifteenMegLimit) ) {
+ // create back branching chain
+ ObjectFile::Atom* prevTarget = ⌖
+ uint64_t prevTargetOffset = ref->getTargetOffset();
+ for (int i=0; i < kIslandRegionsCount ; ++i) {
+ AtomToIsland* region = ®ionsMap[i];
+ int64_t islandRegionAddr = kBetweenRegions * (i+1);
+ if ( (dstAddr <= islandRegionAddr) && (islandRegionAddr < srcAddr) ) {
+ AtomToIsland::iterator pos = region->find(finalTargetAndOffset);
+ if ( pos == region->end() ) {
+ BranchIslandAtom<A>* island = new BranchIslandAtom<A>(*this, target.getDisplayName(), i, *prevTarget, prevTargetOffset);
+ island->setSection(textSection);
+ (*region)[finalTargetAndOffset] = island;
+ if (log) fprintf(stderr, "added back island %s to region %d for %s\n", island->getDisplayName(), i, atom->getDisplayName());
+ regionsIslands[i].push_back(island);
+ ++islandCount;
+ prevTarget = island;
+ prevTargetOffset = 0;
+ }
+ else {
+ prevTarget = pos->second;
+ prevTargetOffset = 0;
+ }
+ }
+ }
+ if (log) fprintf(stderr, "using back island %s for %s\n", prevTarget->getDisplayName(), atom->getDisplayName());
+ ref->setTarget(*prevTarget, prevTargetOffset);
+ }
+ }
+ }
+ }
+
+ // insert islands into __text section and adjust section offsets
+ if ( islandCount > 0 ) {
+ if ( log ) fprintf(stderr, "ld: %u branch islands required in %u regions\n", islandCount, kIslandRegionsCount);
+ std::vector<ObjectFile::Atom*> newAtomList;
+ newAtomList.reserve(textSection->fAtoms.size()+islandCount);
+ uint64_t islandRegionAddr = kBetweenRegions + textSection->getBaseAddress();
+ uint64_t textSectionAlignment = (1 << textSection->fAlignment);
+ int regionIndex = 0;
+ uint64_t atomSlide = 0;
+ uint64_t sectionOffset = 0;
+ for (std::vector<ObjectFile::Atom*>::iterator it=textSection->fAtoms.begin(); it != textSection->fAtoms.end(); it++) {
+ ObjectFile::Atom* atom = *it;
+ if ( (atom->getAddress()+atom->getSize()) > islandRegionAddr ) {
+ uint64_t islandStartOffset = atom->getSectionOffset() + atomSlide;
+ sectionOffset = islandStartOffset;
+ std::vector<ObjectFile::Atom*>* regionIslands = ®ionsIslands[regionIndex];
+ for (std::vector<ObjectFile::Atom*>::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) {
+ ObjectFile::Atom* islandAtom = *rit;
+ newAtomList.push_back(islandAtom);
+ uint64_t alignment = 1 << (islandAtom->getAlignment().powerOf2);
+ sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) );
+ islandAtom->setSectionOffset(sectionOffset);
+ sectionOffset += islandAtom->getSize();
+ }
+ ++regionIndex;
+ islandRegionAddr += kBetweenRegions;
+ uint64_t islandRegionAlignmentBlocks = (sectionOffset - islandStartOffset + textSectionAlignment - 1) / textSectionAlignment;
+ atomSlide += (islandRegionAlignmentBlocks * textSectionAlignment);
+ }
+ newAtomList.push_back(atom);
+ if ( atomSlide != 0 )
+ atom->setSectionOffset(atom->getSectionOffset()+atomSlide);
+ }
+ sectionOffset = textSection->fSize+atomSlide;
+ // put any remaining islands at end of __text section
+ if ( regionIndex < kIslandRegionsCount ) {
+ std::vector<ObjectFile::Atom*>* regionIslands = ®ionsIslands[regionIndex];
+ for (std::vector<ObjectFile::Atom*>::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) {
+ ObjectFile::Atom* islandAtom = *rit;
+ newAtomList.push_back(islandAtom);
+ uint64_t alignment = 1 << (islandAtom->getAlignment().powerOf2);
+ sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) );
+ islandAtom->setSectionOffset(sectionOffset);
+ sectionOffset += islandAtom->getSize();
+ }
+ }
+
+ textSection->fAtoms = newAtomList;
+ textSection->fSize = sectionOffset;
+ result = true;
+ }
+
+ }
+ return result;
+}
+
+
+template <typename A>
+void Writer<A>::adjustLoadCommandsAndPadding()
+{
+ fSegmentCommands->computeSize();
+
+ // recompute load command section offsets
+ uint64_t offset = 0;
+ std::vector<class ObjectFile::Atom*>& loadCommandAtoms = fLoadCommandsSection->fAtoms;
+ const unsigned int atomCount = loadCommandAtoms.size();
+ for (unsigned int i=0; i < atomCount; ++i) {
+ ObjectFile::Atom* atom = loadCommandAtoms[i];
+ uint64_t alignment = 1 << atom->getAlignment().powerOf2;
+ offset = ( (offset+alignment-1) & (-alignment) );
+ atom->setSectionOffset(offset);
+ uint32_t atomSize = atom->getSize();
+ if ( atomSize > fLargestAtomSize )
+ fLargestAtomSize = atomSize;
+ offset += atomSize;
+ fLoadCommandsSection->fSize = offset;
+ }
+
+ std::vector<SectionInfo*>& sectionInfos = fLoadCommandsSegment->fSections;
+ const int sectionCount = sectionInfos.size();
+ uint32_t totalSizeOfTEXTLessHeaderAndLoadCommands = 0;
+ for(int j=0; j < sectionCount; ++j) {
+ SectionInfo* curSection = sectionInfos[j];
+ if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 )
+ break;
+ totalSizeOfTEXTLessHeaderAndLoadCommands += curSection->fSize;
+ }
+ uint64_t paddingSize = 0;
+ if ( fOptions.outputKind() == Options::kDyld ) {
+ // dyld itself has special padding requirements. We want the beginning __text section to start at a stable address
+ paddingSize = 4096 - (totalSizeOfTEXTLessHeaderAndLoadCommands % 4096);
+ }
+ else if ( fOptions.outputKind() == Options::kObjectFile ) {
+ // mach-o .o files need no padding between load commands and first section
+ // but leave enough room that the object file could be signed
+ paddingSize = 32;
+ }
+ else if ( fOptions.outputKind() == Options::kPreload ) {
+ // mach-o MH_PRELOAD files need no padding between load commands and first section
+ paddingSize = 0;
+ }
+ else if ( fOptions.makeEncryptable() ) {
+ // want load commands to end on a page boundary, so __text starts on page boundary
+ paddingSize = 4096 - ((totalSizeOfTEXTLessHeaderAndLoadCommands+fOptions.minimumHeaderPad()) % 4096) + fOptions.minimumHeaderPad();
+ fEncryptionLoadCommand->setStartEncryptionOffset(totalSizeOfTEXTLessHeaderAndLoadCommands+paddingSize);
+ }
+ else {
+ // work backwards from end of segment and lay out sections so that extra room goes to padding atom
+ uint64_t addr = 0;
+ for(int j=sectionCount-1; j >=0; --j) {
+ SectionInfo* curSection = sectionInfos[j];
+ if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 ) {
+ addr -= (fLoadCommandsSection->fSize+fMachHeaderAtom->getSize());
+ paddingSize = addr % fOptions.segmentAlignment();
+ break;
+ }
+ addr -= curSection->fSize;
+ addr = addr & (0 - (1 << curSection->fAlignment));
+ }
+
+ // if command line requires more padding than this
+ uint32_t minPad = fOptions.minimumHeaderPad();
+ if ( fOptions.maxMminimumHeaderPad() ) {
+ // -headerpad_max_install_names means there should be room for every path load command to grow to 1204 bytes
+ uint32_t altMin = fLibraryToOrdinal.size() * MAXPATHLEN;
+ if ( fOptions.outputKind() == Options::kDynamicLibrary )
+ altMin += MAXPATHLEN;
+ if ( altMin > minPad )
+ minPad = altMin;
+ }
+ if ( paddingSize < minPad ) {
+ int extraPages = (minPad - paddingSize + fOptions.segmentAlignment() - 1)/fOptions.segmentAlignment();
+ paddingSize += extraPages * fOptions.segmentAlignment();
+ }
+ }
+
+ // adjust atom size and update section size
+ fHeaderPadding->setSize(paddingSize);
+ for(int j=0; j < sectionCount; ++j) {
+ SectionInfo* curSection = sectionInfos[j];
+ if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 )
+ curSection->fSize = paddingSize;
+ }
+}
+
+static uint64_t segmentAlign(uint64_t addr, uint64_t alignment)
+{
+ return ((addr+alignment-1) & (-alignment));
+}
+
+// assign file offsets and logical address to all segments
+template <typename A>
+void Writer<A>::assignFileOffsets()
+{
+ const bool virtualSectionOccupyAddressSpace = ((fOptions.outputKind() != Options::kObjectFile)
+ && (fOptions.outputKind() != Options::kPreload));
+ bool haveFixedSegments = false;
+ uint64_t fileOffset = 0;
+ uint64_t nextContiguousAddress = fOptions.baseAddress();
+ uint64_t nextReadOnlyAddress = fOptions.baseAddress();
+ uint64_t nextWritableAddress = fOptions.baseWritableAddress();
+
+ // process segments with fixed addresses (-segaddr)
+ for (std::vector<Options::SegmentStart>::iterator it = fOptions.customSegmentAddresses().begin(); it != fOptions.customSegmentAddresses().end(); ++it) {
+ for (std::vector<SegmentInfo*>::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) {
+ SegmentInfo* curSegment = *segit;
+ if ( strcmp(curSegment->fName, it->name) == 0 ) {
+ curSegment->fBaseAddress = it->address;
+ curSegment->fFixedAddress = true;
+ break;
+ }
+ }
+ }
+
+ // process segments with fixed addresses (-seg_page_size)
+ for (std::vector<Options::SegmentSize>::iterator it = fOptions.customSegmentSizes().begin(); it != fOptions.customSegmentSizes().end(); ++it) {
+ for (std::vector<SegmentInfo*>::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) {
+ SegmentInfo* curSegment = *segit;
+ if ( strcmp(curSegment->fName, it->name) == 0 ) {
+ curSegment->fPageSize = it->size;
+ break;
+ }
+ }
+ }
+
+ // Run through the segments and each segment's sections to assign addresses
+ for (std::vector<SegmentInfo*>::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) {
+ SegmentInfo* curSegment = *segit;
+
+ if ( fOptions.splitSeg() ) {
+ if ( curSegment->fInitProtection & VM_PROT_WRITE )
+ nextContiguousAddress = nextWritableAddress;
+ else
+ nextContiguousAddress = nextReadOnlyAddress;
+ }
+
+ if ( fOptions.outputKind() == Options::kPreload ) {
+ if ( strcmp(curSegment->fName, "__HEADER") == 0 )
+ nextContiguousAddress = 0;
+ else if ( strcmp(curSegment->fName, "__TEXT") == 0 )
+ nextContiguousAddress = fOptions.baseAddress();
+ }
+
+ fileOffset = segmentAlign(fileOffset, curSegment->fPageSize);
+ curSegment->fFileOffset = fileOffset;
+
+ // Set the segment base address
+ if ( curSegment->fFixedAddress )
+ haveFixedSegments = true;
+ else
+ curSegment->fBaseAddress = segmentAlign(nextContiguousAddress, curSegment->fPageSize);
+
+ // We've set the segment address, now run through each section.
+ uint64_t address = curSegment->fBaseAddress;
+ SectionInfo* firstZeroFillSection = NULL;
+ SectionInfo* prevSection = NULL;
+
+ std::vector<SectionInfo*>& sectionInfos = curSegment->fSections;
+
+ for (std::vector<SectionInfo*>::iterator it = sectionInfos.begin(); it != sectionInfos.end(); ++it) {
+ SectionInfo* curSection = *it;
+
+ // adjust section address based on alignment
+ uint64_t alignment = 1 << curSection->fAlignment;
+ if ( curSection->fAtoms.size() == 1 ) {
+ // if there is only one atom in section, use modulus for even better layout
+ ObjectFile::Alignment atomAlign = curSection->fAtoms[0]->getAlignment();
+ uint64_t atomAlignP2 = (1 << atomAlign.powerOf2);
+ uint64_t currentModulus = (address % atomAlignP2);
+ if ( currentModulus != atomAlign.modulus ) {
+ if ( atomAlign.modulus > currentModulus )
+ address += atomAlign.modulus-currentModulus;
+ else
+ address += atomAlign.modulus+atomAlignP2-currentModulus;
+ }
+ }
+ else {
+ address = ( (address+alignment-1) & (-alignment) );
+ }
+
+ // adjust file offset to match address
+ if ( prevSection != NULL ) {
+ if ( virtualSectionOccupyAddressSpace || !prevSection->fVirtualSection )
+ fileOffset = (address - prevSection->getBaseAddress()) + prevSection->fFileOffset;
+ else
+ fileOffset = ( (fileOffset+alignment-1) & (-alignment) );
+ }
+
+ // update section info
+ curSection->fFileOffset = fileOffset;
+ curSection->setBaseAddress(address);
+ //fprintf(stderr, "%s %s addr=0x%llX, fileoffset=0x%llX, size=0x%llX\n", curSegment->fName, curSection->fSectionName, address, fileOffset, curSection->fSize);
+
+ // keep track of trailing zero fill sections
+ if ( curSection->fAllZeroFill && (firstZeroFillSection == NULL) )
+ firstZeroFillSection = curSection;
+ if ( !curSection->fAllZeroFill && (firstZeroFillSection != NULL) && (fOptions.outputKind() != Options::kObjectFile) )
+ throwf("zero-fill section %s not at end of segment", curSection->fSectionName);
+
+ // update running pointers
+ if ( virtualSectionOccupyAddressSpace || !curSection->fVirtualSection )
+ address += curSection->fSize;
+ fileOffset += curSection->fSize;
+
+ // sanity check size of 32-bit binaries
+ if ( address > maxAddress() )
+ throwf("section %s exceeds 4GB limit", curSection->fSectionName);
+
+ // update segment info
+ curSegment->fFileSize = fileOffset - curSegment->fFileOffset;
+ curSegment->fSize = curSegment->fFileSize;
+ prevSection = curSection;
+ }
+
+ if ( fOptions.outputKind() == Options::kObjectFile ) {
+ // don't page align .o files
+ }
+ else {
+ // optimize trailing zero-fill sections to not occupy disk space
+ if ( firstZeroFillSection != NULL ) {
+ curSegment->fFileSize = firstZeroFillSection->fFileOffset - curSegment->fFileOffset;
+ fileOffset = firstZeroFillSection->fFileOffset;
+ }
+ // page align segment size
+ curSegment->fFileSize = segmentAlign(curSegment->fFileSize, curSegment->fPageSize);
+ curSegment->fSize = segmentAlign(curSegment->fSize, curSegment->fPageSize);
+ if ( !curSegment->fIndependentAddress && (curSegment->fBaseAddress >= nextContiguousAddress) ) {
+ nextContiguousAddress = segmentAlign(curSegment->fBaseAddress+curSegment->fSize, curSegment->fPageSize);
+ fileOffset = segmentAlign(fileOffset, curSegment->fPageSize);
+ if ( curSegment->fInitProtection & VM_PROT_WRITE )
+ nextWritableAddress = nextContiguousAddress;
+ else
+ nextReadOnlyAddress = nextContiguousAddress;
+ }
+ }
+ //fprintf(stderr, "end of seg %s, fileoffset=0x%llX, nextContiguousAddress=0x%llX\n", curSegment->fName, fileOffset, nextContiguousAddress);
+ }
+
+ // check for segment overlaps caused by user specified fixed segments (e.g. __PAGEZERO, __UNIXSTACK)
+ if ( haveFixedSegments ) {
+ int segCount = fSegmentInfos.size();
+ for(int i=0; i < segCount; ++i) {
+ SegmentInfo* segment1 = fSegmentInfos[i];
+
+ for(int j=0; j < segCount; ++j) {
+ if ( i != j ) {
+ SegmentInfo* segment2 = fSegmentInfos[j];
+
+ if ( segment1->fBaseAddress < segment2->fBaseAddress ) {
+ if ( (segment1->fBaseAddress+segment1->fSize) > segment2->fBaseAddress )
+ throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)",
+ segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize);
+ }
+ else if ( segment1->fBaseAddress > segment2->fBaseAddress ) {
+ if ( (segment2->fBaseAddress+segment2->fSize) > segment1->fBaseAddress )
+ throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)",
+ segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize);
+ }
+ else 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;
+ }
+ }
+
+ // record size of encrypted part of __TEXT segment
+ if ( fOptions.makeEncryptable() ) {
+ for (std::vector<SegmentInfo*>::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) {
+ SegmentInfo* curSegment = *segit;
+ if ( strcmp(curSegment->fName, "__TEXT") == 0 ) {
+ fEncryptionLoadCommand->setEndEncryptionOffset(curSegment->fFileSize);
+ break;
+ }
+ }
+ }
+
+}
+
+template <typename A>
+void Writer<A>::adjustLinkEditSections()
+{
+ // link edit content is always in last segment
+ SegmentInfo* lastSeg = fSegmentInfos[fSegmentInfos.size()-1];
+ unsigned int firstLinkEditSectionIndex = 0;
+ while ( strcmp(lastSeg->fSections[firstLinkEditSectionIndex]->fSegmentName, "__LINKEDIT") != 0 )
+ ++firstLinkEditSectionIndex;
+
+ const unsigned int linkEditSectionCount = 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 < linkEditSectionCount; ++i) {
+ std::vector<class ObjectFile::Atom*>& atoms = lastSeg->fSections[i]->fAtoms;
+ // adjust section address based on alignment
+ uint64_t sectionAlignment = 1 << lastSeg->fSections[i]->fAlignment;
+ uint64_t pad = ((address+sectionAlignment-1) & (-sectionAlignment)) - address;
+ address += pad;
+ fileOffset += pad; // adjust file offset to match address
+ lastSeg->fSections[i]->setBaseAddress(address);
+ if ( strcmp(lastSeg->fSections[i]->fSectionName, "._absolute") == 0 )
+ lastSeg->fSections[i]->setBaseAddress(0);
+ lastSeg->fSections[i]->fFileOffset = fileOffset;
+ uint64_t sectionOffset = 0;
+ for (unsigned int j=0; j < atoms.size(); ++j) {
+ ObjectFile::Atom* atom = atoms[j];
+ uint64_t alignment = 1 << atom->getAlignment().powerOf2;
+ sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) );
+ atom->setSectionOffset(sectionOffset);
+ uint64_t size = atom->getSize();
+ sectionOffset += 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;
+ }
+ if ( fOptions.outputKind() == Options::kObjectFile ) {
+ //lastSeg->fBaseAddress = 0;
+ //lastSeg->fSize = lastSeg->fSections[firstLinkEditSectionIndex]->
+ //lastSeg->fFileOffset = 0;
+ //lastSeg->fFileSize =
+ }
+ else {
+ lastSeg->fFileSize = fileOffset - lastSeg->fFileOffset;
+ lastSeg->fSize = (address - lastSeg->fBaseAddress+4095) & (-4096);
+ }
+}
+
+
+template <typename A>
+ObjectFile::Atom::Scope MachHeaderAtom<A>::getScope() const
+{
+ switch ( fWriter.fOptions.outputKind() ) {
+ case Options::kDynamicExecutable:
+ case Options::kStaticExecutable:
+ return ObjectFile::Atom::scopeGlobal;
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ case Options::kDyld:
+ case Options::kObjectFile:
+ case Options::kPreload:
+ case Options::kKextBundle:
+ return ObjectFile::Atom::scopeLinkageUnit;
+ }
+ throw "unknown header type";
+}
+
+template <typename A>
+ObjectFile::Atom::SymbolTableInclusion MachHeaderAtom<A>::getSymbolTableInclusion() const
+{
+ switch ( fWriter.fOptions.outputKind() ) {
+ case Options::kDynamicExecutable:
+ return ObjectFile::Atom::kSymbolTableInAndNeverStrip;
+ case Options::kStaticExecutable:
+ return ObjectFile::Atom::kSymbolTableInAsAbsolute;
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ case Options::kDyld:
+ return ObjectFile::Atom::kSymbolTableIn;
+ case Options::kObjectFile:
+ case Options::kPreload:
+ case Options::kKextBundle:
+ return ObjectFile::Atom::kSymbolTableNotIn;
+ }
+ throw "unknown header type";
+}
+
+template <typename A>
+const char* MachHeaderAtom<A>::getName() const
+{
+ switch ( fWriter.fOptions.outputKind() ) {
+ case Options::kDynamicExecutable:
+ case Options::kStaticExecutable:
+ return "__mh_execute_header";
+ case Options::kDynamicLibrary:
+ return "__mh_dylib_header";
+ case Options::kDynamicBundle:
+ return "__mh_bundle_header";
+ case Options::kObjectFile:
+ case Options::kPreload:
+ case Options::kKextBundle:
+ return NULL;
+ case Options::kDyld:
+ return "__mh_dylinker_header";
+ }
+ throw "unknown header type";
+}
+
+template <typename A>
+const char* MachHeaderAtom<A>::getDisplayName() const
+{
+ switch ( fWriter.fOptions.outputKind() ) {
+ case Options::kDynamicExecutable:
+ case Options::kStaticExecutable:
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ case Options::kDyld:
+ return this->getName();
+ case Options::kObjectFile:
+ case Options::kPreload:
+ case Options::kKextBundle:
+ return "mach header";
+ }
+ throw "unknown header type";
+}
+
+template <typename A>
+void MachHeaderAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ // get file type
+ uint32_t fileType = 0;
+ switch ( fWriter.fOptions.outputKind() ) {
+ case Options::kDynamicExecutable:
+ case Options::kStaticExecutable:
+ fileType = MH_EXECUTE;
+ break;
+ case Options::kDynamicLibrary:
+ fileType = MH_DYLIB;
+ break;
+ case Options::kDynamicBundle:
+ fileType = MH_BUNDLE;
+ break;
+ case Options::kObjectFile:
+ fileType = MH_OBJECT;
+ break;
+ case Options::kDyld:
+ fileType = MH_DYLINKER;
+ break;
+ case Options::kPreload:
+ fileType = MH_PRELOAD;
+ break;
+ case Options::kKextBundle:
+ fileType = MH_KEXT_BUNDLE;
+ break;
+ }
+
+ // get flags
+ uint32_t flags = 0;
+ if ( fWriter.fOptions.outputKind() == Options::kObjectFile ) {
+ if ( fWriter.fCanScatter )
+ flags = MH_SUBSECTIONS_VIA_SYMBOLS;
+ }
+ else {
+ if ( fWriter.fOptions.outputKind() == Options::kStaticExecutable ) {
+ flags |= MH_NOUNDEFS;
+ }
+ else if ( fWriter.fOptions.outputKind() == Options::kPreload ) {
+ flags |= MH_NOUNDEFS;
+ if ( fWriter.fOptions.positionIndependentExecutable() )
+ flags |= MH_PIE;
+ }
+ else {
+ flags = MH_DYLDLINK;
+ if ( fWriter.fOptions.bindAtLoad() )
+ flags |= MH_BINDATLOAD;
+ switch ( fWriter.fOptions.nameSpace() ) {
+ case Options::kTwoLevelNameSpace:
+ flags |= MH_TWOLEVEL | MH_NOUNDEFS;
+ break;
+ case Options::kFlatNameSpace:
+ break;
+ case Options::kForceFlatNameSpace:
+ flags |= MH_FORCE_FLAT;
+ break;
+ }
+ bool hasWeakDefines = fWriter.fHasWeakExports;
+ if ( fWriter.fRegularDefAtomsThatOverrideADylibsWeakDef->size() != 0 ) {
+ for(std::set<const ObjectFile::Atom*>::iterator it = fWriter.fRegularDefAtomsThatOverrideADylibsWeakDef->begin();
+ it != fWriter.fRegularDefAtomsThatOverrideADylibsWeakDef->end(); ++it) {
+ if ( fWriter.shouldExport(**it) ) {
+ hasWeakDefines = true;
+ break;
+ }
+ }
+ }
+ if ( hasWeakDefines )
+ flags |= MH_WEAK_DEFINES;
+ if ( fWriter.fReferencesWeakImports || fWriter.fHasWeakExports )
+ flags |= MH_BINDS_TO_WEAK;
+ if ( fWriter.fOptions.prebind() )
+ flags |= MH_PREBOUND;
+ if ( fWriter.fOptions.splitSeg() )
+ flags |= MH_SPLIT_SEGS;
+ if ( (fWriter.fOptions.outputKind() == Options::kDynamicLibrary) && fWriter.fNoReExportedDylibs )
+ flags |= MH_NO_REEXPORTED_DYLIBS;
+ if ( fWriter.fOptions.positionIndependentExecutable() )
+ flags |= MH_PIE;
+ if ( fWriter.fOptions.markAutoDeadStripDylib() )
+ flags |= MH_DEAD_STRIPPABLE_DYLIB;
+ }
+ if ( fWriter.fOptions.hasExecutableStack() )
+ flags |= MH_ALLOW_STACK_EXECUTION;
+ if ( fWriter.fOptions.readerOptions().fRootSafe )
+ flags |= MH_ROOT_SAFE;
+ if ( fWriter.fOptions.readerOptions().fSetuidSafe )
+ flags |= MH_SETUID_SAFE;
+ }
+
+ // get commands info
+ uint32_t commandsSize = 0;
+ uint32_t commandsCount = 0;
+
+ std::vector<class ObjectFile::Atom*>& loadCommandAtoms = fWriter.fLoadCommandsSection->fAtoms;
+ for (std::vector<ObjectFile::Atom*>::iterator it=loadCommandAtoms.begin(); it != loadCommandAtoms.end(); it++) {
+ ObjectFile::Atom* atom = *it;
+ commandsSize += atom->getSize();
+ // segment and symbol table atoms can contain more than one load command
+ if ( atom == fWriter.fSegmentCommands )
+ commandsCount += fWriter.fSegmentCommands->commandCount();
+ else if ( atom == fWriter.fSymbolTableCommands )
+ commandsCount += fWriter.fSymbolTableCommands->commandCount();
+ else if ( atom->getSize() != 0 )
+ ++commandsCount;
+ }
+
+ // fill out mach_header
+ macho_header<typename A::P>* mh = (macho_header<typename A::P>*)buffer;
+ setHeaderInfo(*mh);
+ mh->set_filetype(fileType);
+ mh->set_ncmds(commandsCount);
+ mh->set_sizeofcmds(commandsSize);
+ mh->set_flags(flags);
+}
+
+template <>
+void MachHeaderAtom<ppc>::setHeaderInfo(macho_header<ppc::P>& header) const
+{
+ header.set_magic(MH_MAGIC);
+ header.set_cputype(CPU_TYPE_POWERPC);
+ header.set_cpusubtype(fWriter.fCpuConstraint);
+}
+
+template <>
+void MachHeaderAtom<ppc64>::setHeaderInfo(macho_header<ppc64::P>& header) const
+{
+ header.set_magic(MH_MAGIC_64);
+ header.set_cputype(CPU_TYPE_POWERPC64);
+ if ( (fWriter.fOptions.outputKind() == Options::kDynamicExecutable) && (fWriter.fOptions.macosxVersionMin() >= ObjectFile::ReaderOptions::k10_5) )
+ header.set_cpusubtype(CPU_SUBTYPE_POWERPC_ALL | 0x80000000);
+ else
+ header.set_cpusubtype(CPU_SUBTYPE_POWERPC_ALL);
+ header.set_reserved(0);
+}
+
+template <>
+void MachHeaderAtom<x86>::setHeaderInfo(macho_header<x86::P>& header) const
+{
+ header.set_magic(MH_MAGIC);
+ header.set_cputype(CPU_TYPE_I386);
+ 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);
+ if ( (fWriter.fOptions.outputKind() == Options::kDynamicExecutable) && (fWriter.fOptions.macosxVersionMin() >= ObjectFile::ReaderOptions::k10_5) )
+ header.set_cpusubtype(CPU_SUBTYPE_X86_64_ALL | 0x80000000);
+ else
+ header.set_cpusubtype(CPU_SUBTYPE_X86_64_ALL);
+ header.set_reserved(0);
+}
+
+template <>
+void MachHeaderAtom<arm>::setHeaderInfo(macho_header<arm::P>& header) const
+{
+ header.set_magic(MH_MAGIC);
+ header.set_cputype(CPU_TYPE_ARM);
+ header.set_cpusubtype(fWriter.fCpuConstraint);
+}
+
+template <typename A>
+CustomStackAtom<A>::CustomStackAtom(Writer<A>& writer)
+ : WriterAtom<A>(writer, Segment::fgStackSegment)
+{
+ if ( stackGrowsDown() )
+ Segment::fgStackSegment.setBaseAddress(writer.fOptions.customStackAddr() - writer.fOptions.customStackSize());
+ else
+ Segment::fgStackSegment.setBaseAddress(writer.fOptions.customStackAddr());
+}
+
+
+template <> bool CustomStackAtom<ppc>::stackGrowsDown() { return true; }
+template <> bool CustomStackAtom<ppc64>::stackGrowsDown() { return true; }
+template <> bool CustomStackAtom<x86>::stackGrowsDown() { return true; }
+template <> bool CustomStackAtom<x86_64>::stackGrowsDown() { return true; }
+template <> bool CustomStackAtom<arm>::stackGrowsDown() { return true; }
+
+template <typename A>
+void SegmentLoadCommandsAtom<A>::computeSize()
+{
+ uint64_t size = 0;
+ std::vector<SegmentInfo*>& segmentInfos = fWriter.fSegmentInfos;
+ int segCount = 0;
+ for(std::vector<SegmentInfo*>::iterator it = segmentInfos.begin(); it != segmentInfos.end(); ++it) {
+ SegmentInfo* seg = *it;
+ if ( seg->fHasLoadCommand ) {
+ ++segCount;
+ size += sizeof(macho_segment_command<P>);
+ std::vector<SectionInfo*>& sectionInfos = seg->fSections;
+ const int sectionCount = sectionInfos.size();
+ for(int j=0; j < sectionCount; ++j) {
+ if ( fWriter.fEmitVirtualSections || ! sectionInfos[j]->fVirtualSection )
+ size += sizeof(macho_section<P>);
+ }
+ }
+ }
+ fSize = size;
+ fCommandCount = segCount;
+ if ( fWriter.fPadSegmentInfo != NULL ) {
+ ++fCommandCount;
+ fSize += sizeof(macho_segment_command<P>);
+ }
+}
+
+template <>
+uint64_t LoadCommandAtom<ppc>::alignedSize(uint64_t size)
+{
+ return ((size+3) & (-4)); // 4-byte align all load commands for 32-bit mach-o
+}
+
+template <>
+uint64_t LoadCommandAtom<ppc64>::alignedSize(uint64_t size)
+{
+ return ((size+7) & (-8)); // 8-byte align all load commands for 64-bit mach-o
+}
+
+template <>
+uint64_t LoadCommandAtom<x86>::alignedSize(uint64_t size)
+{
+ 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 <>
+uint64_t LoadCommandAtom<arm>::alignedSize(uint64_t size)
+{
+ return ((size+3) & (-4)); // 4-byte align all load commands for 32-bit mach-o
+}
+
+template <typename A>
+void SegmentLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ uint64_t size = this->getSize();
+ const bool oneSegment =( fWriter.fOptions.outputKind() == Options::kObjectFile );
+ bzero(buffer, size);
+ uint8_t* p = buffer;
+ typename std::vector<SegmentInfo*>& segmentInfos = fWriter.fSegmentInfos;
+ for(std::vector<SegmentInfo*>::iterator it = segmentInfos.begin(); it != segmentInfos.end(); ++it) {
+ SegmentInfo* segInfo = *it;
+ if ( ! segInfo->fHasLoadCommand )
+ continue;
+ const int sectionCount = segInfo->fSections.size();
+ macho_segment_command<P>* cmd = (macho_segment_command<P>*)p;
+ cmd->set_cmd(macho_segment_command<P>::CMD);
+ cmd->set_segname(segInfo->fName);
+ cmd->set_vmaddr(segInfo->fBaseAddress);
+ cmd->set_vmsize(oneSegment ? 0 : segInfo->fSize);
+ cmd->set_fileoff(segInfo->fFileOffset);
+ cmd->set_filesize(oneSegment ? 0 : segInfo->fFileSize);
+ cmd->set_maxprot(segInfo->fMaxProtection);
+ cmd->set_initprot(segInfo->fInitProtection);
+ // add sections array
+ macho_section<P>* const sections = (macho_section<P>*)&p[sizeof(macho_segment_command<P>)];
+ unsigned int sectionsEmitted = 0;
+ for (int j=0; j < sectionCount; ++j) {
+ SectionInfo* sectInfo = segInfo->fSections[j];
+ if ( fWriter.fEmitVirtualSections || !sectInfo->fVirtualSection ) {
+ macho_section<P>* sect = §ions[sectionsEmitted++];
+ if ( oneSegment ) {
+ // .o file segment does not cover load commands, so recalc at first real section
+ if ( sectionsEmitted == 1 ) {
+ cmd->set_vmaddr(sectInfo->getBaseAddress());
+ cmd->set_fileoff(sectInfo->fFileOffset);
+ }
+ cmd->set_filesize((sectInfo->fFileOffset+sectInfo->fSize)-cmd->fileoff());
+ cmd->set_vmsize(sectInfo->getBaseAddress() + sectInfo->fSize);
+ }
+ sect->set_sectname(sectInfo->fSectionName);
+ sect->set_segname(sectInfo->fSegmentName);
+ sect->set_addr(sectInfo->getBaseAddress());
+ sect->set_size(sectInfo->fSize);
+ sect->set_offset(sectInfo->fFileOffset);
+ sect->set_align(sectInfo->fAlignment);
+ if ( sectInfo->fRelocCount != 0 ) {
+ sect->set_reloff(sectInfo->fRelocOffset * sizeof(macho_relocation_info<P>) + fWriter.fSectionRelocationsAtom->getFileOffset());
+ sect->set_nreloc(sectInfo->fRelocCount);
+ }
+ if ( sectInfo->fAllZeroFill ) {
+ sect->set_flags(S_ZEROFILL);
+ sect->set_offset(0);
+ }
+ else if ( sectInfo->fAllLazyPointers ) {
+ sect->set_flags(S_LAZY_SYMBOL_POINTERS);
+ sect->set_reserved1(sectInfo->fIndirectSymbolOffset);
+ }
+ else if ( sectInfo->fAllLazyDylibPointers ) {
+ sect->set_flags(S_LAZY_DYLIB_SYMBOL_POINTERS);
+ sect->set_reserved1(sectInfo->fIndirectSymbolOffset);
+ }
+ else if ( sectInfo->fAllNonLazyPointers ) {
+ sect->set_flags(S_NON_LAZY_SYMBOL_POINTERS);
+ sect->set_reserved1(sectInfo->fIndirectSymbolOffset);
+ }
+ else if ( sectInfo->fAllStubs ) {
+ sect->set_flags(S_SYMBOL_STUBS | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS);
+ sect->set_reserved1(sectInfo->fIndirectSymbolOffset);
+ sect->set_reserved2(sectInfo->fSize / sectInfo->fAtoms.size());
+ if ( sectInfo->fHasTextLocalRelocs )
+ sect->set_flags(sect->flags() | S_ATTR_LOC_RELOC);
+ }
+ else if ( sectInfo->fAllSelfModifyingStubs ) {
+ sect->set_flags(S_SYMBOL_STUBS | S_ATTR_SELF_MODIFYING_CODE);
+ sect->set_reserved1(sectInfo->fIndirectSymbolOffset);
+ sect->set_reserved2(sectInfo->fSize / sectInfo->fAtoms.size());
+ }
+ else if ( sectInfo->fAllStubHelpers ) {
+ sect->set_flags(S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS);
+ if ( sectInfo->fHasTextLocalRelocs )
+ sect->set_flags(sect->flags() | S_ATTR_LOC_RELOC);
+ }
+ else if ( sectInfo->fAtoms.at(0)->getContentType() == ObjectFile::Atom::kCStringType ) {
+ sect->set_flags(S_CSTRING_LITERALS);
+ }
+ else if ( sectInfo->fAtoms.at(0)->getContentType() == ObjectFile::Atom::kCFIType ) {
+ sect->set_flags(S_COALESCED | S_ATTR_NO_TOC | S_ATTR_STRIP_STATIC_SYMS);
+ }
+ else if ( (strcmp(sectInfo->fSectionName, "__mod_init_func") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) {
+ sect->set_flags(S_MOD_INIT_FUNC_POINTERS);
+ }
+ else if ( (strcmp(sectInfo->fSectionName, "__mod_term_func") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) {
+ sect->set_flags(S_MOD_TERM_FUNC_POINTERS);
+ }
+ else if ( (strcmp(sectInfo->fSectionName, "__textcoal_nt") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) {
+ sect->set_flags(S_COALESCED);
+ }
+ else if ( (strcmp(sectInfo->fSectionName, "__const_coal") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) {
+ sect->set_flags(S_COALESCED);
+ }
+ else if ( (strcmp(sectInfo->fSectionName, "__interpose") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) {
+ sect->set_flags(S_INTERPOSING);
+ }
+ else if ( (strcmp(sectInfo->fSectionName, "__literal4") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) {
+ sect->set_flags(S_4BYTE_LITERALS);
+ }
+ else if ( (strcmp(sectInfo->fSectionName, "__literal8") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) {
+ sect->set_flags(S_8BYTE_LITERALS);
+ }
+ else if ( (strcmp(sectInfo->fSectionName, "__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 ( (strcmp(sectInfo->fSectionName, "__objc_selrefs") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) {
+ sect->set_flags(S_LITERAL_POINTERS);
+ }
+ else if ( (strcmp(sectInfo->fSectionName, "__cls_refs") == 0) && (strcmp(sectInfo->fSegmentName, "__OBJC") == 0) ) {
+ sect->set_flags(S_LITERAL_POINTERS);
+ }
+ else if ( (strncmp(sectInfo->fSectionName, "__dof_", 6) == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) {
+ sect->set_flags(S_DTRACE_DOF);
+ }
+ else if ( (strncmp(sectInfo->fSectionName, "__dof_", 6) == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) {
+ sect->set_flags(S_DTRACE_DOF);
+ }
+ 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);
+ if ( sectInfo->fHasTextLocalRelocs )
+ sect->set_flags(sect->flags() | S_ATTR_LOC_RELOC);
+ if ( sectInfo->fHasTextExternalRelocs )
+ sect->set_flags(sect->flags() | S_ATTR_EXT_RELOC);
+ }
+ //fprintf(stderr, "section %s flags=0x%08X\n", sectInfo->fSectionName, sect->flags());
+ }
+ }
+ p = &p[sizeof(macho_segment_command<P>) + sectionsEmitted*sizeof(macho_section<P>)];
+ cmd->set_cmdsize(sizeof(macho_segment_command<P>) + sectionsEmitted*sizeof(macho_section<P>));
+ cmd->set_nsects(sectionsEmitted);
+ }
+}
+
+
+template <typename A>
+SymbolTableLoadCommandsAtom<A>::SymbolTableLoadCommandsAtom(Writer<A>& writer)
+ : LoadCommandAtom<A>(writer), fNeedsDynamicSymbolTable(false)
+{
+ bzero(&fSymbolTable, sizeof(macho_symtab_command<P>));
+ bzero(&fDynamicSymbolTable, sizeof(macho_dysymtab_command<P>));
+ switch ( fWriter.fOptions.outputKind() ) {
+ case Options::kDynamicExecutable:
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ case Options::kDyld:
+ case Options::kKextBundle:
+ fNeedsDynamicSymbolTable = true;
+ break;
+ case Options::kObjectFile:
+ case Options::kStaticExecutable:
+ fNeedsDynamicSymbolTable = false;
+ case Options::kPreload:
+ fNeedsDynamicSymbolTable = fWriter.fOptions.positionIndependentExecutable();
+ break;
+ }
+ writer.fSymbolTableCommands = this;
+}
+
+
+
+template <typename A>
+void SymbolTableLoadCommandsAtom<A>::needDynamicTable()
+{
+ fNeedsDynamicSymbolTable = true;
+}
+
+
+template <typename A>
+uint64_t SymbolTableLoadCommandsAtom<A>::getSize() const
+{
+ if ( fNeedsDynamicSymbolTable )
+ return this->alignedSize(sizeof(macho_symtab_command<P>) + sizeof(macho_dysymtab_command<P>));
+ else
+ return this->alignedSize(sizeof(macho_symtab_command<P>));
+}
+
+template <typename A>
+void SymbolTableLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ // build LC_SYMTAB command
+ macho_symtab_command<P>* symbolTableCmd = (macho_symtab_command<P>*)buffer;
+ bzero(symbolTableCmd, sizeof(macho_symtab_command<P>));
+ symbolTableCmd->set_cmd(LC_SYMTAB);
+ symbolTableCmd->set_cmdsize(sizeof(macho_symtab_command<P>));
+ symbolTableCmd->set_nsyms(fWriter.fSymbolTableCount);
+ symbolTableCmd->set_symoff(fWriter.fSymbolTableCount == 0 ? 0 : fWriter.fSymbolTableAtom->getFileOffset());
+ symbolTableCmd->set_stroff(fWriter.fStringsAtom->getSize() == 0 ? 0 : fWriter.fStringsAtom->getFileOffset());
+ symbolTableCmd->set_strsize(fWriter.fStringsAtom->getSize());
+
+ // build LC_DYSYMTAB command
+ if ( fNeedsDynamicSymbolTable ) {
+ macho_dysymtab_command<P>* dynamicSymbolTableCmd = (macho_dysymtab_command<P>*)&buffer[sizeof(macho_symtab_command<P>)];
+ bzero(dynamicSymbolTableCmd, sizeof(macho_dysymtab_command<P>));
+ dynamicSymbolTableCmd->set_cmd(LC_DYSYMTAB);
+ dynamicSymbolTableCmd->set_cmdsize(sizeof(macho_dysymtab_command<P>));
+ dynamicSymbolTableCmd->set_ilocalsym(fWriter.fSymbolTableStabsStartIndex);
+ dynamicSymbolTableCmd->set_nlocalsym(fWriter.fSymbolTableStabsCount + fWriter.fSymbolTableLocalCount);
+ dynamicSymbolTableCmd->set_iextdefsym(fWriter.fSymbolTableExportStartIndex);
+ dynamicSymbolTableCmd->set_nextdefsym(fWriter.fSymbolTableExportCount);
+ dynamicSymbolTableCmd->set_iundefsym(fWriter.fSymbolTableImportStartIndex);
+ dynamicSymbolTableCmd->set_nundefsym(fWriter.fSymbolTableImportCount);
+ if ( fWriter.fModuleInfoAtom != NULL ) {
+ dynamicSymbolTableCmd->set_tocoff(fWriter.fModuleInfoAtom->getTableOfContentsFileOffset());
+ dynamicSymbolTableCmd->set_ntoc(fWriter.fSymbolTableExportCount);
+ dynamicSymbolTableCmd->set_modtaboff(fWriter.fModuleInfoAtom->getModuleTableFileOffset());
+ dynamicSymbolTableCmd->set_nmodtab(1);
+ dynamicSymbolTableCmd->set_extrefsymoff(fWriter.fModuleInfoAtom->getReferencesFileOffset());
+ dynamicSymbolTableCmd->set_nextrefsyms(fWriter.fModuleInfoAtom->getReferencesCount());
+ }
+ dynamicSymbolTableCmd->set_indirectsymoff((fWriter.fIndirectTableAtom == NULL) ? 0 : fWriter.fIndirectTableAtom->getFileOffset());
+ dynamicSymbolTableCmd->set_nindirectsyms((fWriter.fIndirectTableAtom == NULL) ? 0 : fWriter.fIndirectTableAtom->fTable.size());
+ if ( fWriter.fOptions.outputKind() != Options::kObjectFile ) {
+ if ( fWriter.fExternalRelocationsAtom != 0 ) {
+ dynamicSymbolTableCmd->set_extreloff((fWriter.fExternalRelocs.size()==0) ? 0 : fWriter.fExternalRelocationsAtom->getFileOffset());
+ dynamicSymbolTableCmd->set_nextrel(fWriter.fExternalRelocs.size());
+ }
+ if ( fWriter.fLocalRelocationsAtom != 0 ) {
+ dynamicSymbolTableCmd->set_locreloff((fWriter.fInternalRelocs.size()==0) ? 0 : fWriter.fLocalRelocationsAtom->getFileOffset());
+ dynamicSymbolTableCmd->set_nlocrel(fWriter.fInternalRelocs.size());
+ }
+ }
+ }
+}
+
+
+template <typename A>
+unsigned int SymbolTableLoadCommandsAtom<A>::commandCount()
+{
+ return fNeedsDynamicSymbolTable ? 2 : 1;
+}
+
+template <typename A>
+uint64_t DyldLoadCommandsAtom<A>::getSize() const
+{
+ return this->alignedSize(sizeof(macho_dylinker_command<P>) + strlen("/usr/lib/dyld") + 1);
+}
+
+template <typename A>
+void DyldLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ uint64_t size = this->getSize();
+ bzero(buffer, size);
+ macho_dylinker_command<P>* cmd = (macho_dylinker_command<P>*)buffer;
+ if ( fWriter.fOptions.outputKind() == Options::kDyld )
+ cmd->set_cmd(LC_ID_DYLINKER);
+ else
+ cmd->set_cmd(LC_LOAD_DYLINKER);
+ cmd->set_cmdsize(this->getSize());
+ cmd->set_name_offset();
+ strcpy((char*)&buffer[sizeof(macho_dylinker_command<P>)], "/usr/lib/dyld");
+}
+
+template <typename A>
+uint64_t AllowableClientLoadCommandsAtom<A>::getSize() const
+{
+ return this->alignedSize(sizeof(macho_sub_client_command<P>) + strlen(this->clientString) + 1);
+}
+
+template <typename A>
+void AllowableClientLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ uint64_t size = this->getSize();
+
+ bzero(buffer, size);
+ macho_sub_client_command<P>* cmd = (macho_sub_client_command<P>*)buffer;
+ cmd->set_cmd(LC_SUB_CLIENT);
+ cmd->set_cmdsize(size);
+ cmd->set_client_offset();
+ strcpy((char*)&buffer[sizeof(macho_sub_client_command<P>)], this->clientString);
+
+}
+
+template <typename A>
+uint64_t DylibLoadCommandsAtom<A>::getSize() const
+{
+ if ( fOptimizedAway ) {
+ return 0;
+ }
+ else {
+ const char* path = fInfo.reader->getInstallPath();
+ return this->alignedSize(sizeof(macho_dylib_command<P>) + strlen(path) + 1);
+ }
+}
+
+template <typename A>
+void DylibLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ if ( fOptimizedAway )
+ return;
+ uint64_t size = this->getSize();
+ bzero(buffer, size);
+ const char* path = fInfo.reader->getInstallPath();
+ macho_dylib_command<P>* cmd = (macho_dylib_command<P>*)buffer;
+ // <rdar://problem/5529626> If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB
+ bool autoWeakLoadDylib = ( (fWriter.fDylibReadersWithWeakImports.count(fInfo.reader) > 0)
+ && (fWriter.fDylibReadersWithNonWeakImports.count(fInfo.reader) == 0) );
+ if ( fInfo.options.fLazyLoad )
+ cmd->set_cmd(LC_LAZY_LOAD_DYLIB);
+ else if ( fInfo.options.fWeakImport || autoWeakLoadDylib )
+ cmd->set_cmd(LC_LOAD_WEAK_DYLIB);
+ else if ( fInfo.options.fReExport && (fWriter.fOptions.macosxVersionMin() >= ObjectFile::ReaderOptions::k10_5) )
+ cmd->set_cmd(LC_REEXPORT_DYLIB);
+ else
+ cmd->set_cmd(LC_LOAD_DYLIB);
+ cmd->set_cmdsize(this->getSize());
+ cmd->set_timestamp(2); // needs to be some constant value that is different than DylibIDLoadCommandsAtom uses
+ cmd->set_current_version(fInfo.reader->getCurrentVersion());
+ cmd->set_compatibility_version(fInfo.reader->getCompatibilityVersion());
+ cmd->set_name_offset();
+ strcpy((char*)&buffer[sizeof(macho_dylib_command<P>)], path);
+}
+
+
+
+template <typename A>
+uint64_t DylibIDLoadCommandsAtom<A>::getSize() const
+{
+ return this->alignedSize(sizeof(macho_dylib_command<P>) + strlen(fWriter.fOptions.installPath()) + 1);
+}
+
+template <typename A>
+void DylibIDLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ uint64_t size = this->getSize();
+ bzero(buffer, size);
+ macho_dylib_command<P>* cmd = (macho_dylib_command<P>*)buffer;
+ cmd->set_cmd(LC_ID_DYLIB);
+ cmd->set_cmdsize(this->getSize());
+ cmd->set_name_offset();
+ cmd->set_timestamp(1); // needs to be some constant value that is different than DylibLoadCommandsAtom uses
+ cmd->set_current_version(fWriter.fOptions.currentVersion());
+ cmd->set_compatibility_version(fWriter.fOptions.compatibilityVersion());
+ strcpy((char*)&buffer[sizeof(macho_dylib_command<P>)], fWriter.fOptions.installPath());
+}
+
+
+template <typename A>
+void RoutinesLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ uint64_t initAddr = fWriter.getAtomLoadAddress(fWriter.fEntryPoint);
+ if (fWriter.fEntryPoint->isThumb())
+ initAddr |= 1ULL;
+ bzero(buffer, sizeof(macho_routines_command<P>));
+ macho_routines_command<P>* cmd = (macho_routines_command<P>*)buffer;
+ cmd->set_cmd(macho_routines_command<P>::CMD);
+ cmd->set_cmdsize(this->getSize());
+ cmd->set_init_address(initAddr);
+}
+
+
+template <typename A>
+uint64_t SubUmbrellaLoadCommandsAtom<A>::getSize() const
+{
+ return this->alignedSize(sizeof(macho_sub_umbrella_command<P>) + strlen(fName) + 1);
+}
+
+template <typename A>
+void SubUmbrellaLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ uint64_t size = this->getSize();
+ bzero(buffer, size);
+ macho_sub_umbrella_command<P>* cmd = (macho_sub_umbrella_command<P>*)buffer;
+ cmd->set_cmd(LC_SUB_UMBRELLA);
+ cmd->set_cmdsize(this->getSize());
+ cmd->set_sub_umbrella_offset();
+ strcpy((char*)&buffer[sizeof(macho_sub_umbrella_command<P>)], fName);
+}
+
+template <typename A>
+void UUIDLoadCommandAtom<A>::generate()
+{
+ switch ( fWriter.fOptions.getUUIDMode() ) {
+ case Options::kUUIDNone:
+ fEmit = false;
+ break;
+ case Options::kUUIDRandom:
+ ::uuid_generate_random(fUUID);
+ fEmit = true;
+ break;
+ case Options::kUUIDContent:
+ bzero(fUUID, 16);
+ fEmit = true;
+ break;
+ }
+}
+
+template <typename A>
+void UUIDLoadCommandAtom<A>::setContent(const uint8_t uuid[16])
+{
+ memcpy(fUUID, uuid, 16);
+}
+
+template <typename A>
+void UUIDLoadCommandAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ if (fEmit) {
+ uint64_t size = this->getSize();
+ bzero(buffer, size);
+ macho_uuid_command<P>* cmd = (macho_uuid_command<P>*)buffer;
+ cmd->set_cmd(LC_UUID);
+ cmd->set_cmdsize(this->getSize());
+ cmd->set_uuid((uint8_t*)fUUID);
+ }
+}
+
+
+template <typename A>
+uint64_t SubLibraryLoadCommandsAtom<A>::getSize() const
+{
+ return this->alignedSize(sizeof(macho_sub_library_command<P>) + fNameLength + 1);
+}
+
+template <typename A>
+void SubLibraryLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ uint64_t size = this->getSize();
+ bzero(buffer, size);
+ macho_sub_library_command<P>* cmd = (macho_sub_library_command<P>*)buffer;
+ cmd->set_cmd(LC_SUB_LIBRARY);
+ cmd->set_cmdsize(this->getSize());
+ cmd->set_sub_library_offset();
+ strncpy((char*)&buffer[sizeof(macho_sub_library_command<P>)], fNameStart, fNameLength);
+ buffer[sizeof(macho_sub_library_command<P>)+fNameLength] = '\0';
+}
+
+template <typename A>
+uint64_t UmbrellaLoadCommandsAtom<A>::getSize() const
+{
+ return this->alignedSize(sizeof(macho_sub_framework_command<P>) + strlen(fName) + 1);
+}
+
+template <typename A>
+void UmbrellaLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ uint64_t size = this->getSize();
+ bzero(buffer, size);
+ macho_sub_framework_command<P>* cmd = (macho_sub_framework_command<P>*)buffer;
+ cmd->set_cmd(LC_SUB_FRAMEWORK);
+ cmd->set_cmdsize(this->getSize());
+ cmd->set_umbrella_offset();
+ strcpy((char*)&buffer[sizeof(macho_sub_framework_command<P>)], fName);
+}
+
+template <>
+uint64_t ThreadsLoadCommandsAtom<ppc>::getSize() const
+{
+ return this->alignedSize(16 + 40*4); // base size + PPC_THREAD_STATE_COUNT * 4
+}
+
+template <>
+uint64_t ThreadsLoadCommandsAtom<ppc64>::getSize() const
+{
+ return this->alignedSize(16 + 76*4); // base size + PPC_THREAD_STATE64_COUNT * 4
+}
+
+template <>
+uint64_t ThreadsLoadCommandsAtom<x86>::getSize() const
+{
+ 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);
+}
+
+// We should be picking it up from a header
+template <>
+uint64_t ThreadsLoadCommandsAtom<arm>::getSize() const
+{
+ return this->alignedSize(16 + 17 * 4); // base size + ARM_THREAD_STATE_COUNT * 4
+}
+
+template <>
+void ThreadsLoadCommandsAtom<ppc>::copyRawContent(uint8_t buffer[]) const
+{
+ uint64_t size = this->getSize();
+ uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint);
+ bzero(buffer, size);
+ macho_thread_command<ppc::P>* cmd = (macho_thread_command<ppc::P>*)buffer;
+ cmd->set_cmd(LC_UNIXTHREAD);
+ cmd->set_cmdsize(size);
+ cmd->set_flavor(1); // PPC_THREAD_STATE
+ cmd->set_count(40); // PPC_THREAD_STATE_COUNT;
+ cmd->set_thread_register(0, start);
+ if ( fWriter.fOptions.hasCustomStack() )
+ cmd->set_thread_register(3, fWriter.fOptions.customStackAddr()); // r1
+}
+
+
+template <>
+void ThreadsLoadCommandsAtom<ppc64>::copyRawContent(uint8_t buffer[]) const
+{
+ uint64_t size = this->getSize();
+ uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint);
+ bzero(buffer, size);
+ macho_thread_command<ppc64::P>* cmd = (macho_thread_command<ppc64::P>*)buffer;
+ cmd->set_cmd(LC_UNIXTHREAD);
+ cmd->set_cmdsize(size);
+ cmd->set_flavor(5); // PPC_THREAD_STATE64
+ cmd->set_count(76); // PPC_THREAD_STATE64_COUNT;
+ cmd->set_thread_register(0, start);
+ if ( fWriter.fOptions.hasCustomStack() )
+ cmd->set_thread_register(3, fWriter.fOptions.customStackAddr()); // r1
+}
+
+template <>
+void ThreadsLoadCommandsAtom<x86>::copyRawContent(uint8_t buffer[]) const
+{
+ uint64_t size = this->getSize();
+ uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint);
+ bzero(buffer, size);
+ macho_thread_command<x86::P>* cmd = (macho_thread_command<x86::P>*)buffer;
+ cmd->set_cmd(LC_UNIXTHREAD);
+ cmd->set_cmdsize(size);
+ cmd->set_flavor(1); // i386_THREAD_STATE
+ cmd->set_count(16); // i386_THREAD_STATE_COUNT;
+ cmd->set_thread_register(10, start);
+ if ( fWriter.fOptions.hasCustomStack() )
+ cmd->set_thread_register(7, fWriter.fOptions.customStackAddr()); // esp
+}
+
+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 <>
+void ThreadsLoadCommandsAtom<arm>::copyRawContent(uint8_t buffer[]) const
+{
+ uint64_t size = this->getSize();
+ uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint);
+ if ( fWriter.fEntryPoint->isThumb() )
+ start |= 1ULL;
+ bzero(buffer, size);
+ macho_thread_command<arm::P>* cmd = (macho_thread_command<arm::P>*)buffer;
+ cmd->set_cmd(LC_UNIXTHREAD);
+ cmd->set_cmdsize(size);
+ cmd->set_flavor(1);
+ cmd->set_count(17);
+ cmd->set_thread_register(15, start); // pc
+ if ( fWriter.fOptions.hasCustomStack() )
+ cmd->set_thread_register(13, fWriter.fOptions.customStackAddr()); // FIXME: sp?
+}
+
+template <typename A>
+uint64_t RPathLoadCommandsAtom<A>::getSize() const
+{
+ return this->alignedSize(sizeof(macho_rpath_command<P>) + strlen(fPath) + 1);
+}
+
+template <typename A>
+void RPathLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ uint64_t size = this->getSize();
+ bzero(buffer, size);
+ macho_rpath_command<P>* cmd = (macho_rpath_command<P>*)buffer;
+ cmd->set_cmd(LC_RPATH);
+ cmd->set_cmdsize(this->getSize());
+ cmd->set_path_offset();
+ strcpy((char*)&buffer[sizeof(macho_rpath_command<P>)], fPath);
+}
+
+
+
+template <typename A>
+void EncryptionLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ uint64_t size = this->getSize();
+ bzero(buffer, size);
+ macho_encryption_info_command<P>* cmd = (macho_encryption_info_command<P>*)buffer;
+ cmd->set_cmd(LC_ENCRYPTION_INFO);
+ cmd->set_cmdsize(this->getSize());
+ cmd->set_cryptoff(fStartOffset);
+ cmd->set_cryptsize(fEndOffset-fStartOffset);
+ cmd->set_cryptid(0);
+}
+
+
+
+template <typename A>
+void LoadCommandsPaddingAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ bzero(buffer, fSize);
+}
+
+template <typename A>
+void LoadCommandsPaddingAtom<A>::setSize(uint64_t newSize)
+{
+ fSize = newSize;
+ // this resizing by-passes the way fLargestAtomSize is set, so re-check here
+ if ( fWriter.fLargestAtomSize < newSize )
+ fWriter.fLargestAtomSize = newSize;
+}
+
+template <typename A>
+void UnwindInfoAtom<A>::addUnwindInfo(ObjectFile::Atom* func, uint32_t offset, uint32_t encoding,
+ ObjectFile::Reference* fdeRef, ObjectFile::Reference* lsdaRef,
+ ObjectFile::Atom* personalityPointer)
+{
+ Info info;
+ info.func = func;
+ if ( fdeRef != NULL )
+ info.fde = &fdeRef->getTarget();
+ else
+ info.fde = NULL;
+ if ( lsdaRef != NULL ) {
+ info.lsda = &lsdaRef->getTarget();
+ info.lsdaOffset = lsdaRef->getTargetOffset();
+ }
+ else {
+ info.lsda = NULL;
+ info.lsdaOffset = 0;
+ }
+ info.personalityPointer = personalityPointer;
+ info.encoding = encoding;
+ fInfos.push_back(info);
+ //fprintf(stderr, "addUnwindInfo() encoding=0x%08X, lsda=%p, lsdaOffset=%d, person=%p, func=%s\n",
+ // encoding, info.lsda, info.lsdaOffset, personalityPointer, func->getDisplayName());
+}
+
+template <>
+bool UnwindInfoAtom<x86>::encodingMeansUseDwarf(compact_unwind_encoding_t encoding)
+{
+ return ( (encoding & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_DWARF);
+}
+
+template <>
+bool UnwindInfoAtom<x86_64>::encodingMeansUseDwarf(compact_unwind_encoding_t encoding)
+{
+ return ( (encoding & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF);
+}
+
+template <typename A>
+bool UnwindInfoAtom<A>::encodingMeansUseDwarf(compact_unwind_encoding_t encoding)
+{
+ return false;
+}
+
+
+template <typename A>
+void UnwindInfoAtom<A>::compressDuplicates(std::vector<Info>& uniqueInfos)
+{
+ // build new list removing entries where next function has same encoding
+ uniqueInfos.reserve(fInfos.size());
+ Info last;
+ last.func = NULL;
+ last.lsda = NULL;
+ last.lsdaOffset = 0;
+ last.personalityPointer = NULL;
+ last.encoding = 0xFFFFFFFF;
+ for(typename std::vector<Info>::iterator it=fInfos.begin(); it != fInfos.end(); ++it) {
+ Info& newInfo = *it;
+ bool newNeedsDwarf = encodingMeansUseDwarf(newInfo.encoding);
+ // remove infos which have same encoding and personalityPointer as last one
+ if ( newNeedsDwarf || (newInfo.encoding != last.encoding) || (newInfo.personalityPointer != last.personalityPointer)
+ || (newInfo.lsda != NULL) || (last.lsda != NULL) ) {
+ uniqueInfos.push_back(newInfo);
+ }
+ last = newInfo;
+ }
+ //fprintf(stderr, "compressDuplicates() fInfos.size()=%lu, uniqueInfos.size()=%lu\n", fInfos.size(), uniqueInfos.size());
+}
+
+template <typename A>
+void UnwindInfoAtom<A>::findCommonEncoding(const std::vector<Info>& uniqueInfos, std::map<uint32_t, unsigned int>& commonEncodings)
+{
+ // scan infos to get frequency counts for each encoding
+ std::map<uint32_t, unsigned int> encodingsUsed;
+ unsigned int mostCommonEncodingUsageCount = 0;
+ for(typename std::vector<Info>::const_iterator it=uniqueInfos.begin(); it != uniqueInfos.end(); ++it) {
+ // never put dwarf into common table
+ if ( encodingMeansUseDwarf(it->encoding) )
+ continue;
+ std::map<uint32_t, unsigned int>::iterator pos = encodingsUsed.find(it->encoding);
+ if ( pos == encodingsUsed.end() ) {
+ encodingsUsed[it->encoding] = 1;
+ }
+ else {
+ encodingsUsed[it->encoding] += 1;
+ if ( mostCommonEncodingUsageCount < encodingsUsed[it->encoding] )
+ mostCommonEncodingUsageCount = encodingsUsed[it->encoding];
+ }
+ }
+ // put the most common encodings into the common table, but at most 127 of them
+ for(unsigned int usages=mostCommonEncodingUsageCount; usages > 1; --usages) {
+ for (std::map<uint32_t, unsigned int>::iterator euit=encodingsUsed.begin(); euit != encodingsUsed.end(); ++euit) {
+ if ( euit->second == usages ) {
+ unsigned int size = commonEncodings.size();
+ if ( size < 127 ) {
+ commonEncodings[euit->first] = size;
+ }
+ }
+ }
+ }
+}
+
+template <typename A>
+void UnwindInfoAtom<A>::makeLsdaIndex(const std::vector<Info>& uniqueInfos, std::map<ObjectFile::Atom*, uint32_t>& lsdaIndexOffsetMap)
+{
+ for(typename std::vector<Info>::const_iterator it=uniqueInfos.begin(); it != uniqueInfos.end(); ++it) {
+ lsdaIndexOffsetMap[it->func] = fLSDAIndex.size() * sizeof(macho_unwind_info_section_header_lsda_index_entry<P>);
+ if ( it->lsda != NULL ) {
+ LSDAEntry entry;
+ entry.func = it->func;
+ entry.lsda = it->lsda;
+ entry.lsdaOffset = it->lsdaOffset;
+ fLSDAIndex.push_back(entry);
+ }
+ }
+}
+
+template <typename A>
+void UnwindInfoAtom<A>::makePersonalityIndex(std::vector<Info>& uniqueInfos)
+{
+ for(typename std::vector<Info>::iterator it=uniqueInfos.begin(); it != uniqueInfos.end(); ++it) {
+ if ( it->personalityPointer != NULL ) {
+ std::map<ObjectFile::Atom*, uint32_t>::iterator pos = fPersonalityIndexMap.find(it->personalityPointer);
+ if ( pos == fPersonalityIndexMap.end() ) {
+ const uint32_t nextIndex = fPersonalityIndexMap.size() + 1;
+ fPersonalityIndexMap[it->personalityPointer] = nextIndex;
+ }
+ uint32_t personalityIndex = fPersonalityIndexMap[it->personalityPointer];
+ it->encoding |= (personalityIndex << (__builtin_ctz(UNWIND_PERSONALITY_MASK)) );
+ }
+ }
+}
+
+template <typename A>
+unsigned int UnwindInfoAtom<A>::makeRegularSecondLevelPage(const std::vector<Info>& uniqueInfos, uint32_t pageSize,
+ unsigned int endIndex, uint8_t*& pageEnd)
+{
+ const unsigned int maxEntriesPerPage = (pageSize - sizeof(unwind_info_regular_second_level_page_header))/sizeof(unwind_info_regular_second_level_entry);
+ const unsigned int entriesToAdd = ((endIndex > maxEntriesPerPage) ? maxEntriesPerPage : endIndex);
+ uint8_t* pageStart = pageEnd
+ - entriesToAdd*sizeof(unwind_info_regular_second_level_entry)
+ - sizeof(unwind_info_regular_second_level_page_header);
+ macho_unwind_info_regular_second_level_page_header<P>* page = (macho_unwind_info_regular_second_level_page_header<P>*)pageStart;
+ page->set_kind(UNWIND_SECOND_LEVEL_REGULAR);
+ page->set_entryPageOffset(sizeof(macho_unwind_info_regular_second_level_page_header<P>));
+ page->set_entryCount(entriesToAdd);
+ macho_unwind_info_regular_second_level_entry<P>* entryTable = (macho_unwind_info_regular_second_level_entry<P>*)(pageStart + page->entryPageOffset());
+ for (unsigned int i=0; i < entriesToAdd; ++i) {
+ const Info& info = uniqueInfos[endIndex-entriesToAdd+i];
+ entryTable[i].set_functionOffset(0);
+ entryTable[i].set_encoding(info.encoding);
+ RegFixUp fixup;
+ fixup.contentPointer = (uint8_t*)(&entryTable[i]);
+ fixup.func = info.func;
+ fixup.fde = ( encodingMeansUseDwarf(info.encoding) ? info.fde : NULL );
+ fRegFixUps.push_back(fixup);
+ }
+ //fprintf(stderr, "regular page with %u entries\n", entriesToAdd);
+ pageEnd = pageStart;
+ return endIndex - entriesToAdd;
+}
+
+
+template <typename A>
+unsigned int UnwindInfoAtom<A>::makeCompressedSecondLevelPage(const std::vector<Info>& uniqueInfos,
+ const std::map<uint32_t,unsigned int> commonEncodings,
+ uint32_t pageSize, unsigned int endIndex, uint8_t*& pageEnd)
+{
+ const bool log = false;
+ if (log) fprintf(stderr, "makeCompressedSecondLevelPage(pageSize=%u, endIndex=%u)\n", pageSize, endIndex);
+ // first pass calculates how many compressed entries we could fit in this sized page
+ // keep adding entries to page until:
+ // 1) encoding table plus entry table plus header exceed page size
+ // 2) the file offset delta from the first to last function > 24 bits
+ // 3) custom encoding index reachs 255
+ // 4) run out of uniqueInfos to encode
+ std::map<uint32_t, unsigned int> pageSpecificEncodings;
+ uint32_t space4 = (pageSize - sizeof(unwind_info_compressed_second_level_page_header))/sizeof(uint32_t);
+ std::vector<uint8_t> encodingIndexes;
+ int index = endIndex-1;
+ int entryCount = 0;
+ uint64_t lastEntryAddress = uniqueInfos[index].func->getAddress();
+ bool canDo = true;
+ while ( canDo && (index >= 0) ) {
+ const Info& info = uniqueInfos[index--];
+ // compute encoding index
+ unsigned int encodingIndex;
+ std::map<uint32_t, unsigned int>::const_iterator pos = commonEncodings.find(info.encoding);
+ if ( pos != commonEncodings.end() ) {
+ encodingIndex = pos->second;
+ }
+ else {
+ // no commmon entry, so add one on this page
+ uint32_t encoding = info.encoding;
+ if ( encodingMeansUseDwarf(encoding) ) {
+ // make unique pseudo encoding so this dwarf will gets is own encoding entry slot
+ encoding += (index+1);
+ }
+ std::map<uint32_t, unsigned int>::iterator ppos = pageSpecificEncodings.find(encoding);
+ if ( ppos != pageSpecificEncodings.end() ) {
+ encodingIndex = pos->second;
+ }
+ else {
+ encodingIndex = commonEncodings.size() + pageSpecificEncodings.size();
+ if ( encodingIndex <= 255 ) {
+ pageSpecificEncodings[encoding] = encodingIndex;
+ }
+ else {
+ canDo = false; // case 3)
+ if (log) fprintf(stderr, "end of compressed page with %u entries, %lu custom encodings because too many custom encodings\n",
+ entryCount, pageSpecificEncodings.size());
+ }
+ }
+ }
+ if ( canDo )
+ encodingIndexes.push_back(encodingIndex);
+ // compute function offset
+ uint32_t funcOffsetWithInPage = lastEntryAddress - info.func->getAddress();
+ if ( funcOffsetWithInPage > 0x00FFFF00 ) {
+ // don't use 0x00FFFFFF because addresses may vary after atoms are laid out again
+ canDo = false; // case 2)
+ if (log) fprintf(stderr, "can't use compressed page with %u entries because function offset too big\n", entryCount);
+ }
+ else {
+ ++entryCount;
+ }
+ // check room for entry
+ if ( (pageSpecificEncodings.size()+entryCount) >= space4 ) {
+ canDo = false; // case 1)
+ --entryCount;
+ if (log) fprintf(stderr, "end of compressed page with %u entries because full\n", entryCount);
+ }
+ //if (log) fprintf(stderr, "space4=%d, pageSpecificEncodings.size()=%ld, entryCount=%d\n", space4, pageSpecificEncodings.size(), entryCount);
+ }
+
+ // check for cases where it would be better to use a regular (non-compressed) page
+ const unsigned int compressPageUsed = sizeof(unwind_info_compressed_second_level_page_header)
+ + pageSpecificEncodings.size()*sizeof(uint32_t)
+ + entryCount*sizeof(uint32_t);
+ if ( (compressPageUsed < (pageSize-4) && (index >= 0) ) ) {
+ const int regularEntriesPerPage = (pageSize - sizeof(unwind_info_regular_second_level_page_header))/sizeof(unwind_info_regular_second_level_entry);
+ if ( entryCount < regularEntriesPerPage ) {
+ return makeRegularSecondLevelPage(uniqueInfos, pageSize, endIndex, pageEnd);
+ }
+ }
+
+ // check if we need any padding because adding another entry would take 8 bytes but only have room for 4
+ uint32_t pad = 0;
+ if ( compressPageUsed == (pageSize-4) )
+ pad = 4;
+
+ // second pass fills in page
+ uint8_t* pageStart = pageEnd - compressPageUsed - pad;
+ macho_unwind_info_compressed_second_level_page_header<P>* page = (macho_unwind_info_compressed_second_level_page_header<P>*)pageStart;
+ page->set_kind(UNWIND_SECOND_LEVEL_COMPRESSED);
+ page->set_entryPageOffset(sizeof(macho_unwind_info_compressed_second_level_page_header<P>));
+ page->set_entryCount(entryCount);
+ page->set_encodingsPageOffset(page->entryPageOffset()+entryCount*sizeof(uint32_t));
+ page->set_encodingsCount(pageSpecificEncodings.size());
+ uint32_t* const encodingsArray = (uint32_t*)&pageStart[page->encodingsPageOffset()];
+ // fill in entry table
+ uint32_t* const entiresArray = (uint32_t*)&pageStart[page->entryPageOffset()];
+ ObjectFile::Atom* firstFunc = uniqueInfos[endIndex-entryCount].func;
+ for(unsigned int i=endIndex-entryCount; i < endIndex; ++i) {
+ const Info& info = uniqueInfos[i];
+ uint8_t encodingIndex;
+ if ( encodingMeansUseDwarf(info.encoding) ) {
+ // dwarf entries are always in page specific encodings
+ encodingIndex = pageSpecificEncodings[info.encoding+i];
+ }
+ else {
+ std::map<uint32_t, unsigned int>::const_iterator pos = commonEncodings.find(info.encoding);
+ if ( pos != commonEncodings.end() )
+ encodingIndex = pos->second;
+ else
+ encodingIndex = pageSpecificEncodings[info.encoding];
+ }
+ uint32_t entryIndex = i - endIndex + entryCount;
+ A::P::E::set32(entiresArray[entryIndex], encodingIndex << 24);
+ CompressedFixUp funcStartFixUp;
+ funcStartFixUp.contentPointer = (uint8_t*)(&entiresArray[entryIndex]);
+ funcStartFixUp.func = info.func;
+ funcStartFixUp.fromFunc = firstFunc;
+ fCompressedFixUps.push_back(funcStartFixUp);
+ if ( encodingMeansUseDwarf(info.encoding) ) {
+ CompressedEncodingFixUp dwarfStartFixup;
+ dwarfStartFixup.contentPointer = (uint8_t*)(&encodingsArray[encodingIndex-commonEncodings.size()]);
+ dwarfStartFixup.fde = info.fde;
+ fCompressedEncodingFixUps.push_back(dwarfStartFixup);
+ }
+ }
+ // fill in encodings table
+ for(std::map<uint32_t, unsigned int>::const_iterator it = pageSpecificEncodings.begin(); it != pageSpecificEncodings.end(); ++it) {
+ A::P::E::set32(encodingsArray[it->second-commonEncodings.size()], it->first);
+ }
+
+ if (log) fprintf(stderr, "compressed page with %u entries, %lu custom encodings\n", entryCount, pageSpecificEncodings.size());
+
+ // update pageEnd;
+ pageEnd = pageStart;
+ return endIndex-entryCount; // endIndex for next page
+}
+
+template <> void UnwindInfoAtom<ppc>::generate() { }
+template <> void UnwindInfoAtom<ppc64>::generate() { }
+template <> void UnwindInfoAtom<arm>::generate() { }
+
+
+template <typename A>
+void UnwindInfoAtom<A>::generate()
+{
+ // only generate table if there are functions with unwind info
+ if ( fInfos.size() > 0 ) {
+ // find offset of end of __unwind_info section
+ SectionInfo* unwindSectionInfo = (SectionInfo*)this->getSection();
+
+ // build new list that has proper offsetInImage and remove entries where next function has same encoding
+ std::vector<Info> uniqueInfos;
+ this->compressDuplicates(uniqueInfos);
+
+ // build personality index, update encodings with personality index
+ this->makePersonalityIndex(uniqueInfos);
+ if ( fPersonalityIndexMap.size() > 3 )
+ throw "too many personality routines for compact unwind to encode";
+
+ // put the most common encodings into the common table, but at most 127 of them
+ std::map<uint32_t, unsigned int> commonEncodings;
+ this->findCommonEncoding(uniqueInfos, commonEncodings);
+
+ // build lsda index
+ std::map<ObjectFile::Atom*, uint32_t> lsdaIndexOffsetMap;
+ this->makeLsdaIndex(uniqueInfos, lsdaIndexOffsetMap);
+
+ // calculate worst case size for all unwind info pages when allocating buffer
+ const unsigned int entriesPerRegularPage = (4096-sizeof(unwind_info_regular_second_level_page_header))/sizeof(unwind_info_regular_second_level_entry);
+ const unsigned int pageCount = ((uniqueInfos.size() - 1)/entriesPerRegularPage) + 1;
+ fPagesContentForDelete = (uint8_t*)calloc(pageCount,4096);
+ fPagesSize = 0;
+ if ( fPagesContentForDelete == NULL )
+ throw "could not allocate space for compact unwind info";
+ ObjectFile::Atom* secondLevelFirstFuncs[pageCount*3];
+ uint8_t* secondLevelPagesStarts[pageCount*3];
+
+ // make last second level page smaller so that all other second level pages can be page aligned
+ uint32_t maxLastPageSize = unwindSectionInfo->fFileOffset % 4096;
+ uint32_t tailPad = 0;
+ if ( maxLastPageSize < 128 ) {
+ tailPad = maxLastPageSize;
+ maxLastPageSize = 4096;
+ }
+
+ // fill in pages in reverse order
+ unsigned int endIndex = uniqueInfos.size();
+ unsigned int secondLevelPageCount = 0;
+ uint8_t* pageEnd = &fPagesContentForDelete[pageCount*4096];
+ uint32_t pageSize = maxLastPageSize;
+ while ( endIndex > 0 ) {
+ endIndex = makeCompressedSecondLevelPage(uniqueInfos, commonEncodings, pageSize, endIndex, pageEnd);
+ secondLevelPagesStarts[secondLevelPageCount] = pageEnd;
+ secondLevelFirstFuncs[secondLevelPageCount] = uniqueInfos[endIndex].func;
+ ++secondLevelPageCount;
+ pageSize = 4096; // last page can be odd size, make rest up to 4096 bytes in size
+ }
+ fPagesContent = pageEnd;
+ fPagesSize = &fPagesContentForDelete[pageCount*4096] - pageEnd;
+
+ // calculate section layout
+ const uint32_t commonEncodingsArraySectionOffset = sizeof(macho_unwind_info_section_header<P>);
+ const uint32_t commonEncodingsArrayCount = commonEncodings.size();
+ const uint32_t commonEncodingsArraySize = commonEncodingsArrayCount * sizeof(compact_unwind_encoding_t);
+ const uint32_t personalityArraySectionOffset = commonEncodingsArraySectionOffset + commonEncodingsArraySize;
+ const uint32_t personalityArrayCount = fPersonalityIndexMap.size();
+ const uint32_t personalityArraySize = personalityArrayCount * sizeof(uint32_t);
+ const uint32_t indexSectionOffset = personalityArraySectionOffset + personalityArraySize;
+ const uint32_t indexCount = secondLevelPageCount+1;
+ const uint32_t indexSize = indexCount * sizeof(macho_unwind_info_section_header_index_entry<P>);
+ const uint32_t lsdaIndexArraySectionOffset = indexSectionOffset + indexSize;
+ const uint32_t lsdaIndexArrayCount = fLSDAIndex.size();
+ const uint32_t lsdaIndexArraySize = lsdaIndexArrayCount * sizeof(macho_unwind_info_section_header_lsda_index_entry<P>);
+ const uint32_t headerEndSectionOffset = lsdaIndexArraySectionOffset + lsdaIndexArraySize;
+
+
+ // allocate and fill in section header
+ fHeaderSize = headerEndSectionOffset;
+ fHeaderContent = new uint8_t[fHeaderSize];
+ bzero(fHeaderContent, fHeaderSize);
+ macho_unwind_info_section_header<P>* sectionHeader = (macho_unwind_info_section_header<P>*)fHeaderContent;
+ sectionHeader->set_version(UNWIND_SECTION_VERSION);
+ sectionHeader->set_commonEncodingsArraySectionOffset(commonEncodingsArraySectionOffset);
+ sectionHeader->set_commonEncodingsArrayCount(commonEncodingsArrayCount);
+ sectionHeader->set_personalityArraySectionOffset(personalityArraySectionOffset);
+ sectionHeader->set_personalityArrayCount(personalityArrayCount);
+ sectionHeader->set_indexSectionOffset(indexSectionOffset);
+ sectionHeader->set_indexCount(indexCount);
+
+ // copy common encodings
+ uint32_t* commonEncodingsTable = (uint32_t*)&fHeaderContent[commonEncodingsArraySectionOffset];
+ for (std::map<uint32_t, unsigned int>::iterator it=commonEncodings.begin(); it != commonEncodings.end(); ++it)
+ A::P::E::set32(commonEncodingsTable[it->second], it->first);
+
+ // make references for personality entries
+ uint32_t* personalityArray = (uint32_t*)&fHeaderContent[sectionHeader->personalityArraySectionOffset()];
+ for (std::map<ObjectFile::Atom*, unsigned int>::iterator it=fPersonalityIndexMap.begin(); it != fPersonalityIndexMap.end(); ++it) {
+ uint32_t offset = (uint8_t*)&personalityArray[it->second-1] - fHeaderContent;
+ fReferences.push_back(new WriterReference<A>(offset, A::kImageOffset32, it->first));
+ }
+
+ // build first level index and references
+ macho_unwind_info_section_header_index_entry<P>* indexTable = (macho_unwind_info_section_header_index_entry<P>*)&fHeaderContent[indexSectionOffset];
+ for (unsigned int i=0; i < secondLevelPageCount; ++i) {
+ unsigned int reverseIndex = secondLevelPageCount - 1 - i;
+ indexTable[i].set_functionOffset(0);
+ indexTable[i].set_secondLevelPagesSectionOffset(secondLevelPagesStarts[reverseIndex]-fPagesContent+headerEndSectionOffset);
+ indexTable[i].set_lsdaIndexArraySectionOffset(lsdaIndexOffsetMap[secondLevelFirstFuncs[reverseIndex]]+lsdaIndexArraySectionOffset);
+ uint32_t refOffset = (uint8_t*)&indexTable[i] - fHeaderContent;
+ fReferences.push_back(new WriterReference<A>(refOffset, A::kImageOffset32, secondLevelFirstFuncs[reverseIndex]));
+ }
+ indexTable[secondLevelPageCount].set_functionOffset(0);
+ indexTable[secondLevelPageCount].set_secondLevelPagesSectionOffset(0);
+ indexTable[secondLevelPageCount].set_lsdaIndexArraySectionOffset(lsdaIndexArraySectionOffset+lsdaIndexArraySize);
+ fReferences.push_back(new WriterReference<A>((uint8_t*)&indexTable[secondLevelPageCount] - fHeaderContent, A::kImageOffset32,
+ fInfos.back().func, fInfos.back().func->getSize()+1));
+
+ // build lsda references
+ uint32_t lsdaEntrySectionOffset = lsdaIndexArraySectionOffset;
+ for (typename std::vector<LSDAEntry>::iterator it = fLSDAIndex.begin(); it != fLSDAIndex.end(); ++it) {
+ fReferences.push_back(new WriterReference<A>(lsdaEntrySectionOffset, A::kImageOffset32, it->func));
+ fReferences.push_back(new WriterReference<A>(lsdaEntrySectionOffset+4, A::kImageOffset32, it->lsda, it->lsdaOffset));
+ lsdaEntrySectionOffset += sizeof(unwind_info_section_header_lsda_index_entry);
+ }
+
+ // make references for regular second level entries
+ for (typename std::vector<RegFixUp>::iterator it = fRegFixUps.begin(); it != fRegFixUps.end(); ++it) {
+ uint32_t offset = (it->contentPointer - fPagesContent) + fHeaderSize;
+ fReferences.push_back(new WriterReference<A>(offset, A::kImageOffset32, it->func));
+ if ( it->fde != NULL )
+ fReferences.push_back(new WriterReference<A>(offset+4, A::kSectionOffset24, it->fde));
+ }
+ // make references for compressed second level entries
+ for (typename std::vector<CompressedFixUp>::iterator it = fCompressedFixUps.begin(); it != fCompressedFixUps.end(); ++it) {
+ uint32_t offset = (it->contentPointer - fPagesContent) + fHeaderSize;
+ fReferences.push_back(new WriterReference<A>(offset, A::kPointerDiff24, it->func, 0, it->fromFunc, 0));
+ }
+ for (typename std::vector<CompressedEncodingFixUp>::iterator it = fCompressedEncodingFixUps.begin(); it != fCompressedEncodingFixUps.end(); ++it) {
+ uint32_t offset = (it->contentPointer - fPagesContent) + fHeaderSize;
+ fReferences.push_back(new WriterReference<A>(offset, A::kSectionOffset24, it->fde));
+ }
+
+ // update section record with new size
+ unwindSectionInfo->fSize = this->getSize();
+
+ // alter alignment so this section lays out so second level tables are page aligned
+ if ( secondLevelPageCount > 2 )
+ fAlignment = ObjectFile::Alignment(12, (unwindSectionInfo->fFileOffset - this->getSize()) % 4096);
+ }
+
+}
+
+
+
+
+template <typename A>
+void UnwindInfoAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ memcpy(buffer, fHeaderContent, fHeaderSize);
+ memcpy(&buffer[fHeaderSize], fPagesContent, fPagesSize);
+}
+
+
+
+template <typename A>
+uint64_t LinkEditAtom<A>::getFileOffset() const
+{
+ return ((SectionInfo*)this->getSection())->fFileOffset + this->getSectionOffset();
+}
+
+
+template <typename A>
+uint64_t SectionRelocationsLinkEditAtom<A>::getSize() const
+{
+ return fWriter.fSectionRelocs.size() * sizeof(macho_relocation_info<P>);
+}
+
+template <typename A>
+void SectionRelocationsLinkEditAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ memcpy(buffer, &fWriter.fSectionRelocs[0], this->getSize());
+}
+
+
+template <typename A>
+uint64_t LocalRelocationsLinkEditAtom<A>::getSize() const
+{
+ return fWriter.fInternalRelocs.size() * sizeof(macho_relocation_info<P>);
+}
+
+template <typename A>
+void LocalRelocationsLinkEditAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ memcpy(buffer, &fWriter.fInternalRelocs[0], this->getSize());
+}
+
+
+
+template <typename A>
+uint64_t SymbolTableLinkEditAtom<A>::getSize() const
+{
+ return fWriter.fSymbolTableCount * sizeof(macho_nlist<P>);
+}
+
+template <typename A>
+void SymbolTableLinkEditAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ memcpy(buffer, fWriter.fSymbolTable, this->getSize());
+}
+
+template <typename A>
+uint64_t ExternalRelocationsLinkEditAtom<A>::getSize() const
+{
+ return fWriter.fExternalRelocs.size() * sizeof(macho_relocation_info<P>);
+}
+
+template <typename A>
+void ExternalRelocationsLinkEditAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ std::sort(fWriter.fExternalRelocs.begin(), fWriter.fExternalRelocs.end(), ExternalRelocSorter<P>());
+ memcpy(buffer, &fWriter.fExternalRelocs[0], this->getSize());
+}
+
+
+
+template <typename A>
+uint64_t IndirectTableLinkEditAtom<A>::getSize() const
+{
+ return fTable.size() * sizeof(uint32_t);
+}
+
+template <typename A>
+void IndirectTableLinkEditAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ uint64_t size = this->getSize();
+ bzero(buffer, size);
+ const uint32_t indirectTableSize = fTable.size();
+ uint32_t* indirectTable = (uint32_t*)buffer;
+ for(std::vector<IndirectEntry>::const_iterator it = fTable.begin(); it != fTable.end(); ++it) {
+ if ( it->indirectIndex < indirectTableSize )
+ A::P::E::set32(indirectTable[it->indirectIndex], it->symbolIndex);
+ else
+ throwf("malformed indirect table. size=%d, index=%d", indirectTableSize, it->indirectIndex);
+ }
+}
+
+
+
+template <typename A>
+uint64_t ModuleInfoLinkEditAtom<A>::getSize() const
+{
+ return fWriter.fSymbolTableExportCount*sizeof(macho_dylib_table_of_contents<P>)
+ + sizeof(macho_dylib_module<P>)
+ + this->getReferencesCount()*sizeof(uint32_t);
+}
+
+template <typename A>
+uint32_t ModuleInfoLinkEditAtom<A>::getTableOfContentsFileOffset() const
+{
+ return this->getFileOffset();
+}
+
+template <typename A>
+uint32_t ModuleInfoLinkEditAtom<A>::getModuleTableFileOffset() const
+{
+ return this->getFileOffset() + fWriter.fSymbolTableExportCount*sizeof(macho_dylib_table_of_contents<P>);
+}
+
+template <typename A>
+uint32_t ModuleInfoLinkEditAtom<A>::getReferencesFileOffset() const
+{
+ return this->getModuleTableFileOffset() + sizeof(macho_dylib_module<P>);
+}
+
+template <typename A>
+uint32_t ModuleInfoLinkEditAtom<A>::getReferencesCount() const
+{
+ return fWriter.fSymbolTableExportCount + fWriter.fSymbolTableImportCount;
+}
+
+template <typename A>
+void ModuleInfoLinkEditAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ uint64_t size = this->getSize();
+ bzero(buffer, size);
+ // create toc. The symbols are already sorted, they are all in the smae module
+ macho_dylib_table_of_contents<P>* p = (macho_dylib_table_of_contents<P>*)buffer;
+ for(uint32_t i=0; i < fWriter.fSymbolTableExportCount; ++i, ++p) {
+ p->set_symbol_index(fWriter.fSymbolTableExportStartIndex+i);
+ p->set_module_index(0);
+ }
+ // create module table (one entry)
+ pint_t objcModuleSectionStart = 0;
+ pint_t objcModuleSectionSize = 0;
+ uint16_t numInits = 0;
+ uint16_t numTerms = 0;
+ std::vector<SegmentInfo*>& segmentInfos = fWriter.fSegmentInfos;
+ for (std::vector<SegmentInfo*>::iterator segit = segmentInfos.begin(); segit != segmentInfos.end(); ++segit) {
+ std::vector<SectionInfo*>& sectionInfos = (*segit)->fSections;
+ if ( strcmp((*segit)->fName, "__DATA") == 0 ) {
+ for (std::vector<SectionInfo*>::iterator sectit = sectionInfos.begin(); sectit != sectionInfos.end(); ++sectit) {
+ if ( strcmp((*sectit)->fSectionName, "__mod_init_func") == 0 )
+ numInits = (*sectit)->fSize / sizeof(typename A::P::uint_t);
+ else if ( strcmp((*sectit)->fSectionName, "__mod_term_func") == 0 )
+ numTerms = (*sectit)->fSize / sizeof(typename A::P::uint_t);
+ }
+ }
+ else if ( strcmp((*segit)->fName, "__OBJC") == 0 ) {
+ for (std::vector<SectionInfo*>::iterator sectit = sectionInfos.begin(); sectit != sectionInfos.end(); ++sectit) {
+ SectionInfo* sectInfo = (*sectit);
+ if ( strcmp(sectInfo->fSectionName, "__module_info") == 0 ) {
+ objcModuleSectionStart = sectInfo->getBaseAddress();
+ objcModuleSectionSize = sectInfo->fSize;
+ }
+ }
+ }
+ }
+ macho_dylib_module<P>* module = (macho_dylib_module<P>*)&buffer[fWriter.fSymbolTableExportCount*sizeof(macho_dylib_table_of_contents<P>)];
+ module->set_module_name(fModuleNameOffset);
+ module->set_iextdefsym(fWriter.fSymbolTableExportStartIndex);
+ module->set_nextdefsym(fWriter.fSymbolTableExportCount);
+ module->set_irefsym(0);
+ module->set_nrefsym(this->getReferencesCount());
+ module->set_ilocalsym(fWriter.fSymbolTableStabsStartIndex);
+ module->set_nlocalsym(fWriter.fSymbolTableStabsCount+fWriter.fSymbolTableLocalCount);
+ module->set_iextrel(0);
+ module->set_nextrel(fWriter.fExternalRelocs.size());
+ module->set_iinit_iterm(0,0);
+ module->set_ninit_nterm(numInits,numTerms);
+ module->set_objc_module_info_addr(objcModuleSectionStart);
+ module->set_objc_module_info_size(objcModuleSectionSize);
+ // create reference table
+ macho_dylib_reference<P>* ref = (macho_dylib_reference<P>*)((uint8_t*)module + sizeof(macho_dylib_module<P>));
+ for(uint32_t i=0; i < fWriter.fSymbolTableExportCount; ++i, ++ref) {
+ ref->set_isym(fWriter.fSymbolTableExportStartIndex+i);
+ ref->set_flags(REFERENCE_FLAG_DEFINED);
+ }
+ for(uint32_t i=0; i < fWriter.fSymbolTableImportCount; ++i, ++ref) {
+ ref->set_isym(fWriter.fSymbolTableImportStartIndex+i);
+ std::map<const ObjectFile::Atom*,ObjectFile::Atom*>::iterator pos = fWriter.fStubsMap.find(fWriter.fImportedAtoms[i]);
+ if ( pos != fWriter.fStubsMap.end() )
+ ref->set_flags(REFERENCE_FLAG_UNDEFINED_LAZY);
+ else
+ ref->set_flags(REFERENCE_FLAG_UNDEFINED_NON_LAZY);
+ }
+}
+
+
+
+template <typename A>
+StringsLinkEditAtom<A>::StringsLinkEditAtom(Writer<A>& writer)
+ : LinkEditAtom<A>(writer), fCurrentBuffer(NULL), fCurrentBufferUsed(0)
+{
+ fCurrentBuffer = new char[kBufferSize];
+ // burn first byte of string pool (so zero is never a valid string offset)
+ fCurrentBuffer[fCurrentBufferUsed++] = ' ';
+ // make offset 1 always point to an empty string
+ fCurrentBuffer[fCurrentBufferUsed++] = '\0';
+}
+
+template <typename A>
+uint64_t StringsLinkEditAtom<A>::getSize() const
+{
+ // align size
+ return (kBufferSize * fFullBuffers.size() + fCurrentBufferUsed + sizeof(typename A::P::uint_t) - 1) & (-sizeof(typename A::P::uint_t));
+}
+
+template <typename A>
+void StringsLinkEditAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ uint64_t offset = 0;
+ for (unsigned int i=0; i < fFullBuffers.size(); ++i) {
+ memcpy(&buffer[offset], fFullBuffers[i], kBufferSize);
+ offset += kBufferSize;
+ }
+ memcpy(&buffer[offset], fCurrentBuffer, fCurrentBufferUsed);
+ // zero fill end to align
+ offset += fCurrentBufferUsed;
+ while ( (offset % sizeof(typename A::P::uint_t)) != 0 )
+ buffer[offset++] = 0;
+}
+
+template <typename A>
+int32_t StringsLinkEditAtom<A>::add(const char* name)
+{
+ int32_t offset = kBufferSize * fFullBuffers.size() + fCurrentBufferUsed;
+ int lenNeeded = strlcpy(&fCurrentBuffer[fCurrentBufferUsed], name, kBufferSize-fCurrentBufferUsed)+1;
+ if ( (fCurrentBufferUsed+lenNeeded) < kBufferSize ) {
+ fCurrentBufferUsed += lenNeeded;
+ }
+ else {
+ int copied = kBufferSize-fCurrentBufferUsed-1;
+ // change trailing '\0' that strlcpy added to real char
+ fCurrentBuffer[kBufferSize-1] = name[copied];
+ // alloc next buffer
+ fFullBuffers.push_back(fCurrentBuffer);
+ fCurrentBuffer = new char[kBufferSize];
+ fCurrentBufferUsed = 0;
+ // append rest of string
+ this->add(&name[copied+1]);
+ }
+ return offset;
+}
+
+
+template <typename A>
+int32_t StringsLinkEditAtom<A>::addUnique(const char* name)
+{
+ StringToOffset::iterator pos = fUniqueStrings.find(name);
+ if ( pos != fUniqueStrings.end() ) {
+ return pos->second;
+ }
+ else {
+ int32_t offset = this->add(name);
+ fUniqueStrings[name] = offset;
+ return offset;
+ }
+}
+
+
+template <typename A>
+const char* StringsLinkEditAtom<A>::stringForIndex(int32_t index) const
+{
+ int32_t currentBufferStartIndex = kBufferSize * fFullBuffers.size();
+ int32_t maxIndex = currentBufferStartIndex + fCurrentBufferUsed;
+ // check for out of bounds
+ if ( index > maxIndex )
+ return "";
+ // check for index in fCurrentBuffer
+ if ( index > currentBufferStartIndex )
+ return &fCurrentBuffer[index-currentBufferStartIndex];
+ // otherwise index is in a full buffer
+ uint32_t fullBufferIndex = index/kBufferSize;
+ return &fFullBuffers[fullBufferIndex][index-(kBufferSize*fullBufferIndex)];
+}
+
+
+
+template <typename A>
+BranchIslandAtom<A>::BranchIslandAtom(Writer<A>& writer, const char* name, int islandRegion, ObjectFile::Atom& target, uint32_t targetOffset)
+ : WriterAtom<A>(writer, Segment::fgTextSegment), fTarget(target), fTargetOffset(targetOffset)
+{
+ char* buf = new char[strlen(name)+32];
+ if ( targetOffset == 0 ) {
+ if ( islandRegion == 0 )
+ sprintf(buf, "%s$island", name);
+ else
+ sprintf(buf, "%s$island_%d", name, islandRegion);
+ }
+ else {
+ sprintf(buf, "%s_plus_%d$island_%d", name, targetOffset, islandRegion);
+ }
+ fName = buf;
+}
+
+
+template <>
+void BranchIslandAtom<ppc>::copyRawContent(uint8_t buffer[]) const
+{
+ int64_t displacement = fTarget.getAddress() + fTargetOffset - this->getAddress();
+ int32_t branchInstruction = 0x48000000 | ((uint32_t)displacement & 0x03FFFFFC);
+ OSWriteBigInt32(buffer, 0, branchInstruction);
+}
+
+template <>
+void BranchIslandAtom<ppc64>::copyRawContent(uint8_t buffer[]) const
+{
+ int64_t displacement = fTarget.getAddress() + fTargetOffset - this->getAddress();
+ int32_t branchInstruction = 0x48000000 | ((uint32_t)displacement & 0x03FFFFFC);
+ OSWriteBigInt32(buffer, 0, branchInstruction);
+}
+
+template <>
+uint64_t BranchIslandAtom<ppc>::getSize() const
+{
+ return 4;
+}
+
+template <>
+uint64_t BranchIslandAtom<ppc64>::getSize() const
+{
+ return 4;
+}
+
+
+
+template <typename A>
+uint64_t SegmentSplitInfoLoadCommandsAtom<A>::getSize() const
+{
+ if ( fWriter.fSplitCodeToDataContentAtom->canEncode() )
+ return this->alignedSize(sizeof(macho_linkedit_data_command<P>));
+ else
+ return 0; // a zero size causes the load command to be suppressed
+}
+
+template <typename A>
+void SegmentSplitInfoLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ uint64_t size = this->getSize();
+ if ( size > 0 ) {
+ bzero(buffer, size);
+ macho_linkedit_data_command<P>* cmd = (macho_linkedit_data_command<P>*)buffer;
+ cmd->set_cmd(LC_SEGMENT_SPLIT_INFO);
+ cmd->set_cmdsize(size);
+ cmd->set_dataoff(fWriter.fSplitCodeToDataContentAtom->getFileOffset());
+ cmd->set_datasize(fWriter.fSplitCodeToDataContentAtom->getSize());
+ }
+}
+
+
+template <typename A>
+uint64_t SegmentSplitInfoContentAtom<A>::getSize() const
+{
+ return fEncodedData.size();
+}
+
+template <typename A>
+void SegmentSplitInfoContentAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ memcpy(buffer, &fEncodedData[0], fEncodedData.size());
+}
+
+
+template <typename A>
+void SegmentSplitInfoContentAtom<A>::uleb128EncodeAddresses(const std::vector<SegmentSplitInfoContentAtom<A>::AtomAndOffset>& locations)
+{
+ pint_t addr = fWriter.fOptions.baseAddress();
+ for(typename std::vector<AtomAndOffset>::const_iterator it = locations.begin(); it != locations.end(); ++it) {
+ pint_t nextAddr = it->atom->getAddress() + it->offset;
+ //fprintf(stderr, "\t0x%0llX\n", (uint64_t)nextAddr);
+ uint64_t delta = nextAddr - addr;
+ if ( delta == 0 )
+ throw "double split seg info for same address";
+ // uleb128 encode
+ uint8_t byte;
+ do {
+ byte = delta & 0x7F;
+ delta &= ~0x7F;
+ if ( delta != 0 )
+ byte |= 0x80;
+ fEncodedData.push_back(byte);
+ delta = delta >> 7;
+ }
+ while( byte >= 0x80 );
+ addr = nextAddr;
+ }
+}
+
+template <typename A>
+void SegmentSplitInfoContentAtom<A>::encode()
+{
+ if ( ! fCantEncode ) {
+ fEncodedData.reserve(8192);
+
+ if ( fKind1Locations.size() != 0 ) {
+ fEncodedData.push_back(1);
+ //fprintf(stderr, "type 1:\n");
+ this->uleb128EncodeAddresses(fKind1Locations);
+ fEncodedData.push_back(0);
+ }
+
+ if ( fKind2Locations.size() != 0 ) {
+ fEncodedData.push_back(2);
+ //fprintf(stderr, "type 2:\n");
+ this->uleb128EncodeAddresses(fKind2Locations);
+ fEncodedData.push_back(0);
+ }
+
+ if ( fKind3Locations.size() != 0 ) {
+ fEncodedData.push_back(3);
+ //fprintf(stderr, "type 3:\n");
+ this->uleb128EncodeAddresses(fKind3Locations);
+ fEncodedData.push_back(0);
+ }
+
+ if ( fKind4Locations.size() != 0 ) {
+ fEncodedData.push_back(4);
+ //fprintf(stderr, "type 4:\n");
+ this->uleb128EncodeAddresses(fKind4Locations);
+ fEncodedData.push_back(0);
+ }
+
+ // always add zero byte to mark end
+ fEncodedData.push_back(0);
+
+ // add zeros to end to align size
+ while ( (fEncodedData.size() % sizeof(pint_t)) != 0 )
+ fEncodedData.push_back(0);
+ }
+}
+
+
+template <typename A>
+ObjCInfoAtom<A>::ObjCInfoAtom(Writer<A>& writer, ObjectFile::Reader::ObjcConstraint objcConstraint, bool objcReplacementClasses)
+ : WriterAtom<A>(writer, getInfoSegment())
+{
+ fContent[0] = 0;
+ uint32_t value = 0;
+ // struct objc_image_info {
+ // uint32_t version; // initially 0
+ // uint32_t flags;
+ // };
+ // #define OBJC_IMAGE_SUPPORTS_GC 2
+ // #define OBJC_IMAGE_GC_ONLY 4
+ //
+ if ( objcReplacementClasses )
+ value = 1;
+ switch ( objcConstraint ) {
+ case ObjectFile::Reader::kObjcNone:
+ case ObjectFile::Reader::kObjcRetainRelease:
+ break;
+ case ObjectFile::Reader::kObjcRetainReleaseOrGC:
+ value |= 2;
+ break;
+ case ObjectFile::Reader::kObjcGC:
+ value |= 6;
+ break;
+ }
+ A::P::E::set32(fContent[1], value);
+}
+
+template <typename A>
+void ObjCInfoAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ memcpy(buffer, &fContent[0], 8);
+}
+
+
+// objc info section is in a different segment and section for 32 vs 64 bit runtimes
+template <> const char* ObjCInfoAtom<ppc>::getSectionName() const { return "__image_info"; }
+template <> const char* ObjCInfoAtom<x86>::getSectionName() const { return "__image_info"; }
+template <> const char* ObjCInfoAtom<arm>::getSectionName() const { return "__objc_imageinfo"; }
+template <> const char* ObjCInfoAtom<ppc64>::getSectionName() const { return "__objc_imageinfo"; }
+template <> const char* ObjCInfoAtom<x86_64>::getSectionName() const { return "__objc_imageinfo"; }
+
+template <> Segment& ObjCInfoAtom<ppc>::getInfoSegment() const { return Segment::fgObjCSegment; }
+template <> Segment& ObjCInfoAtom<x86>::getInfoSegment() const { return Segment::fgObjCSegment; }
+template <> Segment& ObjCInfoAtom<ppc64>::getInfoSegment() const { return Segment::fgDataSegment; }
+template <> Segment& ObjCInfoAtom<x86_64>::getInfoSegment() const { return Segment::fgDataSegment; }
+template <> Segment& ObjCInfoAtom<arm>::getInfoSegment() const { return Segment::fgDataSegment; }
+
+
+
+
+template <typename A>
+void DyldInfoLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ // build LC_DYLD_INFO command
+ macho_dyld_info_command<P>* cmd = (macho_dyld_info_command<P>*)buffer;
+ bzero(cmd, sizeof(macho_dyld_info_command<P>));
+
+ cmd->set_cmd( fWriter.fOptions.makeClassicDyldInfo() ? LC_DYLD_INFO : LC_DYLD_INFO_ONLY);
+ cmd->set_cmdsize(sizeof(macho_dyld_info_command<P>));
+ if ( (fWriter.fCompressedRebaseInfoAtom != NULL) && (fWriter.fCompressedRebaseInfoAtom->getSize() != 0) ) {
+ cmd->set_rebase_off(fWriter.fCompressedRebaseInfoAtom->getFileOffset());
+ cmd->set_rebase_size(fWriter.fCompressedRebaseInfoAtom->getSize());
+ }
+ if ( (fWriter.fCompressedBindingInfoAtom != NULL) && (fWriter.fCompressedBindingInfoAtom->getSize() != 0) ) {
+ cmd->set_bind_off(fWriter.fCompressedBindingInfoAtom->getFileOffset());
+ cmd->set_bind_size(fWriter.fCompressedBindingInfoAtom->getSize());
+ }
+ if ( (fWriter.fCompressedWeakBindingInfoAtom != NULL) && (fWriter.fCompressedWeakBindingInfoAtom->getSize() != 0) ) {
+ cmd->set_weak_bind_off(fWriter.fCompressedWeakBindingInfoAtom->getFileOffset());
+ cmd->set_weak_bind_size(fWriter.fCompressedWeakBindingInfoAtom->getSize());
+ }
+ if ( (fWriter.fCompressedLazyBindingInfoAtom != NULL) && (fWriter.fCompressedLazyBindingInfoAtom->getSize() != 0) ) {
+ cmd->set_lazy_bind_off(fWriter.fCompressedLazyBindingInfoAtom->getFileOffset());
+ cmd->set_lazy_bind_size(fWriter.fCompressedLazyBindingInfoAtom->getSize());
+ }
+ if ( (fWriter.fCompressedExportInfoAtom != NULL) && (fWriter.fCompressedExportInfoAtom->getSize() != 0) ) {
+ cmd->set_export_off(fWriter.fCompressedExportInfoAtom->getFileOffset());
+ cmd->set_export_size(fWriter.fCompressedExportInfoAtom->getSize());
+ }
+}
+
+
+struct rebase_tmp
+{
+ rebase_tmp(uint8_t op, uint64_t p1, uint64_t p2=0) : opcode(op), operand1(p1), operand2(p2) {}
+ uint8_t opcode;
+ uint64_t operand1;
+ uint64_t operand2;
+};
+
+
+template <typename A>
+void CompressedRebaseInfoLinkEditAtom<A>::encode()
+{
+ // sort rebase info by type, then address
+ const std::vector<SegmentInfo*>& segments = fWriter.fSegmentInfos;
+ std::vector<RebaseInfo>& info = fWriter.fRebaseInfo;
+ std::sort(info.begin(), info.end());
+
+ // convert to temp encoding that can be more easily optimized
+ std::vector<rebase_tmp> mid;
+ const SegmentInfo* currentSegment = NULL;
+ unsigned int segIndex = 0;
+ uint8_t type = 0;
+ uint64_t address = (uint64_t)(-1);
+ for (std::vector<RebaseInfo>::iterator it = info.begin(); it != info.end(); ++it) {
+ if ( type != it->fType ) {
+ mid.push_back(rebase_tmp(REBASE_OPCODE_SET_TYPE_IMM, it->fType));
+ type = it->fType;
+ }
+ if ( address != it->fAddress ) {
+ if ( (currentSegment == NULL) || (it->fAddress < currentSegment->fBaseAddress)
+ || ((currentSegment->fBaseAddress+currentSegment->fSize) <= it->fAddress) ) {
+ segIndex = 0;
+ for (std::vector<SegmentInfo*>::const_iterator segit = segments.begin(); segit != segments.end(); ++segit) {
+ if ( ((*segit)->fBaseAddress <= it->fAddress) && (it->fAddress < ((*segit)->fBaseAddress+(*segit)->fSize)) ) {
+ currentSegment = *segit;
+ break;
+ }
+ ++segIndex;
+ }
+ mid.push_back(rebase_tmp(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB, segIndex, it->fAddress - currentSegment->fBaseAddress));
+ }
+ else {
+ mid.push_back(rebase_tmp(REBASE_OPCODE_ADD_ADDR_ULEB, it->fAddress-address));
+ }
+ address = it->fAddress;
+ }
+ mid.push_back(rebase_tmp(REBASE_OPCODE_DO_REBASE_ULEB_TIMES, 1));
+ address += sizeof(pint_t);
+ }
+ mid.push_back(rebase_tmp(REBASE_OPCODE_DONE, 0));
+
+ // optimize phase 1, compress packed runs of pointers
+ rebase_tmp* dst = &mid[0];
+ for (const rebase_tmp* src = &mid[0]; src->opcode != REBASE_OPCODE_DONE; ++src) {
+ if ( (src->opcode == REBASE_OPCODE_DO_REBASE_ULEB_TIMES) && (src->operand1 == 1) ) {
+ *dst = *src++;
+ while (src->opcode == REBASE_OPCODE_DO_REBASE_ULEB_TIMES ) {
+ dst->operand1 += src->operand1;
+ ++src;
+ }
+ --src;
+ ++dst;
+ }
+ else {
+ *dst++ = *src;
+ }
+ }
+ dst->opcode = REBASE_OPCODE_DONE;
+
+ // optimize phase 2, combine rebase/add pairs
+ dst = &mid[0];
+ for (const rebase_tmp* src = &mid[0]; src->opcode != REBASE_OPCODE_DONE; ++src) {
+ if ( (src->opcode == REBASE_OPCODE_DO_REBASE_ULEB_TIMES)
+ && (src->operand1 == 1)
+ && (src[1].opcode == REBASE_OPCODE_ADD_ADDR_ULEB)) {
+ dst->opcode = REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB;
+ dst->operand1 = src[1].operand1;
+ ++src;
+ ++dst;
+ }
+ else {
+ *dst++ = *src;
+ }
+ }
+ dst->opcode = REBASE_OPCODE_DONE;
+
+ // optimize phase 3, compress packed runs of REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB with
+ // same addr delta into one REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB
+ dst = &mid[0];
+ for (const rebase_tmp* src = &mid[0]; src->opcode != REBASE_OPCODE_DONE; ++src) {
+ uint64_t delta = src->operand1;
+ if ( (src->opcode == REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB)
+ && (src[1].opcode == REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB)
+ && (src[2].opcode == REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB)
+ && (src[1].operand1 == delta)
+ && (src[2].operand1 == delta) ) {
+ // found at least three in a row, this is worth compressing
+ dst->opcode = REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB;
+ dst->operand1 = 1;
+ dst->operand2 = delta;
+ ++src;
+ while ( (src->opcode == REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB)
+ && (src->operand1 == delta) ) {
+ dst->operand1++;
+ ++src;
+ }
+ --src;
+ ++dst;
+ }
+ else {
+ *dst++ = *src;
+ }
+ }
+ dst->opcode = REBASE_OPCODE_DONE;
+
+ // optimize phase 4, use immediate encodings
+ for (rebase_tmp* p = &mid[0]; p->opcode != REBASE_OPCODE_DONE; ++p) {
+ if ( (p->opcode == REBASE_OPCODE_ADD_ADDR_ULEB)
+ && (p->operand1 < (15*sizeof(pint_t)))
+ && ((p->operand1 % sizeof(pint_t)) == 0) ) {
+ p->opcode = REBASE_OPCODE_ADD_ADDR_IMM_SCALED;
+ p->operand1 = p->operand1/sizeof(pint_t);
+ }
+ else if ( (p->opcode == REBASE_OPCODE_DO_REBASE_ULEB_TIMES) && (p->operand1 < 15) ) {
+ p->opcode = REBASE_OPCODE_DO_REBASE_IMM_TIMES;
+ }
+ }
+
+ // convert to compressed encoding
+ const static bool log = false;
+ fEncodedData.reserve(info.size()*2);
+ bool done = false;
+ for (std::vector<rebase_tmp>::iterator it = mid.begin(); !done && it != mid.end() ; ++it) {
+ switch ( it->opcode ) {
+ case REBASE_OPCODE_DONE:
+ if ( log ) fprintf(stderr, "REBASE_OPCODE_DONE()\n");
+ done = true;
+ break;
+ case REBASE_OPCODE_SET_TYPE_IMM:
+ if ( log ) fprintf(stderr, "REBASE_OPCODE_SET_TYPE_IMM(%lld)\n", it->operand1);
+ fEncodedData.append_byte(REBASE_OPCODE_SET_TYPE_IMM | it->operand1);
+ break;
+ case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ if ( log ) fprintf(stderr, "REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(%lld, 0x%llX)\n", it->operand1, it->operand2);
+ fEncodedData.append_byte(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | it->operand1);
+ fEncodedData.append_uleb128(it->operand2);
+ break;
+ case REBASE_OPCODE_ADD_ADDR_ULEB:
+ if ( log ) fprintf(stderr, "REBASE_OPCODE_ADD_ADDR_ULEB(0x%llX)\n", it->operand1);
+ fEncodedData.append_byte(REBASE_OPCODE_ADD_ADDR_ULEB);
+ fEncodedData.append_uleb128(it->operand1);
+ break;
+ case REBASE_OPCODE_ADD_ADDR_IMM_SCALED:
+ if ( log ) fprintf(stderr, "REBASE_OPCODE_ADD_ADDR_IMM_SCALED(%lld=0x%llX)\n", it->operand1, it->operand1*sizeof(pint_t));
+ fEncodedData.append_byte(REBASE_OPCODE_ADD_ADDR_IMM_SCALED | it->operand1 );
+ break;
+ case REBASE_OPCODE_DO_REBASE_IMM_TIMES:
+ if ( log ) fprintf(stderr, "REBASE_OPCODE_DO_REBASE_IMM_TIMES(%lld)\n", it->operand1);
+ fEncodedData.append_byte(REBASE_OPCODE_DO_REBASE_IMM_TIMES | it->operand1);
+ break;
+ case REBASE_OPCODE_DO_REBASE_ULEB_TIMES:
+ if ( log ) fprintf(stderr, "REBASE_OPCODE_DO_REBASE_ULEB_TIMES(%lld)\n", it->operand1);
+ fEncodedData.append_byte(REBASE_OPCODE_DO_REBASE_ULEB_TIMES);
+ fEncodedData.append_uleb128(it->operand1);
+ break;
+ case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
+ if ( log ) fprintf(stderr, "REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB(0x%llX)\n", it->operand1);
+ fEncodedData.append_byte(REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB);
+ fEncodedData.append_uleb128(it->operand1);
+ break;
+ case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB:
+ if ( log ) fprintf(stderr, "REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB(%lld, %lld)\n", it->operand1, it->operand2);
+ fEncodedData.append_byte(REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB);
+ fEncodedData.append_uleb128(it->operand1);
+ fEncodedData.append_uleb128(it->operand2);
+ break;
+ }
+ }
+
+
+ // align to pointer size
+ fEncodedData.pad_to_size(sizeof(pint_t));
+
+ if (log) fprintf(stderr, "total rebase info size = %ld\n", fEncodedData.size());
+}
+
+
+struct binding_tmp
+{
+ binding_tmp(uint8_t op, uint64_t p1, uint64_t p2=0, const char* s=NULL)
+ : opcode(op), operand1(p1), operand2(p2), name(s) {}
+ uint8_t opcode;
+ uint64_t operand1;
+ uint64_t operand2;
+ const char* name;
+};
+
+
+
+template <typename A>
+void CompressedBindingInfoLinkEditAtom<A>::encode()
+{
+ // sort by library, symbol, type, then address
+ const std::vector<SegmentInfo*>& segments = fWriter.fSegmentInfos;
+ std::vector<BindingInfo>& info = fWriter.fBindingInfo;
+ std::sort(info.begin(), info.end());
+
+ // convert to temp encoding that can be more easily optimized
+ std::vector<binding_tmp> mid;
+ const SegmentInfo* currentSegment = NULL;
+ unsigned int segIndex = 0;
+ int ordinal = 0x80000000;
+ const char* symbolName = NULL;
+ uint8_t type = 0;
+ uint64_t address = (uint64_t)(-1);
+ int64_t addend = 0;
+ for (std::vector<BindingInfo>::iterator it = info.begin(); it != info.end(); ++it) {
+ if ( ordinal != it->fLibraryOrdinal ) {
+ if ( it->fLibraryOrdinal <= 0 ) {
+ // special lookups are encoded as negative numbers in BindingInfo
+ mid.push_back(binding_tmp(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, it->fLibraryOrdinal));
+ }
+ else {
+ mid.push_back(binding_tmp(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB, it->fLibraryOrdinal));
+ }
+ ordinal = it->fLibraryOrdinal;
+ }
+ if ( symbolName != it->fSymbolName ) {
+ mid.push_back(binding_tmp(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM, it->fFlags, 0, it->fSymbolName));
+ symbolName = it->fSymbolName;
+ }
+ if ( type != it->fType ) {
+ mid.push_back(binding_tmp(BIND_OPCODE_SET_TYPE_IMM, it->fType));
+ type = it->fType;
+ }
+ if ( address != it->fAddress ) {
+ if ( (currentSegment == NULL) || (it->fAddress < currentSegment->fBaseAddress)
+ || ((currentSegment->fBaseAddress+currentSegment->fSize) <=it->fAddress)
+ || (it->fAddress < address) ) {
+ segIndex = 0;
+ for (std::vector<SegmentInfo*>::const_iterator segit = segments.begin(); segit != segments.end(); ++segit) {
+ if ( ((*segit)->fBaseAddress <= it->fAddress) && (it->fAddress < ((*segit)->fBaseAddress+(*segit)->fSize)) ) {
+ currentSegment = *segit;
+ break;
+ }
+ ++segIndex;
+ }
+ mid.push_back(binding_tmp(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB, segIndex, it->fAddress - currentSegment->fBaseAddress));
+ }
+ else {
+ mid.push_back(binding_tmp(BIND_OPCODE_ADD_ADDR_ULEB, it->fAddress-address));
+ }
+ address = it->fAddress;
+ }
+ if ( addend != it->fAddend ) {
+ mid.push_back(binding_tmp(BIND_OPCODE_SET_ADDEND_SLEB, it->fAddend));
+ addend = it->fAddend;
+ }
+ mid.push_back(binding_tmp(BIND_OPCODE_DO_BIND, 0));
+ address += sizeof(pint_t);
+ }
+ mid.push_back(binding_tmp(BIND_OPCODE_DONE, 0));
+
+
+ // optimize phase 1, combine bind/add pairs
+ binding_tmp* dst = &mid[0];
+ for (const binding_tmp* src = &mid[0]; src->opcode != BIND_OPCODE_DONE; ++src) {
+ if ( (src->opcode == BIND_OPCODE_DO_BIND)
+ && (src[1].opcode == BIND_OPCODE_ADD_ADDR_ULEB) ) {
+ dst->opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB;
+ dst->operand1 = src[1].operand1;
+ ++src;
+ ++dst;
+ }
+ else {
+ *dst++ = *src;
+ }
+ }
+ dst->opcode = BIND_OPCODE_DONE;
+
+ // optimize phase 2, compress packed runs of BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB with
+ // same addr delta into one BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB
+ dst = &mid[0];
+ for (const binding_tmp* src = &mid[0]; src->opcode != BIND_OPCODE_DONE; ++src) {
+ uint64_t delta = src->operand1;
+ if ( (src->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB)
+ && (src[1].opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB)
+ && (src[1].operand1 == delta) ) {
+ // found at least two in a row, this is worth compressing
+ dst->opcode = BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB;
+ dst->operand1 = 1;
+ dst->operand2 = delta;
+ ++src;
+ while ( (src->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB)
+ && (src->operand1 == delta) ) {
+ dst->operand1++;
+ ++src;
+ }
+ --src;
+ ++dst;
+ }
+ else {
+ *dst++ = *src;
+ }
+ }
+ dst->opcode = BIND_OPCODE_DONE;
+
+ // optimize phase 3, use immediate encodings
+ for (binding_tmp* p = &mid[0]; p->opcode != REBASE_OPCODE_DONE; ++p) {
+ if ( (p->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB)
+ && (p->operand1 < (15*sizeof(pint_t)))
+ && ((p->operand1 % sizeof(pint_t)) == 0) ) {
+ p->opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED;
+ p->operand1 = p->operand1/sizeof(pint_t);
+ }
+ else if ( (p->opcode == BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB) && (p->operand1 <= 15) ) {
+ p->opcode = BIND_OPCODE_SET_DYLIB_ORDINAL_IMM;
+ }
+ }
+ dst->opcode = BIND_OPCODE_DONE;
+
+ // convert to compressed encoding
+ const static bool log = false;
+ fEncodedData.reserve(info.size()*2);
+ bool done = false;
+ for (std::vector<binding_tmp>::iterator it = mid.begin(); !done && it != mid.end() ; ++it) {
+ switch ( it->opcode ) {
+ case BIND_OPCODE_DONE:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_DONE()\n");
+ done = true;
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_ORDINAL_IMM(%lld)\n", it->operand1);
+ fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | it->operand1);
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB(%lld)\n", it->operand1);
+ fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
+ fEncodedData.append_uleb128(it->operand1);
+ break;
+ case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_SPECIAL_IMM(%lld)\n", it->operand1);
+ fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | (it->operand1 & BIND_IMMEDIATE_MASK));
+ break;
+ case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM(0x%0llX, %s)\n", it->operand1, it->name);
+ fEncodedData.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | it->operand1);
+ fEncodedData.append_string(it->name);
+ break;
+ case BIND_OPCODE_SET_TYPE_IMM:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_SET_TYPE_IMM(%lld)\n", it->operand1);
+ fEncodedData.append_byte(BIND_OPCODE_SET_TYPE_IMM | it->operand1);
+ break;
+ case BIND_OPCODE_SET_ADDEND_SLEB:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_SET_ADDEND_SLEB(%lld)\n", it->operand1);
+ fEncodedData.append_byte(BIND_OPCODE_SET_ADDEND_SLEB);
+ fEncodedData.append_sleb128(it->operand1);
+ break;
+ case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(%lld, 0x%llX)\n", it->operand1, it->operand2);
+ fEncodedData.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | it->operand1);
+ fEncodedData.append_uleb128(it->operand2);
+ break;
+ case BIND_OPCODE_ADD_ADDR_ULEB:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_ADD_ADDR_ULEB(0x%llX)\n", it->operand1);
+ fEncodedData.append_byte(BIND_OPCODE_ADD_ADDR_ULEB);
+ fEncodedData.append_uleb128(it->operand1);
+ break;
+ case BIND_OPCODE_DO_BIND:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND()\n");
+ fEncodedData.append_byte(BIND_OPCODE_DO_BIND);
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB(0x%llX)\n", it->operand1);
+ fEncodedData.append_byte(BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB);
+ fEncodedData.append_uleb128(it->operand1);
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED(%lld=0x%llX)\n", it->operand1, it->operand1*sizeof(pint_t));
+ fEncodedData.append_byte(BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED | it->operand1 );
+ break;
+ case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB(%lld, %lld)\n", it->operand1, it->operand2);
+ fEncodedData.append_byte(BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB);
+ fEncodedData.append_uleb128(it->operand1);
+ fEncodedData.append_uleb128(it->operand2);
+ break;
+ }
+ }
+
+ // align to pointer size
+ fEncodedData.pad_to_size(sizeof(pint_t));
+
+ if (log) fprintf(stderr, "total binding info size = %ld\n", fEncodedData.size());
+
+}
+
+
+
+struct WeakBindingSorter
+{
+ bool operator()(const BindingInfo& left, const BindingInfo& right)
+ {
+ // sort by symbol, type, address
+ if ( left.fSymbolName != right.fSymbolName )
+ return ( strcmp(left.fSymbolName, right.fSymbolName) < 0 );
+ if ( left.fType != right.fType )
+ return (left.fType < right.fType);
+ return (left.fAddress < right.fAddress);
+ }
+};
+
+
+
+template <typename A>
+void CompressedWeakBindingInfoLinkEditAtom<A>::encode()
+{
+ // add regular atoms that override a dylib's weak definitions
+ for(std::set<const class ObjectFile::Atom*>::iterator it = fWriter.fRegularDefAtomsThatOverrideADylibsWeakDef->begin();
+ it != fWriter.fRegularDefAtomsThatOverrideADylibsWeakDef->end(); ++it) {
+ if ( fWriter.shouldExport(**it) )
+ fWriter.fWeakBindingInfo.push_back(BindingInfo(0, (*it)->getName(), true, 0, 0));
+ }
+
+ // add all exported weak definitions
+ for(std::vector<class ObjectFile::Atom*>::iterator it = fWriter.fAllAtoms->begin(); it != fWriter.fAllAtoms->end(); ++it) {
+ ObjectFile::Atom* atom = *it;
+ if ( (atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition) && fWriter.shouldExport(*atom) ) {
+ fWriter.fWeakBindingInfo.push_back(BindingInfo(0, atom->getName(), false, 0, 0));
+ }
+ }
+
+ // sort by symbol, type, address
+ const std::vector<SegmentInfo*>& segments = fWriter.fSegmentInfos;
+ std::vector<BindingInfo>& info = fWriter.fWeakBindingInfo;
+ if ( info.size() == 0 )
+ return;
+ std::sort(info.begin(), info.end(), WeakBindingSorter());
+
+ // convert to temp encoding that can be more easily optimized
+ std::vector<binding_tmp> mid;
+ mid.reserve(info.size());
+ const SegmentInfo* currentSegment = NULL;
+ unsigned int segIndex = 0;
+ const char* symbolName = NULL;
+ uint8_t type = 0;
+ uint64_t address = (uint64_t)(-1);
+ int64_t addend = 0;
+ for (std::vector<BindingInfo>::iterator it = info.begin(); it != info.end(); ++it) {
+ if ( symbolName != it->fSymbolName ) {
+ mid.push_back(binding_tmp(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM, it->fFlags, 0, it->fSymbolName));
+ symbolName = it->fSymbolName;
+ }
+ if ( it->fType != 0 ) {
+ if ( type != it->fType ) {
+ mid.push_back(binding_tmp(BIND_OPCODE_SET_TYPE_IMM, it->fType));
+ type = it->fType;
+ }
+ if ( address != it->fAddress ) {
+ // non weak symbols just have BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
+ // weak symbols have SET_SEG, ADD_ADDR, SET_ADDED, DO_BIND
+ if ( (currentSegment == NULL) || (it->fAddress < currentSegment->fBaseAddress)
+ || ((currentSegment->fBaseAddress+currentSegment->fSize) <=it->fAddress) ) {
+ segIndex = 0;
+ for (std::vector<SegmentInfo*>::const_iterator segit = segments.begin(); segit != segments.end(); ++segit) {
+ if ( ((*segit)->fBaseAddress <= it->fAddress) && (it->fAddress < ((*segit)->fBaseAddress+(*segit)->fSize)) ) {
+ currentSegment = *segit;
+ break;
+ }
+ ++segIndex;
+ }
+ mid.push_back(binding_tmp(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB, segIndex, it->fAddress - currentSegment->fBaseAddress));
+ }
+ else {
+ mid.push_back(binding_tmp(BIND_OPCODE_ADD_ADDR_ULEB, it->fAddress-address));
+ }
+ address = it->fAddress;
+ }
+ if ( addend != it->fAddend ) {
+ mid.push_back(binding_tmp(BIND_OPCODE_SET_ADDEND_SLEB, it->fAddend));
+ addend = it->fAddend;
+ }
+ mid.push_back(binding_tmp(BIND_OPCODE_DO_BIND, 0));
+ address += sizeof(pint_t);
+ }
+ }
+ mid.push_back(binding_tmp(BIND_OPCODE_DONE, 0));
+
+
+ // optimize phase 1, combine bind/add pairs
+ binding_tmp* dst = &mid[0];
+ for (const binding_tmp* src = &mid[0]; src->opcode != BIND_OPCODE_DONE; ++src) {
+ if ( (src->opcode == BIND_OPCODE_DO_BIND)
+ && (src[1].opcode == BIND_OPCODE_ADD_ADDR_ULEB) ) {
+ dst->opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB;
+ dst->operand1 = src[1].operand1;
+ ++src;
+ ++dst;
+ }
+ else {
+ *dst++ = *src;
+ }
+ }
+ dst->opcode = BIND_OPCODE_DONE;
+
+ // optimize phase 2, compress packed runs of BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB with
+ // same addr delta into one BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB
+ dst = &mid[0];
+ for (const binding_tmp* src = &mid[0]; src->opcode != BIND_OPCODE_DONE; ++src) {
+ uint64_t delta = src->operand1;
+ if ( (src->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB)
+ && (src[1].opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB)
+ && (src[1].operand1 == delta) ) {
+ // found at least two in a row, this is worth compressing
+ dst->opcode = BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB;
+ dst->operand1 = 1;
+ dst->operand2 = delta;
+ ++src;
+ while ( (src->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB)
+ && (src->operand1 == delta) ) {
+ dst->operand1++;
+ ++src;
+ }
+ --src;
+ ++dst;
+ }
+ else {
+ *dst++ = *src;
+ }
+ }
+ dst->opcode = BIND_OPCODE_DONE;
+
+ // optimize phase 3, use immediate encodings
+ for (binding_tmp* p = &mid[0]; p->opcode != REBASE_OPCODE_DONE; ++p) {
+ if ( (p->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB)
+ && (p->operand1 < (15*sizeof(pint_t)))
+ && ((p->operand1 % sizeof(pint_t)) == 0) ) {
+ p->opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED;
+ p->operand1 = p->operand1/sizeof(pint_t);
+ }
+ }
+ dst->opcode = BIND_OPCODE_DONE;
+
+
+ // convert to compressed encoding
+ const static bool log = false;
+ fEncodedData.reserve(info.size()*2);
+ bool done = false;
+ for (std::vector<binding_tmp>::iterator it = mid.begin(); !done && it != mid.end() ; ++it) {
+ switch ( it->opcode ) {
+ case BIND_OPCODE_DONE:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_DONE()\n");
+ fEncodedData.append_byte(BIND_OPCODE_DONE);
+ done = true;
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_ORDINAL_IMM(%lld)\n", it->operand1);
+ fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | it->operand1);
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB(%lld)\n", it->operand1);
+ fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
+ fEncodedData.append_uleb128(it->operand1);
+ break;
+ case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_SPECIAL_IMM(%lld)\n", it->operand1);
+ fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | (it->operand1 & BIND_IMMEDIATE_MASK));
+ break;
+ case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM(0x%0llX, %s)\n", it->operand1, it->name);
+ fEncodedData.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | it->operand1);
+ fEncodedData.append_string(it->name);
+ break;
+ case BIND_OPCODE_SET_TYPE_IMM:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_SET_TYPE_IMM(%lld)\n", it->operand1);
+ fEncodedData.append_byte(BIND_OPCODE_SET_TYPE_IMM | it->operand1);
+ break;
+ case BIND_OPCODE_SET_ADDEND_SLEB:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_SET_ADDEND_SLEB(%lld)\n", it->operand1);
+ fEncodedData.append_byte(BIND_OPCODE_SET_ADDEND_SLEB);
+ fEncodedData.append_sleb128(it->operand1);
+ break;
+ case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(%lld, 0x%llX)\n", it->operand1, it->operand2);
+ fEncodedData.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | it->operand1);
+ fEncodedData.append_uleb128(it->operand2);
+ break;
+ case BIND_OPCODE_ADD_ADDR_ULEB:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_ADD_ADDR_ULEB(0x%llX)\n", it->operand1);
+ fEncodedData.append_byte(BIND_OPCODE_ADD_ADDR_ULEB);
+ fEncodedData.append_uleb128(it->operand1);
+ break;
+ case BIND_OPCODE_DO_BIND:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND()\n");
+ fEncodedData.append_byte(BIND_OPCODE_DO_BIND);
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB(0x%llX)\n", it->operand1);
+ fEncodedData.append_byte(BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB);
+ fEncodedData.append_uleb128(it->operand1);
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED(%lld=0x%llX)\n", it->operand1, it->operand1*sizeof(pint_t));
+ fEncodedData.append_byte(BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED | it->operand1 );
+ break;
+ case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB(%lld, %lld)\n", it->operand1, it->operand2);
+ fEncodedData.append_byte(BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB);
+ fEncodedData.append_uleb128(it->operand1);
+ fEncodedData.append_uleb128(it->operand2);
+ break;
+ }
+ }
+
+ // align to pointer size
+ fEncodedData.pad_to_size(sizeof(pint_t));
+
+ if (log) fprintf(stderr, "total weak binding info size = %ld\n", fEncodedData.size());
+
+}
+
+template <typename A>
+void CompressedLazyBindingInfoLinkEditAtom<A>::encode()
+{
+ // stream all lazy bindings and record start offsets
+ const SegmentInfo* currentSegment = NULL;
+ uint8_t segIndex = 0;
+ const std::vector<SegmentInfo*>& segments = fWriter.fSegmentInfos;
+ std::vector<class LazyPointerAtom<A>*>& allLazys = fWriter.fAllSynthesizedLazyPointers;
+ for (typename std::vector<class LazyPointerAtom<A>*>::iterator it = allLazys.begin(); it != allLazys.end(); ++it) {
+ LazyPointerAtom<A>* lazyPointerAtom = *it;
+ ObjectFile::Atom* lazyPointerTargetAtom = lazyPointerAtom->getTarget();
+
+ // skip lazy pointers that are bound non-lazily because they are coalesced
+ if ( ! fWriter.targetRequiresWeakBinding(*lazyPointerTargetAtom) ) {
+ // record start offset for use by stub helper
+ lazyPointerAtom->setLazyBindingInfoOffset(fEncodedData.size());
+
+ // write address to bind
+ pint_t address = lazyPointerAtom->getAddress();
+ if ( (currentSegment == NULL) || (address < currentSegment->fBaseAddress)
+ || ((currentSegment->fBaseAddress+currentSegment->fSize) <= address) ) {
+ segIndex = 0;
+ for (std::vector<SegmentInfo*>::const_iterator segit = segments.begin(); segit != segments.end(); ++segit) {
+ if ( ((*segit)->fBaseAddress <= address) && (address < ((*segit)->fBaseAddress+(*segit)->fSize)) ) {
+ currentSegment = *segit;
+ break;
+ }
+ ++segIndex;
+ }
+ }
+ fEncodedData.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | segIndex);
+ fEncodedData.append_uleb128(lazyPointerAtom->getAddress() - currentSegment->fBaseAddress);
+
+ // write ordinal
+ int ordinal = fWriter.compressedOrdinalForImortedAtom(lazyPointerTargetAtom);
+ if ( ordinal <= 0 ) {
+ // special lookups are encoded as negative numbers in BindingInfo
+ fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | (ordinal & BIND_IMMEDIATE_MASK) );
+ }
+ else if ( ordinal <= 15 ) {
+ // small ordinals are encoded in opcode
+ fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | ordinal);
+ }
+ else {
+ fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
+ fEncodedData.append_uleb128(ordinal);
+ }
+ // write symbol name
+ bool weak_import = fWriter.fWeakImportMap[lazyPointerTargetAtom];
+ if ( weak_import )
+ fEncodedData.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | BIND_SYMBOL_FLAGS_WEAK_IMPORT);
+ else
+ fEncodedData.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM);
+ fEncodedData.append_string(lazyPointerTargetAtom->getName());
+ // write do bind
+ fEncodedData.append_byte(BIND_OPCODE_DO_BIND);
+ fEncodedData.append_byte(BIND_OPCODE_DONE);
+ }
+ }
+ // align to pointer size
+ fEncodedData.pad_to_size(sizeof(pint_t));
+
+ //fprintf(stderr, "lazy binding info size = %ld, for %ld entries\n", fEncodedData.size(), allLazys.size());
+}
+
+struct TrieEntriesSorter
+{
+ TrieEntriesSorter(Options& o) : fOptions(o) {}
+
+ bool operator()(const mach_o::trie::Entry& left, const mach_o::trie::Entry& right)
+ {
+ unsigned int leftOrder;
+ unsigned int rightOrder;
+ fOptions.exportedSymbolOrder(left.name, &leftOrder);
+ fOptions.exportedSymbolOrder(right.name, &rightOrder);
+ if ( leftOrder != rightOrder )
+ return (leftOrder < rightOrder);
+ else
+ return (left.address < right.address);
+ }
+private:
+ Options& fOptions;
+};
+
+
+template <typename A>
+void CompressedExportInfoLinkEditAtom<A>::encode()
+{
+ // make vector of mach_o::trie::Entry for all exported symbols
+ std::vector<class ObjectFile::Atom*>& exports = fWriter.fExportedAtoms;
+ uint64_t imageBaseAddress = fWriter.fMachHeaderAtom->getAddress();
+ std::vector<mach_o::trie::Entry> entries;
+ entries.reserve(exports.size());
+ for (std::vector<ObjectFile::Atom*>::iterator it = exports.begin(); it != exports.end(); ++it) {
+ ObjectFile::Atom* atom = *it;
+ uint64_t flags = 0;
+ if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition )
+ flags |= EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION;
+ uint64_t address = atom->getAddress() - imageBaseAddress;
+ if ( atom->isThumb() )
+ address |= 1;
+ mach_o::trie::Entry entry;
+ entry.name = atom->getName();
+ entry.flags = flags;
+ entry.address = address;
+ entries.push_back(entry);
+ }
+
+ // sort vector by -exported_symbols_order, and any others by address
+ std::sort(entries.begin(), entries.end(), TrieEntriesSorter(fWriter.fOptions));
+
+ // create trie
+ mach_o::trie::makeTrie(entries, fEncodedData.bytes());
+
+ // align to pointer size
+ fEncodedData.pad_to_size(sizeof(pint_t));
+}
+
+
+
+
+
+}; // namespace executable
+}; // namespace mach_o
+
+
+#endif // __EXECUTABLE_MACH_O__
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2005-2007 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+#ifndef __OBJECTFILE__
+#define __OBJECTFILE__
+
+#include <stdint.h>
+#include <vector>
+#include <map>
+#include <set>
+
+
+
+//
+// These classes represent the abstract Atoms and References that are the basis of the linker.
+// An Atom and a Reference correspond to a Node and Edge in graph theory.
+//
+// A Reader is a class which parses an object file and presents it as Atoms and References.
+// All linking operations are done on Atoms and References. This makes the linker file
+// format independent.
+//
+// A Writer takes a vector of Atoms with all References resolved and produces an executable file.
+//
+//
+
+
+
+namespace ObjectFile {
+
+
+struct LineInfo
+{
+ uint32_t atomOffset;
+ const char* fileName;
+ uint32_t lineNumber;
+};
+
+
+class ReaderOptions
+{
+public:
+ ReaderOptions() : fFullyLoadArchives(false), fLoadAllObjcObjectsFromArchives(false), fFlatNamespace(false),
+ fLinkingMainExecutable(false),
+ fForFinalLinkedImage(false), fNoEHLabels(false), fForStatic(false), fForDyld(false), fMakeTentativeDefinitionsReal(false),
+ fWhyLoad(false), fRootSafe(false), fSetuidSafe(false),fDebugInfoStripping(kDebugInfoFull),
+ fImplicitlyLinkPublicDylibs(true),
+ fAddCompactUnwindEncoding(true),
+ fWarnCompactUnwind(false),
+ fRemoveDwarfUnwindIfCompactExists(false),
+ fMakeCompressedDyldInfo(false),
+ fAutoOrderInitializers(true),
+ fLogObjectFiles(false), fLogAllFiles(false),
+ fTraceDylibs(false), fTraceIndirectDylibs(false), fTraceArchives(false),
+ fTraceOutputFile(NULL), fMacVersionMin(kMinMacVersionUnset), fIPhoneVersionMin(kMinIPhoneVersionUnset) {}
+ enum DebugInfoStripping { kDebugInfoNone, kDebugInfoMinimal, kDebugInfoFull };
+ enum MacVersionMin { kMinMacVersionUnset, k10_1, k10_2, k10_3, k10_4, k10_5, k10_6 };
+ enum IPhoneVersionMin { kMinIPhoneVersionUnset, k2_0, k2_1, k2_2, k3_0 };
+
+ struct AliasPair {
+ const char* realName;
+ const char* alias;
+ };
+
+ bool fFullyLoadArchives;
+ bool fLoadAllObjcObjectsFromArchives;
+ bool fFlatNamespace;
+ bool fLinkingMainExecutable;
+ bool fForFinalLinkedImage;
+ bool fNoEHLabels;
+ bool fForStatic;
+ bool fForDyld;
+ bool fMakeTentativeDefinitionsReal;
+ bool fWhyLoad;
+ bool fRootSafe;
+ bool fSetuidSafe;
+ DebugInfoStripping fDebugInfoStripping;
+ bool fImplicitlyLinkPublicDylibs;
+ bool fAddCompactUnwindEncoding;
+ bool fWarnCompactUnwind;
+ bool fRemoveDwarfUnwindIfCompactExists;
+ bool fMakeCompressedDyldInfo;
+ bool fAutoOrderInitializers;
+ bool fLogObjectFiles;
+ bool fLogAllFiles;
+ bool fTraceDylibs;
+ bool fTraceIndirectDylibs;
+ bool fTraceArchives;
+ const char* fTraceOutputFile;
+ MacVersionMin fMacVersionMin;
+ IPhoneVersionMin fIPhoneVersionMin;
+ std::vector<AliasPair> fAliases;
+};
+
+
+class Reader
+{
+public:
+ enum DebugInfoKind { kDebugInfoNone=0, kDebugInfoStabs=1, kDebugInfoDwarf=2, kDebugInfoStabsUUID=3 };
+ struct Stab
+ {
+ class Atom* atom;
+ uint8_t type;
+ uint8_t other;
+ uint16_t desc;
+ uint32_t value;
+ const char* string;
+ };
+ enum ObjcConstraint { kObjcNone, kObjcRetainRelease, kObjcRetainReleaseOrGC, kObjcGC };
+ enum CpuConstraint { kCpuAny = 0 };
+
+ class DylibHander
+ {
+ public:
+ virtual ~DylibHander() {}
+ virtual Reader* findDylib(const char* installPath, const char* fromPath) = 0;
+ };
+
+
+ static Reader* createReader(const char* path, const ReaderOptions& options);
+
+ virtual const char* getPath() = 0;
+ virtual time_t getModificationTime() = 0;
+ virtual DebugInfoKind getDebugInfoKind() = 0;
+ virtual std::vector<class Atom*>& getAtoms() = 0;
+ virtual std::vector<class Atom*>* getJustInTimeAtomsFor(const char* name) = 0;
+ virtual std::vector<Stab>* getStabs() = 0;
+ virtual ObjcConstraint getObjCConstraint() { return kObjcNone; }
+ virtual uint32_t updateCpuConstraint(uint32_t current) { return current; }
+ virtual bool objcReplacementClasses() { return false; }
+
+ // For relocatable object files only
+ virtual bool canScatterAtoms() { return true; }
+ virtual bool optimize(const std::vector<ObjectFile::Atom*>&, std::vector<ObjectFile::Atom*>&,
+ std::vector<const char*>&, const std::set<ObjectFile::Atom*>&,
+ std::vector<ObjectFile::Atom*>&,
+ uint32_t, ObjectFile::Reader* writer,
+ ObjectFile::Atom* entryPointAtom,
+ const std::vector<const char*>& llvmOptions,
+ bool allGlobalsAReDeadStripRoots, int okind,
+ bool verbose, bool saveTemps, const char* outputFilePath,
+ bool pie, bool allowTextRelocs) { return false; }
+ virtual bool hasLongBranchStubs() { return false; }
+
+ // For Dynamic Libraries only
+ virtual const char* getInstallPath() { return NULL; }
+ virtual uint32_t getTimestamp() { return 0; }
+ virtual uint32_t getCurrentVersion() { return 0; }
+ virtual uint32_t getCompatibilityVersion() { return 0; }
+ virtual void processIndirectLibraries(DylibHander* handler) { }
+ virtual void setExplicitlyLinked() { }
+ virtual bool explicitlyLinked() { return false; }
+ virtual bool implicitlyLinked() { return false; }
+ virtual bool providedExportAtom() { return false; }
+ virtual const char* parentUmbrella() { return NULL; }
+ virtual std::vector<const char*>* getAllowableClients() { return NULL; }
+ virtual bool hasWeakExternals() { return false; }
+ virtual bool deadStrippable() { return false; }
+ virtual bool isLazyLoadedDylib() { return false; }
+
+protected:
+ Reader() {}
+ virtual ~Reader() {}
+};
+
+class Segment
+{
+public:
+ virtual const char* getName() const = 0;
+ virtual bool isContentReadable() const = 0;
+ virtual bool isContentWritable() const = 0;
+ virtual bool isContentExecutable() const = 0;
+
+ uint64_t getBaseAddress() const { return fBaseAddress; }
+ void setBaseAddress(uint64_t addr) { fBaseAddress = addr; }
+ virtual bool hasFixedAddress() const { return false; }
+
+protected:
+ Segment() : fBaseAddress(0) {}
+ virtual ~Segment() {}
+ uint64_t fBaseAddress;
+};
+
+class Reference;
+
+class Section
+{
+public:
+ unsigned int getIndex() { return fIndex; }
+ uint64_t getBaseAddress() { return fBaseAddress; }
+ void setBaseAddress(uint64_t addr) { fBaseAddress = addr; }
+ void* fOther;
+
+protected:
+ Section() : fOther(NULL), fBaseAddress(0), fIndex(0) {}
+ uint64_t fBaseAddress;
+ unsigned int fIndex;
+};
+
+
+struct Alignment
+{
+ Alignment(int p2, int m=0) : powerOf2(p2), modulus(m) {}
+ uint8_t trailingZeros() const { return (modulus==0) ? powerOf2 : __builtin_ctz(modulus); }
+ uint16_t powerOf2;
+ uint16_t modulus;
+};
+
+struct UnwindInfo
+{
+ uint32_t startOffset;
+ uint32_t unwindInfo;
+
+ typedef UnwindInfo* iterator;
+
+};
+
+
+//
+// An atom is the fundamental unit of linking. A C function or global variable is an atom.
+// An atom has content and some attributes. The content of a function atom is the instructions
+// that implement the function. The content of a global variable atom is its initial bits.
+//
+// Name:
+// The name of an atom is the label name generated by the compiler. A C compiler names foo()
+// as _foo. A C++ compiler names foo() as __Z3foov.
+// The name refers to the first byte of the content. An atom cannot have multiple entry points.
+// Such code is modeled as multiple atoms, each having a "follow on" reference to the next.
+// A "follow on" reference is a contraint to the linker to the atoms must be laid out contiguously.
+//
+// Scope:
+// An atom is in one of three scopes: translation-unit, linkage-unit, or global. These correspond
+// to the C visibility of static, hidden, default.
+//
+// DefinitionKind:
+// An atom is one of five defintion kinds:
+// regular Most atoms.
+// weak C++ compiler makes some functions weak if there might be multiple copies
+// that the linker needs to coalesce.
+// tentative A straggler from ancient C when the extern did not exist. "int foo;" is ambiguous.
+// It could be a prototype or it could be a definition.
+// external This is a "proxy" atom produced by a dylib reader. It has no content. It exists
+// so that all References can be resolved.
+// external-weak Same as external, but the definition in the dylib is weak.
+//
+// SymbolTableInclusion:
+// An atom may or may not be in the symbol table in an object file.
+// in Most atoms for functions or global data
+// not-in Anonymous atoms such literal c-strings, or other compiler generated data
+// in-never-strip Atom whose name the strip tool should never remove (e.g. REFERENCED_DYNAMICALLY in mach-o)
+//
+// Ordinal:
+// When a reader is created it is given a base ordinal number. All atoms created by the reader
+// should return a contiguous range of ordinal values that start at the base ordinal. The ordinal
+// values are used by the linker to sort the atom graph when producing the output file.
+//
+class Atom
+{
+public:
+ enum Scope { scopeTranslationUnit, scopeLinkageUnit, scopeGlobal };
+ enum DefinitionKind { kRegularDefinition, kWeakDefinition, kTentativeDefinition, kExternalDefinition, kExternalWeakDefinition, kAbsoluteSymbol };
+ enum ContentType { kUnclassifiedType, kCStringType, kCFIType, kLSDAType };
+ enum SymbolTableInclusion { kSymbolTableNotIn, kSymbolTableIn, kSymbolTableInAndNeverStrip, kSymbolTableInAsAbsolute };
+
+ virtual Reader* getFile() const = 0;
+ virtual bool getTranslationUnitSource(const char** dir, const char** name) const = 0;
+ virtual const char* getName() const = 0;
+ virtual const char* getDisplayName() const = 0;
+ virtual Scope getScope() const = 0;
+ virtual DefinitionKind getDefinitionKind() const = 0;
+ virtual ContentType getContentType() const { return kUnclassifiedType; }
+ virtual SymbolTableInclusion getSymbolTableInclusion() const = 0;
+ virtual bool dontDeadStrip() const = 0;
+ virtual bool isZeroFill() const = 0;
+ virtual bool isThumb() const = 0;
+ virtual uint64_t getSize() const = 0;
+ virtual std::vector<ObjectFile::Reference*>& getReferences() const = 0;
+ virtual bool mustRemainInSection() const = 0;
+ virtual const char* getSectionName() const = 0;
+ virtual Segment& getSegment() const = 0;
+ virtual Atom& getFollowOnAtom() const = 0;
+ virtual uint32_t getOrdinal() const = 0;
+ virtual std::vector<LineInfo>* getLineInfo() const = 0;
+ virtual Alignment getAlignment() const = 0;
+ virtual void copyRawContent(uint8_t buffer[]) const = 0;
+ virtual void setScope(Scope) = 0;
+ virtual UnwindInfo::iterator beginUnwind() { return NULL; }
+ virtual UnwindInfo::iterator endUnwind() { return NULL; }
+ virtual Reference* getLSDA() { return NULL; }
+ virtual Reference* getFDE() { return NULL; }
+ virtual Atom* getPersonalityPointer() { return NULL; }
+
+ uint64_t getSectionOffset() const { return fSectionOffset; }
+ uint64_t getAddress() const { return fSection->getBaseAddress() + fSectionOffset; }
+ class Section* getSection() const { return fSection; }
+
+ virtual void setSectionOffset(uint64_t offset) { fSectionOffset = offset; }
+ virtual void setSection(class Section* sect) { fSection = sect; }
+
+protected:
+ Atom() : fSectionOffset(0), fSection(NULL) {}
+ virtual ~Atom() {}
+
+ uint64_t fSectionOffset;
+ class Section* fSection;
+};
+
+
+//
+// A Reference is a directed edge to another Atom. When an instruction in
+// the content of an Atom refers to another Atom, that is represented by a
+// Reference.
+//
+// There are two kinds of references: direct and by-name. With a direct Reference,
+// the target is bound by the Reader that created it. For instance a reference to a
+// static would produce a direct reference. A by-name reference requires the linker
+// to find the target Atom with the required name in order to be bound.
+//
+// For a link to succeed all References must be bound.
+//
+// A Reference has an optional "from" target. This is used when the content to fix-up
+// is the difference of two Atom address. For instance, if a pointer sized data Atom
+// is to contain A - B, then the Atom would have on Reference with a target of "A" and
+// a from-target of "B".
+//
+// A Reference also has a fix-up-offset. This is the offset into the content of the
+// Atom holding the reference where the fix-up (relocation) will be applied.
+//
+//
+//
+class Reference
+{
+public:
+ enum TargetBinding { kUnboundByName, kBoundDirectly, kBoundByName, kDontBind };
+
+ virtual TargetBinding getTargetBinding() const = 0;
+ virtual TargetBinding getFromTargetBinding() const = 0;
+ virtual uint8_t getKind() const = 0;
+ virtual uint64_t getFixUpOffset() const = 0;
+ virtual const char* getTargetName() const = 0;
+ virtual Atom& getTarget() const = 0;
+ virtual uint64_t getTargetOffset() const = 0;
+ virtual Atom& getFromTarget() const = 0;
+ virtual const char* getFromTargetName() const = 0;
+ virtual uint64_t getFromTargetOffset() const = 0;
+
+ virtual void setTarget(Atom&, uint64_t offset) = 0;
+ virtual void setFromTarget(Atom&) = 0;
+ virtual const char* getDescription() const = 0;
+ virtual bool isBranch() const { return false; }
+
+protected:
+ Reference() {}
+ virtual ~Reference() {}
+};
+
+
+}; // namespace ObjectFile
+
+
+#endif // __OBJECTFILE__
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifndef __OPAQUE_SECTION__
+#define __OPAQUE_SECTION__
+
+
+#include <vector>
+
+#include "ObjectFile.h"
+
+namespace opaque_section {
+
+
+class Segment : public ObjectFile::Segment
+{
+public:
+ Segment(const char* name) { fName = name; }
+ virtual const char* getName() const { return fName; }
+ virtual bool isContentReadable() const { return true; }
+ virtual bool isContentWritable() const { return false; }
+ virtual bool isContentExecutable() const { return (strcmp(fName, "__TEXT") == 0); }
+private:
+ const char* fName;
+};
+
+
+class Reader : public ObjectFile::Reader
+{
+public:
+ Reader(const char* segmentName, const char* sectionName, const char* path,
+ const uint8_t fileContent[], uint64_t fileLength, uint32_t ordinal, const char* symbolName=NULL);
+ virtual ~Reader();
+
+ void addSectionReference(uint8_t kind, uint64_t offsetInSection, const ObjectFile::Atom* targetAtom,
+ uint64_t offsetInTarget, const ObjectFile::Atom* fromTargetAtom=NULL, uint64_t offsetInFromTarget=0);
+
+ virtual const char* getPath() { return fPath; }
+ virtual time_t getModificationTime() { return 0; }
+ virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; }
+ virtual std::vector<class ObjectFile::Atom*>& getAtoms() { return fAtoms; }
+ virtual std::vector<class ObjectFile::Atom*>* getJustInTimeAtomsFor(const char* name) { return NULL; }
+ virtual std::vector<Stab>* getStabs() { return NULL; }
+
+private:
+ const char* fPath;
+ std::vector<class ObjectFile::Atom*> fAtoms;
+};
+
+class Reference : public ObjectFile::Reference
+{
+public:
+ Reference(uint8_t kind, uint64_t fixupOffset, const ObjectFile::Atom* target, uint64_t targetOffset,
+ const ObjectFile::Atom* fromTarget=NULL, uint64_t fromTargetOffset=0)
+ : fFixUpOffset(fixupOffset), fTarget(target), fTargetOffset(targetOffset), fKind(kind),
+ fFromTarget(fromTarget), fFromTargetOffset(fromTargetOffset) {}
+ virtual ~Reference() {}
+
+
+ virtual ObjectFile::Reference::TargetBinding getTargetBinding() const { return ObjectFile::Reference::kBoundDirectly; }
+ virtual ObjectFile::Reference::TargetBinding getFromTargetBinding() const{ return ObjectFile::Reference::kDontBind; }
+ virtual uint8_t getKind() const { return fKind; }
+ virtual uint64_t getFixUpOffset() const { return fFixUpOffset; }
+ virtual const char* getTargetName() const { return fTarget->getName(); }
+ virtual ObjectFile::Atom& getTarget() const { return *((ObjectFile::Atom*)fTarget); }
+ virtual uint64_t getTargetOffset() const { return fTargetOffset; }
+ virtual ObjectFile::Atom& getFromTarget() const { return *((ObjectFile::Atom*)fFromTarget); }
+ virtual const char* getFromTargetName() const { return fFromTarget->getName(); }
+ virtual uint64_t getFromTargetOffset() const { return fFromTargetOffset; }
+ virtual void setTarget(ObjectFile::Atom&, uint64_t offset) { throw "can't set target"; }
+ virtual void setFromTarget(ObjectFile::Atom&) { throw "can't set from target"; }
+ virtual const char* getDescription() const { return "opaque section reference"; }
+
+private:
+ uint64_t fFixUpOffset;
+ const ObjectFile::Atom* fTarget;
+ uint64_t fTargetOffset;
+ uint8_t fKind;
+ const ObjectFile::Atom* fFromTarget;
+ uint64_t fFromTargetOffset;
+};
+
+
+class Atom : public ObjectFile::Atom {
+public:
+ virtual ObjectFile::Reader* getFile() const { return &fOwner; }
+ virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; }
+ virtual const char* getName() const { return fName; }
+ virtual const char* getDisplayName() const;
+ virtual Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; }
+ 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 bool isThumb() const { return false; }
+ virtual uint64_t getSize() const { return fFileLength; }
+ virtual std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(fReferences); }
+ virtual bool mustRemainInSection() const { return false; }
+ virtual const char* getSectionName() const { return fSectionName; }
+ virtual Segment& getSegment() const { return fSegment; }
+ virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); }
+ virtual uint32_t getOrdinal() const { return fOrdinal; }
+ virtual std::vector<ObjectFile::LineInfo>* getLineInfo() const { return NULL; }
+ virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(4); }
+ virtual void copyRawContent(uint8_t buffer[]) const;
+
+ virtual void setScope(Scope) { }
+
+protected:
+ friend class Reader;
+
+ Atom(Reader& owner, Segment& segment, const char* sectionName,
+ const uint8_t fileContent[], uint64_t fileLength, uint32_t ordinal, const char* symbolName);
+ virtual ~Atom() {}
+
+ Reader& fOwner;
+ Segment& fSegment;
+ const char* fName;
+ const char* fSectionName;
+ const uint8_t* fFileContent;
+ uint32_t fOrdinal;
+ uint64_t fFileLength;
+ std::vector<ObjectFile::Reference*> fReferences;
+};
+
+
+
+Atom::Atom(Reader& owner, Segment& segment, const char* sectionName, const uint8_t fileContent[], uint64_t fileLength, uint32_t ordinal, const char* symbolName)
+ : fOwner(owner), fSegment(segment), fSectionName(sectionName), fFileContent(fileContent), fOrdinal(ordinal), fFileLength(fileLength)
+{
+ if ( symbolName != NULL )
+ fName = strdup(symbolName);
+ else
+ asprintf((char**)&fName, "__section$%s%s", segment.getName(), sectionName);
+}
+
+
+Reader::Reader(const char* segmentName, const char* sectionName, const char* path, const uint8_t fileContent[],
+ uint64_t fileLength, uint32_t ordinal, const char* symbolName)
+ : fPath(path)
+{
+ fAtoms.push_back(new Atom(*this, *(new Segment(segmentName)), strdup(sectionName), fileContent, fileLength, ordinal, symbolName));
+}
+
+Reader::~Reader()
+{
+}
+
+void Reader::addSectionReference(uint8_t kind, uint64_t offsetInSection, const ObjectFile::Atom* targetAtom,
+ uint64_t offsetInTarget, const ObjectFile::Atom* fromTargetAtom, uint64_t offsetInFromTarget)
+{
+ fAtoms[0]->getReferences().push_back(new Reference(kind, offsetInSection, targetAtom, offsetInTarget, fromTargetAtom, offsetInFromTarget));
+}
+
+
+const char* Atom::getDisplayName() const
+{
+ static char name[64];
+ sprintf(name, "opaque section %s %s", fSegment.getName(), fSectionName);
+ return name;
+}
+
+
+void Atom::copyRawContent(uint8_t buffer[]) const
+{
+ memcpy(buffer, fFileContent, fFileLength);
+}
+
+
+
+};
+
+
+
+#endif // __OPAQUE_SECTION__
+
+
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2005-2008 Apple 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 <mach/vm_prot.h>
+#include <mach-o/dyld.h>
+#include <fcntl.h>
+#include <vector>
+
+#include "configure.h"
+#include "Options.h"
+#include "Architectures.hpp"
+#include "MachOFileAbstraction.hpp"
+
+extern void printLTOVersion(Options &opts);
+
+// magic to place command line in crash reports
+extern "C" char* __crashreporter_info__;
+static char crashreporterBuffer[1000];
+char* __crashreporter_info__ = crashreporterBuffer;
+
+static bool sEmitWarnings = true;
+static const char* sWarningsSideFilePath = NULL;
+static FILE* sWarningsSideFile = NULL;
+
+void warning(const char* format, ...)
+{
+ if ( sEmitWarnings ) {
+ va_list list;
+ if ( sWarningsSideFilePath != NULL ) {
+ if ( sWarningsSideFile == NULL )
+ sWarningsSideFile = fopen(sWarningsSideFilePath, "a");
+ }
+ va_start(list, format);
+ fprintf(stderr, "ld: warning: ");
+ vfprintf(stderr, format, list);
+ fprintf(stderr, "\n");
+ if ( sWarningsSideFile != NULL ) {
+ fprintf(sWarningsSideFile, "ld: warning: ");
+ vfprintf(sWarningsSideFile, format, list);
+ fprintf(sWarningsSideFile, "\n");
+ fflush(sWarningsSideFile);
+ }
+ va_end(list);
+ }
+}
+
+void throwf(const char* format, ...)
+{
+ va_list list;
+ char* p;
+ va_start(list, format);
+ vasprintf(&p, format, list);
+ va_end(list);
+
+ const char* t = p;
+ throw t;
+}
+
+Options::Options(int argc, const char* argv[])
+ : fOutputFile("a.out"), fArchitecture(0), fSubArchitecture(0), fOutputKind(kDynamicExecutable),
+ fHasPreferredSubType(false), fPrebind(false), fBindAtLoad(false), fKeepPrivateExterns(false),
+ fNeedsModuleTable(false), fIgnoreOtherArchFiles(false), fErrorOnOtherArchFiles(false), fForceSubtypeAll(false),
+ fInterposeMode(kInterposeNone), fDeadStrip(kDeadStripOff), fNameSpace(kTwoLevelNameSpace),
+ fDylibCompatVersion(0), fDylibCurrentVersion(0), fDylibInstallName(NULL), fFinalName(NULL), fEntryName("start"), fBaseAddress(0),
+ fBaseWritableAddress(0), fSplitSegs(false),
+ fExportMode(kExportDefault), fLibrarySearchMode(kSearchAllDirsForDylibsThenAllDirsForArchives),
+ fUndefinedTreatment(kUndefinedError), fMessagesPrefixedWithArchitecture(false),
+ fWeakReferenceMismatchTreatment(kWeakReferenceMismatchNonWeak),
+ fClientName(NULL),
+ fUmbrellaName(NULL), fInitFunctionName(NULL), fDotOutputFile(NULL), fExecutablePath(NULL),
+ fBundleLoader(NULL), fDtraceScriptName(NULL), fSegAddrTablePath(NULL), fMapPath(NULL),
+ fZeroPageSize(ULLONG_MAX), fStackSize(0), fStackAddr(0), fExecutableStack(false),
+ fMinimumHeaderPad(32), fSegmentAlignment(4096),
+ fCommonsMode(kCommonsIgnoreDylibs), fUUIDMode(kUUIDContent), fLocalSymbolHandling(kLocalSymbolsAll), fWarnCommons(false),
+ fVerbose(false), fKeepRelocations(false), fWarnStabs(false),
+ fTraceDylibSearching(false), fPause(false), fStatistics(false), fPrintOptions(false),
+ fSharedRegionEligible(false), fPrintOrderFileStatistics(false),
+ fReadOnlyx86Stubs(false), fPositionIndependentExecutable(false), fMaxMinimumHeaderPad(false),
+ fDeadStripDylibs(false), fAllowTextRelocs(false), fWarnTextRelocs(false),
+ fUsingLazyDylibLinking(false), fEncryptable(true), fOrderData(true), fMarkDeadStrippableDylib(false),
+ fMakeClassicDyldInfo(true), fMakeCompressedDyldInfo(true), fAllowCpuSubtypeMismatches(false), fSaveTempFiles(false)
+{
+ this->checkForClassic(argc, argv);
+ this->parsePreCommandLineEnvironmentSettings();
+ this->parse(argc, argv);
+ this->parsePostCommandLineEnvironmentSettings();
+ this->reconfigureDefaults();
+ this->checkIllegalOptionCombinations();
+}
+
+Options::~Options()
+{
+}
+
+const ObjectFile::ReaderOptions& Options::readerOptions()
+{
+ return fReaderOptions;
+}
+
+
+const char* Options::getOutputFilePath()
+{
+ return fOutputFile;
+}
+
+std::vector<Options::FileInfo>& Options::getInputFiles()
+{
+ return fInputFiles;
+}
+
+Options::OutputKind Options::outputKind()
+{
+ return fOutputKind;
+}
+
+bool Options::bindAtLoad()
+{
+ return fBindAtLoad;
+}
+
+bool Options::prebind()
+{
+ return fPrebind;
+}
+
+bool Options::fullyLoadArchives()
+{
+ return fReaderOptions.fFullyLoadArchives;
+}
+
+Options::NameSpace Options::nameSpace()
+{
+ return fNameSpace;
+}
+
+const char* Options::installPath()
+{
+ if ( fDylibInstallName != NULL )
+ return fDylibInstallName;
+ else if ( fFinalName != NULL )
+ return fFinalName;
+ else
+ return fOutputFile;
+}
+
+uint32_t Options::currentVersion()
+{
+ return fDylibCurrentVersion;
+}
+
+uint32_t Options::compatibilityVersion()
+{
+ return fDylibCompatVersion;
+}
+
+const char* Options::entryName()
+{
+ return fEntryName;
+}
+
+uint64_t Options::baseAddress()
+{
+ return fBaseAddress;
+}
+
+bool Options::keepPrivateExterns()
+{
+ return fKeepPrivateExterns;
+}
+
+bool Options::interposable(const char* name)
+{
+ switch ( fInterposeMode ) {
+ case kInterposeNone:
+ return false;
+ case kInterposeAllExternal:
+ return true;
+ case kInterposeSome:
+ return fInterposeList.contains(name);
+ }
+ throw "internal error";
+}
+
+bool Options::needsModuleTable()
+{
+ return fNeedsModuleTable;
+}
+
+bool Options::ignoreOtherArchInputFiles()
+{
+ return fIgnoreOtherArchFiles;
+}
+
+bool Options::forceCpuSubtypeAll()
+{
+ return fForceSubtypeAll;
+}
+
+bool Options::traceDylibs()
+{
+ return fReaderOptions.fTraceDylibs;
+}
+
+bool Options::traceArchives()
+{
+ return fReaderOptions.fTraceArchives;
+}
+
+Options::UndefinedTreatment Options::undefinedTreatment()
+{
+ return fUndefinedTreatment;
+}
+
+Options::WeakReferenceMismatchTreatment Options::weakReferenceMismatchTreatment()
+{
+ return fWeakReferenceMismatchTreatment;
+}
+
+const char* Options::umbrellaName()
+{
+ return fUmbrellaName;
+}
+
+std::vector<const char*>& Options::allowableClients()
+{
+ return fAllowableClients;
+}
+
+const char* Options::clientName()
+{
+ return fClientName;
+}
+
+uint64_t Options::zeroPageSize()
+{
+ return fZeroPageSize;
+}
+
+bool Options::hasCustomStack()
+{
+ return (fStackSize != 0);
+}
+
+uint64_t Options::customStackSize()
+{
+ return fStackSize;
+}
+
+uint64_t Options::customStackAddr()
+{
+ return fStackAddr;
+}
+
+bool Options::hasExecutableStack()
+{
+ return fExecutableStack;
+}
+
+std::vector<const char*>& Options::initialUndefines()
+{
+ return fInitialUndefines;
+}
+
+bool Options::printWhyLive(const char* symbolName)
+{
+ return ( fWhyLive.find(symbolName) != fWhyLive.end() );
+}
+
+
+const char* Options::initFunctionName()
+{
+ return fInitFunctionName;
+}
+
+const char* Options::dotOutputFile()
+{
+ return fDotOutputFile;
+}
+
+bool Options::hasExportRestrictList()
+{
+ return (fExportMode != kExportDefault);
+}
+
+bool Options::hasExportMaskList()
+{
+ return (fExportMode == kExportSome);
+}
+
+
+bool Options::hasWildCardExportRestrictList()
+{
+ // has -exported_symbols_list which contains some wildcards
+ return ((fExportMode == kExportSome) && fExportSymbols.hasWildCards());
+}
+
+
+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:
+ case Options::kPreload:
+ // by default unused globals in a main executable are stripped
+ return false;
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ case Options::kObjectFile:
+ case Options::kDyld:
+ case Options::kKextBundle:
+ return true;
+ }
+ return false;
+}
+
+uint32_t Options::minimumHeaderPad()
+{
+ return fMinimumHeaderPad;
+}
+
+std::vector<Options::ExtraSection>& Options::extraSections()
+{
+ return fExtraSections;
+}
+
+std::vector<Options::SectionAlignment>& Options::sectionAlignments()
+{
+ return fSectionAlignments;
+}
+
+Options::CommonsMode Options::commonsMode()
+{
+ return fCommonsMode;
+}
+
+bool Options::warnCommons()
+{
+ return fWarnCommons;
+}
+
+bool Options::keepRelocations()
+{
+ return fKeepRelocations;
+}
+
+bool Options::warnStabs()
+{
+ return fWarnStabs;
+}
+
+const char* Options::executablePath()
+{
+ return fExecutablePath;
+}
+
+Options::DeadStripMode Options::deadStrip()
+{
+ return fDeadStrip;
+}
+
+bool Options::hasExportedSymbolOrder()
+{
+ return (fExportSymbolsOrder.size() > 0);
+}
+
+bool Options::exportedSymbolOrder(const char* sym, unsigned int* order)
+{
+ NameToOrder::iterator pos = fExportSymbolsOrder.find(sym);
+ if ( pos != fExportSymbolsOrder.end() ) {
+ *order = pos->second;
+ return true;
+ }
+ else {
+ *order = 0xFFFFFFFF;
+ return false;
+ }
+}
+
+void Options::loadSymbolOrderFile(const char* fileOfExports, NameToOrder& orderMapping)
+{
+ // read in whole file
+ int fd = ::open(fileOfExports, O_RDONLY, 0);
+ if ( fd == -1 )
+ throwf("can't open -exported_symbols_order file: %s", fileOfExports);
+ struct stat stat_buf;
+ ::fstat(fd, &stat_buf);
+ char* p = (char*)malloc(stat_buf.st_size);
+ if ( p == NULL )
+ throwf("can't process -exported_symbols_order file: %s", fileOfExports);
+
+ if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size )
+ throwf("can't read -exported_symbols_order file: %s", fileOfExports);
+
+ ::close(fd);
+
+ // parse into symbols and add to hash_set
+ unsigned int count = 0;
+ char * const end = &p[stat_buf.st_size];
+ enum { lineStart, inSymbol, inComment } state = lineStart;
+ char* symbolStart = NULL;
+ for (char* s = p; s < end; ++s ) {
+ switch ( state ) {
+ case lineStart:
+ if ( *s =='#' ) {
+ state = inComment;
+ }
+ else if ( !isspace(*s) ) {
+ state = inSymbol;
+ symbolStart = s;
+ }
+ break;
+ case inSymbol:
+ if ( (*s == '\n') || (*s == '\r') ) {
+ *s = '\0';
+ // removing any trailing spaces
+ char* last = s-1;
+ while ( isspace(*last) ) {
+ *last = '\0';
+ --last;
+ }
+ orderMapping[symbolStart] = ++count;
+ symbolStart = NULL;
+ state = lineStart;
+ }
+ break;
+ case inComment:
+ if ( (*s == '\n') || (*s == '\r') )
+ state = lineStart;
+ break;
+ }
+ }
+ if ( state == inSymbol ) {
+ warning("missing line-end at end of file \"%s\"", fileOfExports);
+ int len = end-symbolStart+1;
+ char* temp = new char[len];
+ strlcpy(temp, symbolStart, len);
+
+ // remove any trailing spaces
+ char* last = &temp[len-2];
+ while ( isspace(*last) ) {
+ *last = '\0';
+ --last;
+ }
+ orderMapping[temp] = ++count;
+ }
+
+ // Note: we do not free() the malloc buffer, because the strings are used by the export-set hash table
+}
+
+
+bool Options::shouldExport(const char* symbolName)
+{
+ switch (fExportMode) {
+ case kExportSome:
+ return fExportSymbols.contains(symbolName);
+ case kDontExportSome:
+ return ! fDontExportSymbols.contains(symbolName);
+ case kExportDefault:
+ return true;
+ }
+ throw "internal error";
+}
+
+bool Options::keepLocalSymbol(const char* symbolName)
+{
+ switch (fLocalSymbolHandling) {
+ case kLocalSymbolsAll:
+ return true;
+ case kLocalSymbolsNone:
+ return false;
+ case kLocalSymbolsSelectiveInclude:
+ return fLocalSymbolsIncluded.contains(symbolName);
+ case kLocalSymbolsSelectiveExclude:
+ return ! fLocalSymbolsExcluded.contains(symbolName);
+ }
+ throw "internal error";
+}
+
+void Options::parseArch(const char* architecture)
+{
+ if ( architecture == NULL )
+ throw "-arch must be followed by an architecture string";
+ if ( strcmp(architecture, "ppc") == 0 ) {
+ fArchitecture = CPU_TYPE_POWERPC;
+ fSubArchitecture = CPU_SUBTYPE_POWERPC_ALL;
+ }
+ else if ( strcmp(architecture, "ppc64") == 0 ) {
+ fArchitecture = CPU_TYPE_POWERPC64;
+ fSubArchitecture = CPU_SUBTYPE_POWERPC_ALL;
+ }
+ else if ( strcmp(architecture, "i386") == 0 ) {
+ fArchitecture = CPU_TYPE_I386;
+ fSubArchitecture = CPU_SUBTYPE_I386_ALL;
+ }
+ else if ( strcmp(architecture, "x86_64") == 0 ) {
+ fArchitecture = CPU_TYPE_X86_64;
+ fSubArchitecture = CPU_SUBTYPE_X86_64_ALL;
+ }
+ else if ( strcmp(architecture, "arm") == 0 ) {
+ fArchitecture = CPU_TYPE_ARM;
+ fSubArchitecture = CPU_SUBTYPE_ARM_ALL;
+ }
+ // compatibility support for cpu-sub-types
+ else if ( strcmp(architecture, "ppc750") == 0 ) {
+ fArchitecture = CPU_TYPE_POWERPC;
+ fSubArchitecture = CPU_SUBTYPE_POWERPC_750;
+ fHasPreferredSubType = true;
+ }
+ else if ( strcmp(architecture, "ppc7400") == 0 ) {
+ fArchitecture = CPU_TYPE_POWERPC;
+ fSubArchitecture = CPU_SUBTYPE_POWERPC_7400;
+ fHasPreferredSubType = true;
+ }
+ else if ( strcmp(architecture, "ppc7450") == 0 ) {
+ fArchitecture = CPU_TYPE_POWERPC;
+ fSubArchitecture = CPU_SUBTYPE_POWERPC_7450;
+ fHasPreferredSubType = true;
+ }
+ else if ( strcmp(architecture, "ppc970") == 0 ) {
+ fArchitecture = CPU_TYPE_POWERPC;
+ fSubArchitecture = CPU_SUBTYPE_POWERPC_970;
+ fHasPreferredSubType = true;
+ }
+ else if ( strcmp(architecture, "armv6") == 0 ) {
+ fArchitecture = CPU_TYPE_ARM;
+ fSubArchitecture = CPU_SUBTYPE_ARM_V6;
+ fHasPreferredSubType = true;
+ }
+ else if ( strcmp(architecture, "armv5") == 0 ) {
+ fArchitecture = CPU_TYPE_ARM;
+ fSubArchitecture = CPU_SUBTYPE_ARM_V5TEJ;
+ fHasPreferredSubType = true;
+ }
+ else if ( strcmp(architecture, "armv4t") == 0 ) {
+ fArchitecture = CPU_TYPE_ARM;
+ fSubArchitecture = CPU_SUBTYPE_ARM_V4T;
+ fHasPreferredSubType = true;
+ }
+ else if ( strcmp(architecture, "xscale") == 0 ) {
+ fArchitecture = CPU_TYPE_ARM;
+ fSubArchitecture = CPU_SUBTYPE_ARM_XSCALE;
+ fHasPreferredSubType = true;
+ }
+ else if ( strcmp(architecture, "armv7") == 0 ) {
+ fArchitecture = CPU_TYPE_ARM;
+ fSubArchitecture = CPU_SUBTYPE_ARM_V7;
+ fHasPreferredSubType = true;
+ }
+ else
+ throwf("unknown/unsupported architecture name for: -arch %s", architecture);
+}
+
+bool Options::checkForFile(const char* format, const char* dir, const char* rootName, FileInfo& result)
+{
+ struct stat statBuffer;
+ char possiblePath[strlen(dir)+strlen(rootName)+strlen(format)+8];
+ sprintf(possiblePath, format, dir, rootName);
+ bool found = (stat(possiblePath, &statBuffer) == 0);
+ if ( fTraceDylibSearching )
+ printf("[Logging for XBS]%sfound library: '%s'\n", (found ? " " : " not "), possiblePath);
+ if ( found ) {
+ result.path = strdup(possiblePath);
+ result.fileLen = statBuffer.st_size;
+ result.modTime = statBuffer.st_mtime;
+ return true;
+ }
+ return false;
+}
+
+
+Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly)
+{
+ FileInfo result;
+ const int rootNameLen = strlen(rootName);
+ // if rootName ends in .o there is no .a vs .dylib choice
+ if ( (rootNameLen > 3) && (strcmp(&rootName[rootNameLen-2], ".o") == 0) ) {
+ for (std::vector<const char*>::iterator it = fLibrarySearchPaths.begin();
+ it != fLibrarySearchPaths.end();
+ it++) {
+ const char* dir = *it;
+ if ( checkForFile("%s/%s", dir, rootName, result) )
+ return result;
+ }
+ }
+ else {
+ bool lookForDylibs = ( fOutputKind != Options::kDyld);
+ switch ( fLibrarySearchMode ) {
+ case kSearchAllDirsForDylibsThenAllDirsForArchives:
+ // first look in all directories for just for dylibs
+ if ( lookForDylibs ) {
+ for (std::vector<const char*>::iterator it = fLibrarySearchPaths.begin();
+ it != fLibrarySearchPaths.end();
+ it++) {
+ const char* dir = *it;
+ if ( checkForFile("%s/lib%s.dylib", dir, rootName, result) )
+ return result;
+ }
+ for (std::vector<const char*>::iterator it = fLibrarySearchPaths.begin();
+ it != fLibrarySearchPaths.end();
+ it++) {
+ const char* dir = *it;
+ if ( checkForFile("%s/lib%s.so", dir, rootName, result) )
+ return result;
+ }
+ }
+ // next look in all directories for just for archives
+ if ( !dylibsOnly ) {
+ for (std::vector<const char*>::iterator it = fLibrarySearchPaths.begin();
+ it != fLibrarySearchPaths.end();
+ it++) {
+ const char* dir = *it;
+ if ( checkForFile("%s/lib%s.a", dir, rootName, result) )
+ return result;
+ }
+ }
+ break;
+
+ case kSearchDylibAndArchiveInEachDir:
+ // look in each directory for just for a dylib then for an archive
+ for (std::vector<const char*>::iterator it = fLibrarySearchPaths.begin();
+ it != fLibrarySearchPaths.end();
+ it++) {
+ const char* dir = *it;
+ if ( lookForDylibs && checkForFile("%s/lib%s.dylib", dir, rootName, result) )
+ return result;
+ if ( lookForDylibs && checkForFile("%s/lib%s.so", dir, rootName, result) )
+ return result;
+ if ( !dylibsOnly && checkForFile("%s/lib%s.a", dir, rootName, result) )
+ return result;
+ }
+ break;
+ }
+ }
+ throwf("library not found for -l%s", rootName);
+}
+
+Options::FileInfo Options::findFramework(const char* frameworkName)
+{
+ if ( frameworkName == NULL )
+ throw "-framework missing next argument";
+ char temp[strlen(frameworkName)+1];
+ strcpy(temp, frameworkName);
+ const char* name = temp;
+ const char* suffix = NULL;
+ char* comma = strchr(temp, ',');
+ if ( comma != NULL ) {
+ *comma = '\0';
+ suffix = &comma[1];
+ }
+ return findFramework(name, suffix);
+}
+
+Options::FileInfo Options::findFramework(const char* rootName, const char* suffix)
+{
+ struct stat statBuffer;
+ for (std::vector<const char*>::iterator it = fFrameworkSearchPaths.begin();
+ it != fFrameworkSearchPaths.end();
+ it++) {
+ // ??? Shouldn't we be using String here and just initializing it?
+ // ??? Use str.c_str () to pull out the string for the stat call.
+ const char* dir = *it;
+ char possiblePath[PATH_MAX];
+ strcpy(possiblePath, dir);
+ strcat(possiblePath, "/");
+ strcat(possiblePath, rootName);
+ strcat(possiblePath, ".framework/");
+ strcat(possiblePath, rootName);
+ if ( suffix != NULL ) {
+ char realPath[PATH_MAX];
+ // no symlink in framework to suffix variants, so follow main symlink
+ if ( realpath(possiblePath, realPath) != NULL ) {
+ strcpy(possiblePath, realPath);
+ strcat(possiblePath, suffix);
+ }
+ }
+ bool found = (stat(possiblePath, &statBuffer) == 0);
+ if ( fTraceDylibSearching )
+ printf("[Logging for XBS]%sfound framework: '%s'\n",
+ (found ? " " : " not "), possiblePath);
+ if ( found ) {
+ FileInfo result;
+ result.path = strdup(possiblePath);
+ result.fileLen = statBuffer.st_size;
+ result.modTime = statBuffer.st_mtime;
+ return result;
+ }
+ }
+ // try without suffix
+ if ( suffix != NULL )
+ return findFramework(rootName, NULL);
+ else
+ throwf("framework not found %s", rootName);
+}
+
+Options::FileInfo Options::findFile(const char* path)
+{
+ FileInfo result;
+ struct stat statBuffer;
+
+ // if absolute path and not a .o file, the use SDK prefix
+ if ( (path[0] == '/') && (strcmp(&path[strlen(path)-2], ".o") != 0) ) {
+ const int pathLen = strlen(path);
+ for (std::vector<const char*>::iterator it = fSDKPaths.begin(); it != fSDKPaths.end(); it++) {
+ // ??? Shouldn't we be using String here?
+ const char* sdkPathDir = *it;
+ const int sdkPathDirLen = strlen(sdkPathDir);
+ char possiblePath[sdkPathDirLen+pathLen+4];
+ strcpy(possiblePath, sdkPathDir);
+ if ( possiblePath[sdkPathDirLen-1] == '/' )
+ possiblePath[sdkPathDirLen-1] = '\0';
+ strcat(possiblePath, path);
+ if ( stat(possiblePath, &statBuffer) == 0 ) {
+ result.path = strdup(possiblePath);
+ result.fileLen = statBuffer.st_size;
+ result.modTime = statBuffer.st_mtime;
+ return result;
+ }
+ }
+ }
+ // try raw path
+ if ( stat(path, &statBuffer) == 0 ) {
+ result.path = strdup(path);
+ result.fileLen = statBuffer.st_size;
+ result.modTime = statBuffer.st_mtime;
+ return result;
+ }
+
+ // try @executable_path substitution
+ if ( (strncmp(path, "@executable_path/", 17) == 0) && (fExecutablePath != NULL) ) {
+ char newPath[strlen(fExecutablePath) + strlen(path)];
+ strcpy(newPath, fExecutablePath);
+ char* addPoint = strrchr(newPath,'/');
+ if ( addPoint != NULL )
+ strcpy(&addPoint[1], &path[17]);
+ else
+ strcpy(newPath, &path[17]);
+ if ( stat(newPath, &statBuffer) == 0 ) {
+ result.path = strdup(newPath);
+ result.fileLen = statBuffer.st_size;
+ result.modTime = statBuffer.st_mtime;
+ return result;
+ }
+ }
+
+ // not found
+ throwf("file not found: %s", path);
+}
+
+Options::FileInfo Options::findFileUsingPaths(const char* path)
+{
+ FileInfo result;
+
+ const char* lastSlash = strrchr(path, '/');
+ const char* leafName = (lastSlash == NULL) ? path : &lastSlash[1];
+
+ // Is this in a framework?
+ // /path/Foo.framework/Foo ==> true (Foo)
+ // /path/Foo.framework/Frameworks/Bar.framework/Bar ==> true (Bar)
+ // /path/Foo.framework/Resources/Bar ==> false
+ bool isFramework = false;
+ if ( lastSlash != NULL ) {
+ char frameworkDir[strlen(leafName) + 20];
+ strcpy(frameworkDir, "/");
+ strcat(frameworkDir, leafName);
+ strcat(frameworkDir, ".framework/");
+ if ( strstr(path, frameworkDir) != NULL )
+ isFramework = true;
+ }
+
+ // These are abbreviated versions of the routines findFramework and findLibrary above
+ // because we already know the final name of the file that we're looking for and so
+ // don't need to try variations, just paths. We do need to add the additional bits
+ // onto the framework path though.
+ if ( isFramework ) {
+ for (std::vector<const char*>::iterator it = fFrameworkSearchPaths.begin();
+ it != fFrameworkSearchPaths.end();
+ it++) {
+ const char* dir = *it;
+ char possiblePath[PATH_MAX];
+ strcpy(possiblePath, dir);
+ strcat(possiblePath, "/");
+ strcat(possiblePath, leafName);
+ strcat(possiblePath, ".framework");
+
+ //fprintf(stderr,"Finding Framework: %s/%s, leafName=%s\n", possiblePath, leafName, leafName);
+ if ( checkForFile("%s/%s", possiblePath, leafName, result) )
+ return result;
+ }
+ }
+ else {
+ // if this is a .dylib inside a framework, do not search -L paths
+ // <rdar://problem/5427952> ld64's re-export cycle detection logic prevents use of X11 libGL on Leopard
+ int leafLen = strlen(leafName);
+ bool embeddedDylib = ( (leafLen > 6)
+ && (strcmp(&leafName[leafLen-6], ".dylib") == 0)
+ && (strstr(path, ".framework/") != NULL) );
+ if ( !embeddedDylib ) {
+ for (std::vector<const char*>::iterator it = fLibrarySearchPaths.begin();
+ it != fLibrarySearchPaths.end();
+ it++) {
+ const char* dir = *it;
+ //fprintf(stderr,"Finding Library: %s/%s\n", dir, leafName);
+ if ( checkForFile("%s/%s", dir, leafName, result) )
+ return result;
+ }
+ }
+ }
+
+ // If we didn't find it fall back to findFile.
+ return findFile(path);
+}
+
+
+void Options::parseSegAddrTable(const char* segAddrPath, const char* installPath)
+{
+ FILE* file = fopen(segAddrPath, "r");
+ if ( file == NULL ) {
+ warning("-seg_addr_table file cannot be read: %s", segAddrPath);
+ return;
+ }
+
+ char path[PATH_MAX];
+ uint64_t firstColumAddress = 0;
+ uint64_t secondColumAddress = 0;
+ bool hasSecondColumn = false;
+ while ( fgets(path, PATH_MAX, file) != NULL ) {
+ path[PATH_MAX-1] = '\0';
+ char* eol = strchr(path, '\n');
+ if ( eol != NULL )
+ *eol = '\0';
+ // ignore lines not starting with 0x number
+ if ( (path[0] == '0') && (path[1] == 'x') ) {
+ char* p;
+ firstColumAddress = strtoull(path, &p, 16);
+ while ( isspace(*p) )
+ ++p;
+ // see if second column is a number
+ if ( (p[0] == '0') && (p[1] == 'x') ) {
+ secondColumAddress = strtoull(p, &p, 16);
+ hasSecondColumn = true;
+ while ( isspace(*p) )
+ ++p;
+ }
+ while ( isspace(*p) )
+ ++p;
+ if ( p[0] == '/' ) {
+ // remove any trailing whitespace
+ for(char* end = eol-1; (end > p) && isspace(*end); --end)
+ *end = '\0';
+ // see if this line is for the dylib being linked
+ if ( strcmp(p, installPath) == 0 ) {
+ fBaseAddress = firstColumAddress;
+ if ( hasSecondColumn ) {
+ fBaseWritableAddress = secondColumAddress;
+ fSplitSegs = true;
+ }
+ break; // out of while loop
+ }
+ }
+ }
+ }
+
+ fclose(file);
+}
+
+void Options::loadFileList(const char* fileOfPaths)
+{
+ FILE* file;
+ const char* comma = strrchr(fileOfPaths, ',');
+ const char* prefix = NULL;
+ if ( comma != NULL ) {
+ // <rdar://problem/5907981> -filelist fails with comma in path
+ file = fopen(fileOfPaths, "r");
+ if ( file == 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[PATH_MAX];
+ while ( fgets(path, PATH_MAX, file) != NULL ) {
+ path[PATH_MAX-1] = '\0';
+ char* eol = strchr(path, '\n');
+ if ( eol != NULL )
+ *eol = '\0';
+ 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);
+}
+
+bool Options::SetWithWildcards::hasWildCards(const char* symbol)
+{
+ // an exported symbol name containing *, ?, or [ requires wildcard matching
+ return ( strpbrk(symbol, "*?[") != NULL );
+}
+
+void Options::SetWithWildcards::insert(const char* symbol)
+{
+ if ( hasWildCards(symbol) )
+ fWildCard.push_back(symbol);
+ else
+ fRegular.insert(symbol);
+}
+
+bool Options::SetWithWildcards::contains(const char* symbol)
+{
+ // first look at hash table on non-wildcard symbols
+ if ( fRegular.find(symbol) != fRegular.end() )
+ return true;
+ // next walk list of wild card symbols looking for a match
+ for(std::vector<const char*>::iterator it = fWildCard.begin(); it != fWildCard.end(); ++it) {
+ if ( wildCardMatch(*it, symbol) )
+ return true;
+ }
+ return false;
+}
+
+
+bool Options::SetWithWildcards::inCharRange(const char*& p, unsigned char c)
+{
+ ++p; // find end
+ const char* b = p;
+ while ( *p != '\0' ) {
+ if ( *p == ']') {
+ const char* e = p;
+ // found beginining [ and ending ]
+ unsigned char last = '\0';
+ for ( const char* s = b; s < e; ++s ) {
+ if ( *s == '-' ) {
+ unsigned char next = *(++s);
+ if ( (last <= c) && (c <= next) )
+ return true;
+ ++s;
+ }
+ else {
+ if ( *s == c )
+ return true;
+ last = *s;
+ }
+ }
+ return false;
+ }
+ ++p;
+ }
+ return false;
+}
+
+bool Options::SetWithWildcards::wildCardMatch(const char* pattern, const char* symbol)
+{
+ const char* s = symbol;
+ for (const char* p = pattern; *p != '\0'; ++p) {
+ switch ( *p ) {
+ case '*':
+ if ( p[1] == '\0' )
+ return true;
+ for (const char* t = s; *t != '\0'; ++t) {
+ if ( wildCardMatch(&p[1], t) )
+ return true;
+ }
+ return false;
+ case '?':
+ if ( *s == '\0' )
+ return false;
+ ++s;
+ break;
+ case '[':
+ if ( ! inCharRange(p, *s) )
+ return false;
+ ++s;
+ break;
+ default:
+ if ( *s != *p )
+ return false;
+ ++s;
+ }
+ }
+ return (*s == '\0');
+}
+
+
+void Options::loadExportFile(const char* fileOfExports, const char* option, SetWithWildcards& set)
+{
+ // read in whole file
+ int fd = ::open(fileOfExports, O_RDONLY, 0);
+ if ( fd == -1 )
+ throwf("can't open %s file: %s", option, fileOfExports);
+ struct stat stat_buf;
+ ::fstat(fd, &stat_buf);
+ char* p = (char*)malloc(stat_buf.st_size);
+ if ( p == NULL )
+ throwf("can't process %s file: %s", option, fileOfExports);
+
+ if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size )
+ throwf("can't read %s file: %s", option, fileOfExports);
+
+ ::close(fd);
+
+ // parse into symbols and add to hash_set
+ char * const end = &p[stat_buf.st_size];
+ enum { lineStart, inSymbol, inComment } state = lineStart;
+ char* symbolStart = NULL;
+ for (char* s = p; s < end; ++s ) {
+ switch ( state ) {
+ case lineStart:
+ if ( *s =='#' ) {
+ state = inComment;
+ }
+ else if ( !isspace(*s) ) {
+ state = inSymbol;
+ symbolStart = s;
+ }
+ break;
+ case inSymbol:
+ if ( (*s == '\n') || (*s == '\r') ) {
+ *s = '\0';
+ // removing any trailing spaces
+ char* last = s-1;
+ while ( isspace(*last) ) {
+ *last = '\0';
+ --last;
+ }
+ set.insert(symbolStart);
+ symbolStart = NULL;
+ state = lineStart;
+ }
+ break;
+ case inComment:
+ if ( (*s == '\n') || (*s == '\r') )
+ state = lineStart;
+ break;
+ }
+ }
+ if ( state == inSymbol ) {
+ warning("missing line-end at end of file \"%s\"", fileOfExports);
+ int len = end-symbolStart+1;
+ char* temp = new char[len];
+ strlcpy(temp, symbolStart, len);
+
+ // remove any trailing spaces
+ char* last = &temp[len-2];
+ while ( isspace(*last) ) {
+ *last = '\0';
+ --last;
+ }
+ set.insert(temp);
+ }
+
+ // Note: we do not free() the malloc buffer, because the strings are used by the export-set hash table
+}
+
+void Options::parseAliasFile(const char* fileOfAliases)
+{
+ // read in whole file
+ int fd = ::open(fileOfAliases, O_RDONLY, 0);
+ if ( fd == -1 )
+ throwf("can't open alias file: %s", fileOfAliases);
+ struct stat stat_buf;
+ ::fstat(fd, &stat_buf);
+ char* p = (char*)malloc(stat_buf.st_size+1);
+ if ( p == NULL )
+ throwf("can't process alias file: %s", fileOfAliases);
+
+ if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size )
+ throwf("can't read alias file: %s", fileOfAliases);
+ p[stat_buf.st_size] = '\n';
+ ::close(fd);
+
+ // parse into symbols and add to fAliases
+ ObjectFile::ReaderOptions::AliasPair pair;
+ char * const end = &p[stat_buf.st_size+1];
+ enum { lineStart, inRealName, inBetween, inAliasName, inComment } state = lineStart;
+ int lineNumber = 1;
+ for (char* s = p; s < end; ++s ) {
+ switch ( state ) {
+ case lineStart:
+ if ( *s =='#' ) {
+ state = inComment;
+ }
+ else if ( !isspace(*s) ) {
+ state = inRealName;
+ pair.realName = s;
+ }
+ break;
+ case inRealName:
+ if ( *s == '\n' ) {
+ warning("line needs two symbols but has only one at line #%d in \"%s\"", lineNumber, fileOfAliases);
+ ++lineNumber;
+ state = lineStart;
+ }
+ else if ( isspace(*s) ) {
+ *s = '\0';
+ state = inBetween;
+ }
+ break;
+ case inBetween:
+ if ( *s == '\n' ) {
+ warning("line needs two symbols but has only one at line #%d in \"%s\"", lineNumber, fileOfAliases);
+ ++lineNumber;
+ state = lineStart;
+ }
+ else if ( ! isspace(*s) ) {
+ state = inAliasName;
+ pair.alias = s;
+ }
+ break;
+ case inAliasName:
+ if ( *s =='#' ) {
+ *s = '\0';
+ // removing any trailing spaces
+ char* last = s-1;
+ while ( isspace(*last) ) {
+ *last = '\0';
+ --last;
+ }
+ fReaderOptions.fAliases.push_back(pair);
+ state = inComment;
+ }
+ else if ( *s == '\n' ) {
+ *s = '\0';
+ // removing any trailing spaces
+ char* last = s-1;
+ while ( isspace(*last) ) {
+ *last = '\0';
+ --last;
+ }
+ fReaderOptions.fAliases.push_back(pair);
+ state = lineStart;
+ }
+ break;
+ case inComment:
+ if ( *s == '\n' )
+ state = lineStart;
+ break;
+ }
+ }
+
+ // Note: we do not free() the malloc buffer, because the strings therein are used by fAliases
+}
+
+
+
+void Options::setUndefinedTreatment(const char* treatment)
+{
+ if ( treatment == NULL )
+ throw "-undefined missing [ warning | error | suppress | dynamic_lookup ]";
+
+ if ( strcmp(treatment, "warning") == 0 )
+ fUndefinedTreatment = kUndefinedWarning;
+ else if ( strcmp(treatment, "error") == 0 )
+ fUndefinedTreatment = kUndefinedError;
+ else if ( strcmp(treatment, "suppress") == 0 )
+ fUndefinedTreatment = kUndefinedSuppress;
+ else if ( strcmp(treatment, "dynamic_lookup") == 0 )
+ fUndefinedTreatment = kUndefinedDynamicLookup;
+ else
+ throw "invalid option to -undefined [ warning | error | suppress | dynamic_lookup ]";
+}
+
+Options::Treatment Options::parseTreatment(const char* treatment)
+{
+ if ( treatment == NULL )
+ return kNULL;
+
+ if ( strcmp(treatment, "warning") == 0 )
+ return kWarning;
+ else if ( strcmp(treatment, "error") == 0 )
+ return kError;
+ else if ( strcmp(treatment, "suppress") == 0 )
+ return kSuppress;
+ else
+ return kInvalid;
+}
+
+void Options::setMacOSXVersionMin(const char* version)
+{
+ if ( version == NULL )
+ throw "-macosx_version_min argument missing";
+
+ if ( (strncmp(version, "10.", 3) == 0) && isdigit(version[3]) ) {
+ int num = version[3] - '0';
+ switch ( num ) {
+ case 0:
+ case 1:
+ fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_1;
+ break;
+ case 2:
+ fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_2;
+ break;
+ case 3:
+ fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_3;
+ break;
+ case 4:
+ fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_4;
+ break;
+ case 5:
+ fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_5;
+ break;
+ case 6:
+ fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_6;
+ break;
+ default:
+ fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_6;
+ break;
+ }
+ }
+ else {
+ warning("unknown option to -macosx_version_min, not 10.x");
+ }
+}
+
+void Options::setIPhoneVersionMin(const char* version)
+{
+ if ( version == NULL )
+ throw "-iphoneos_version_min argument missing";
+
+ if ( strncmp(version, "1.", 2) == 0 ) {
+ warning("pre-2.0 iPhone OS version not supported");
+ fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_0;
+ }
+ else if ( strncmp(version, "2.", 2) == 0 ) {
+ int num = version[2] - '0';
+ switch ( num ) {
+ case 0:
+ fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_0;
+ break;
+ case 1:
+ fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_1;
+ break;
+ case 2:
+ fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_2;
+ break;
+ default:
+ fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_2;
+ break;
+ }
+ }
+ else if ( strncmp(version, "3.", 2) == 0 ) {
+ int num = version[2] - '0';
+ switch ( num ) {
+ case 0:
+ fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k3_0;
+ break;
+ default:
+ fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k3_0;
+ break;
+ }
+ }
+ else {
+ warning("unknown option to -iphoneos_version_min, not 2.x or 3.x");
+ }
+}
+
+bool Options::minOS(ObjectFile::ReaderOptions::MacVersionMin requiredMacMin, ObjectFile::ReaderOptions::IPhoneVersionMin requirediPhoneOSMin)
+{
+ if ( fReaderOptions.fMacVersionMin != ObjectFile::ReaderOptions::kMinMacVersionUnset ) {
+ return ( fReaderOptions.fMacVersionMin >= requiredMacMin );
+ }
+ else {
+ return ( fReaderOptions.fIPhoneVersionMin >= requirediPhoneOSMin);
+ }
+}
+
+
+void Options::setWeakReferenceMismatchTreatment(const char* treatment)
+{
+ if ( treatment == NULL )
+ throw "-weak_reference_mismatches missing [ error | weak | non-weak ]";
+
+ if ( strcmp(treatment, "error") == 0 )
+ fWeakReferenceMismatchTreatment = kWeakReferenceMismatchError;
+ else if ( strcmp(treatment, "weak") == 0 )
+ fWeakReferenceMismatchTreatment = kWeakReferenceMismatchWeak;
+ else if ( strcmp(treatment, "non-weak") == 0 )
+ fWeakReferenceMismatchTreatment = kWeakReferenceMismatchNonWeak;
+ else
+ throw "invalid option to -weak_reference_mismatches [ error | weak | non-weak ]";
+}
+
+Options::CommonsMode Options::parseCommonsTreatment(const char* mode)
+{
+ if ( mode == NULL )
+ throw "-commons missing [ ignore_dylibs | use_dylibs | error ]";
+
+ if ( strcmp(mode, "ignore_dylibs") == 0 )
+ return kCommonsIgnoreDylibs;
+ else if ( strcmp(mode, "use_dylibs") == 0 )
+ return kCommonsOverriddenByDylibs;
+ else if ( strcmp(mode, "error") == 0 )
+ return kCommonsConflictsDylibsError;
+ else
+ throw "invalid option to -commons [ ignore_dylibs | use_dylibs | error ]";
+}
+
+void Options::addDylibOverride(const char* paths)
+{
+ if ( paths == NULL )
+ throw "-dylib_file must followed by two colon separated paths";
+ const char* colon = strchr(paths, ':');
+ if ( colon == NULL )
+ throw "-dylib_file must followed by two colon separated paths";
+ int len = colon-paths;
+ char* target = new char[len+2];
+ strncpy(target, paths, len);
+ target[len] = '\0';
+ DylibOverride entry;
+ entry.installName = target;
+ entry.useInstead = &colon[1];
+ fDylibOverrides.push_back(entry);
+}
+
+uint64_t Options::parseAddress(const char* addr)
+{
+ char* endptr;
+ uint64_t result = strtoull(addr, &endptr, 16);
+ return result;
+}
+
+uint32_t Options::parseProtection(const char* prot)
+{
+ uint32_t result = 0;
+ for(const char* p = prot; *p != '\0'; ++p) {
+ switch(tolower(*p)) {
+ case 'r':
+ result |= VM_PROT_READ;
+ break;
+ case 'w':
+ result |= VM_PROT_WRITE;
+ break;
+ case 'x':
+ result |= VM_PROT_EXECUTE;
+ break;
+ case '-':
+ break;
+ default:
+ throwf("unknown -segprot lettter in %s", prot);
+ }
+ }
+ return result;
+}
+
+
+
+//
+// Parses number of form X[.Y[.Z]] into a uint32_t where the nibbles are xxxx.yy.zz
+//
+//
+uint32_t Options::parseVersionNumber(const char* versionString)
+{
+ unsigned long x = 0;
+ unsigned long y = 0;
+ unsigned long z = 0;
+ char* end;
+ x = strtoul(versionString, &end, 10);
+ if ( *end == '.' ) {
+ y = strtoul(&end[1], &end, 10);
+ if ( *end == '.' ) {
+ z = strtoul(&end[1], &end, 10);
+ }
+ }
+ if ( (*end != '\0') || (x > 0xffff) || (y > 0xff) || (z > 0xff) )
+ throwf("malformed version number: %s", versionString);
+
+ return (x << 16) | ( y << 8 ) | z;
+}
+
+static const char* cstringSymbolName(const char* orderFileString)
+{
+ char* result;
+ asprintf(&result, "cstring=%s", orderFileString);
+ // convert escaped characters
+ char* d = result;
+ for(const char* s=result; *s != '\0'; ++s, ++d) {
+ if ( *s == '\\' ) {
+ ++s;
+ switch ( *s ) {
+ case 'n':
+ *d = '\n';
+ break;
+ case 't':
+ *d = '\t';
+ break;
+ case 'v':
+ *d = '\v';
+ break;
+ case 'b':
+ *d = '\b';
+ break;
+ case 'r':
+ *d = '\r';
+ break;
+ case 'f':
+ *d = '\f';
+ break;
+ case 'a':
+ *d = '\a';
+ break;
+ case '\\':
+ *d = '\\';
+ break;
+ case '?':
+ *d = '\?';
+ break;
+ case '\'':
+ *d = '\r';
+ break;
+ case '\"':
+ *d = '\"';
+ break;
+ case 'x':
+ // hexadecimal value of char
+ {
+ ++s;
+ char value = 0;
+ while ( isxdigit(*s) ) {
+ value *= 16;
+ if ( isdigit(*s) )
+ value += (*s-'0');
+ else
+ value += ((toupper(*s)-'A') + 10);
+ ++s;
+ }
+ *d = value;
+ }
+ break;
+ default:
+ if ( isdigit(*s) ) {
+ // octal value of char
+ char value = 0;
+ while ( isdigit(*s) ) {
+ value = (value << 3) + (*s-'0');
+ ++s;
+ }
+ *d = value;
+ }
+ }
+ }
+ else {
+ *d = *s;
+ }
+ }
+ *d = '\0';
+ return result;
+}
+
+void Options::parseOrderFile(const char* path, bool cstring)
+{
+ // order files override auto-ordering
+ fReaderOptions.fAutoOrderInitializers = false;
+
+ // read in whole file
+ int fd = ::open(path, O_RDONLY, 0);
+ if ( fd == -1 )
+ throwf("can't open order file: %s", path);
+ struct stat stat_buf;
+ ::fstat(fd, &stat_buf);
+ char* p = (char*)malloc(stat_buf.st_size+1);
+ if ( p == NULL )
+ throwf("can't process order file: %s", path);
+ if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size )
+ throwf("can't read order file: %s", path);
+ ::close(fd);
+ p[stat_buf.st_size] = '\n';
+
+ // parse into vector of pairs
+ char * const end = &p[stat_buf.st_size+1];
+ enum { lineStart, inSymbol, inComment } state = lineStart;
+ char* symbolStart = NULL;
+ for (char* s = p; s < end; ++s ) {
+ switch ( state ) {
+ case lineStart:
+ if ( *s =='#' ) {
+ state = inComment;
+ }
+ else if ( !isspace(*s) || cstring ) {
+ state = inSymbol;
+ symbolStart = s;
+ }
+ break;
+ case inSymbol:
+ if ( (*s == '\n') || (!cstring && (*s == '#')) ) {
+ bool wasComment = (*s == '#');
+ *s = '\0';
+ // removing any trailing spaces
+ char* last = s-1;
+ while ( isspace(*last) ) {
+ *last = '\0';
+ --last;
+ }
+ if ( strncmp(symbolStart, "ppc:", 4) == 0 ) {
+ if ( fArchitecture == CPU_TYPE_POWERPC )
+ symbolStart = &symbolStart[4];
+ else
+ symbolStart = NULL;
+ }
+ // if there is an architecture prefix, only use this symbol it if matches current arch
+ else if ( strncmp(symbolStart, "ppc64:", 6) == 0 ) {
+ if ( fArchitecture == CPU_TYPE_POWERPC64 )
+ symbolStart = &symbolStart[6];
+ else
+ symbolStart = NULL;
+ }
+ else if ( strncmp(symbolStart, "i386:", 5) == 0 ) {
+ if ( fArchitecture == CPU_TYPE_I386 )
+ symbolStart = &symbolStart[5];
+ else
+ symbolStart = NULL;
+ }
+ else if ( strncmp(symbolStart, "x86_64:", 7) == 0 ) {
+ if ( fArchitecture == CPU_TYPE_X86_64 )
+ symbolStart = &symbolStart[7];
+ else
+ symbolStart = NULL;
+ }
+ else if ( strncmp(symbolStart, "arm:", 4) == 0 ) {
+ if ( fArchitecture == CPU_TYPE_ARM )
+ symbolStart = &symbolStart[4];
+ else
+ symbolStart = NULL;
+ }
+ if ( symbolStart != NULL ) {
+ char* objFileName = NULL;
+ char* colon = strstr(symbolStart, ".o:");
+ if ( colon != NULL ) {
+ colon[2] = '\0';
+ objFileName = symbolStart;
+ symbolStart = &colon[3];
+ }
+ // trim leading spaces
+ while ( isspace(*symbolStart) )
+ ++symbolStart;
+ Options::OrderedSymbol pair;
+ if ( cstring )
+ pair.symbolName = cstringSymbolName(symbolStart);
+ else
+ pair.symbolName = symbolStart;
+ pair.objectFileName = objFileName;
+ fOrderedSymbols.push_back(pair);
+ }
+ symbolStart = NULL;
+ if ( wasComment )
+ state = inComment;
+ else
+ state = lineStart;
+ }
+ break;
+ case inComment:
+ if ( *s == '\n' )
+ state = lineStart;
+ break;
+ }
+ }
+ // Note: we do not free() the malloc buffer, because the strings are used by the fOrderedSymbols
+}
+
+void Options::parseSectionOrderFile(const char* segment, const char* section, const char* path)
+{
+ if ( (strcmp(section, "__cstring") == 0) && (strcmp(segment, "__TEXT") == 0) ) {
+ parseOrderFile(path, true);
+ }
+ else if ( (strncmp(section, "__literal",9) == 0) && (strcmp(segment, "__TEXT") == 0) ) {
+ warning("sorting of __literal[4,8,16] sections not supported");
+ }
+ else {
+ // ignore section information and append all symbol names to global order file
+ parseOrderFile(path, false);
+ }
+}
+
+void Options::addSection(const char* segment, const char* section, const char* path)
+{
+ if ( strlen(segment) > 16 )
+ throw "-seccreate segment name max 16 chars";
+ if ( strlen(section) > 16 ) {
+ char* tmp = strdup(section);
+ tmp[16] = '\0';
+ warning("-seccreate section name (%s) truncated to 16 chars (%s)\n", section, tmp);
+ section = tmp;
+ }
+
+ // read in whole file
+ int fd = ::open(path, O_RDONLY, 0);
+ if ( fd == -1 )
+ throwf("can't open -sectcreate file: %s", path);
+ struct stat stat_buf;
+ ::fstat(fd, &stat_buf);
+ char* p = (char*)malloc(stat_buf.st_size);
+ if ( p == NULL )
+ throwf("can't process -sectcreate file: %s", path);
+ if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size )
+ throwf("can't read -sectcreate file: %s", path);
+ ::close(fd);
+
+ // record section to create
+ ExtraSection info = { segment, section, path, (uint8_t*)p, stat_buf.st_size };
+ fExtraSections.push_back(info);
+}
+
+void Options::addSectionAlignment(const char* segment, const char* section, const char* alignmentStr)
+{
+ if ( strlen(segment) > 16 )
+ throw "-sectalign segment name max 16 chars";
+ if ( strlen(section) > 16 )
+ throw "-sectalign section name max 16 chars";
+
+ // argument to -sectalign is a hexadecimal number
+ char* endptr;
+ unsigned long value = strtoul(alignmentStr, &endptr, 16);
+ if ( *endptr != '\0')
+ throw "argument for -sectalign is not a hexadecimal number";
+ if ( value > 0x8000 )
+ throw "argument for -sectalign must be less than or equal to 0x8000";
+ if ( value == 0 ) {
+ warning("zero is not a valid -sectalign");
+ value = 1;
+ }
+
+ // alignment is power of 2 (e.g. page alignment = 12)
+ uint8_t alignment = (uint8_t)__builtin_ctz(value);
+ if ( (unsigned long)(1 << alignment) != value ) {
+ warning("alignment for -sectalign %s %s is not a power of two, using 0x%X",
+ segment, section, 1 << alignment);
+ }
+
+ SectionAlignment info = { segment, section, alignment };
+ fSectionAlignments.push_back(info);
+}
+
+void Options::addLibrary(const FileInfo& info)
+{
+ // if this library has already been added, don't add again (archives are automatically repeatedly searched)
+ for (std::vector<Options::FileInfo>::iterator fit = fInputFiles.begin(); fit != fInputFiles.end(); fit++) {
+ if ( strcmp(info.path, fit->path) == 0 ) {
+ // if dylib is specified again but weak, record that it should be weak
+ if ( info.options.fWeakImport )
+ fit->options.fWeakImport = true;
+ return;
+ }
+ }
+ // add to list
+ fInputFiles.push_back(info);
+}
+
+void Options::warnObsolete(const char* arg)
+{
+ warning("option %s is obsolete and being ignored", arg);
+}
+
+
+
+
+//
+// Process all command line arguments.
+//
+// The only error checking done here is that each option is valid and if it has arguments
+// that they too are valid.
+//
+// The general rule is "last option wins", i.e. if both -bundle and -dylib are specified,
+// whichever was last on the command line is used.
+//
+// Error check for invalid combinations of options is done in checkIllegalOptionCombinations()
+//
+void Options::parse(int argc, const char* argv[])
+{
+ // pass one builds search list from -L and -F options
+ this->buildSearchPaths(argc, argv);
+
+ // reduce re-allocations
+ fInputFiles.reserve(32);
+
+ // pass two parse all other options
+ for(int i=1; i < argc; ++i) {
+ const char* arg = argv[i];
+
+ if ( arg[0] == '-' ) {
+
+ // Since we don't care about the files passed, just the option names, we do this here.
+ if (fPrintOptions)
+ fprintf (stderr, "[Logging ld64 options]\t%s\n", arg);
+
+ if ( (arg[1] == 'L') || (arg[1] == 'F') ) {
+ // previously handled by buildSearchPaths()
+ }
+ // The one gnu style option we have to keep compatibility
+ // with gcc. Might as well have the single hyphen one as well.
+ else if ( (strcmp(arg, "--help") == 0)
+ || (strcmp(arg, "-help") == 0)) {
+ fprintf (stdout, "ld64: For information on command line options please use 'man ld'.\n");
+ exit (0);
+ }
+ else if ( strcmp(arg, "-arch") == 0 ) {
+ parseArch(argv[++i]);
+ }
+ else if ( strcmp(arg, "-dynamic") == 0 ) {
+ // default
+ }
+ else if ( strcmp(arg, "-static") == 0 ) {
+ fReaderOptions.fForStatic = true;
+ if ( fOutputKind != kObjectFile ) {
+ fOutputKind = kStaticExecutable;
+ }
+ }
+ else if ( strcmp(arg, "-dylib") == 0 ) {
+ fOutputKind = kDynamicLibrary;
+ }
+ else if ( strcmp(arg, "-bundle") == 0 ) {
+ fOutputKind = kDynamicBundle;
+ }
+ else if ( strcmp(arg, "-dylinker") == 0 ) {
+ fOutputKind = kDyld;
+ }
+ else if ( strcmp(arg, "-execute") == 0 ) {
+ if ( fOutputKind != kStaticExecutable )
+ fOutputKind = kDynamicExecutable;
+ }
+ else if ( strcmp(arg, "-preload") == 0 ) {
+ fOutputKind = kPreload;
+ }
+ else if ( strcmp(arg, "-r") == 0 ) {
+ fOutputKind = kObjectFile;
+ }
+ else if ( strcmp(arg, "-kext") == 0 ) {
+ fOutputKind = kKextBundle;
+ }
+ else if ( strcmp(arg, "-o") == 0 ) {
+ fOutputFile = argv[++i];
+ }
+ else if ( (arg[1] == 'l') && (strncmp(arg,"-lazy_",6) !=0) ) {
+ addLibrary(findLibrary(&arg[2]));
+ }
+ // This causes a dylib to be weakly bound at
+ // link time. This corresponds to weak_import.
+ else if ( strncmp(arg, "-weak-l", 7) == 0 ) {
+ FileInfo info = findLibrary(&arg[7]);
+ info.options.fWeakImport = true;
+ addLibrary(info);
+ }
+ else if ( strncmp(arg, "-lazy-l", 7) == 0 ) {
+ FileInfo info = findLibrary(&arg[7], true);
+ info.options.fLazyLoad = true;
+ addLibrary(info);
+ fUsingLazyDylibLinking = true;
+ }
+ // Avoid lazy binding.
+ // ??? Deprecate.
+ else if ( strcmp(arg, "-bind_at_load") == 0 ) {
+ fBindAtLoad = true;
+ }
+ else if ( strcmp(arg, "-twolevel_namespace") == 0 ) {
+ fNameSpace = kTwoLevelNameSpace;
+ }
+ else if ( strcmp(arg, "-flat_namespace") == 0 ) {
+ fNameSpace = kFlatNameSpace;
+ }
+ // Also sets a bit to ensure dyld causes everything
+ // in the namespace to be flat.
+ // ??? Deprecate
+ else if ( strcmp(arg, "-force_flat_namespace") == 0 ) {
+ fNameSpace = kForceFlatNameSpace;
+ }
+ // Similar to --whole-archive.
+ else if ( strcmp(arg, "-all_load") == 0 ) {
+ fReaderOptions.fFullyLoadArchives = true;
+ }
+ else if ( strcmp(arg, "-noall_load") == 0) {
+ warnObsolete(arg);
+ }
+ // Similar to -all_load
+ else if ( strcmp(arg, "-ObjC") == 0 ) {
+ fReaderOptions.fLoadAllObjcObjectsFromArchives = true;
+ }
+ // Similar to -all_load, but for the following archive only.
+ else if ( strcmp(arg, "-force_load") == 0 ) {
+ FileInfo info = findFile(argv[++i]);
+ info.options.fForceLoad = true;
+ addLibrary(info);
+ }
+ // Library versioning.
+ else if ( (strcmp(arg, "-dylib_compatibility_version") == 0)
+ || (strcmp(arg, "-compatibility_version") == 0)) {
+ const char* vers = argv[++i];
+ if ( vers == NULL )
+ throw "-dylib_compatibility_version missing <version>";
+ fDylibCompatVersion = parseVersionNumber(vers);
+ }
+ else if ( (strcmp(arg, "-dylib_current_version") == 0)
+ || (strcmp(arg, "-current_version") == 0)) {
+ const char* vers = argv[++i];
+ if ( vers == NULL )
+ throw "-dylib_current_version missing <version>";
+ fDylibCurrentVersion = parseVersionNumber(vers);
+ }
+ else if ( strcmp(arg, "-sectorder") == 0 ) {
+ if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) )
+ throw "-sectorder missing <segment> <section> <file-path>";
+ parseSectionOrderFile(argv[i+1], argv[i+2], argv[i+3]);
+ i += 3;
+ }
+ else if ( strcmp(arg, "-order_file") == 0 ) {
+ parseOrderFile(argv[++i], false);
+ }
+ else if ( strcmp(arg, "-order_file_statistics") == 0 ) {
+ fPrintOrderFileStatistics = true;
+ }
+ // ??? Deprecate segcreate.
+ // -sectcreate puts whole files into a section in the output.
+ else if ( (strcmp(arg, "-sectcreate") == 0) || (strcmp(arg, "-segcreate") == 0) ) {
+ if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) )
+ throw "-sectcreate missing <segment> <section> <file-path>";
+ addSection(argv[i+1], argv[i+2], argv[i+3]);
+ i += 3;
+ }
+ // Since we have a full path in binary/library names we need to be able to override it.
+ else if ( (strcmp(arg, "-dylib_install_name") == 0)
+ || (strcmp(arg, "-dylinker_install_name") == 0)
+ || (strcmp(arg, "-install_name") == 0)) {
+ fDylibInstallName = argv[++i];
+ if ( fDylibInstallName == NULL )
+ throw "-install_name missing <path>";
+ }
+ // Sets the base address of the output.
+ else if ( (strcmp(arg, "-seg1addr") == 0) || (strcmp(arg, "-image_base") == 0) ) {
+ const char* address = argv[++i];
+ if ( address == NULL )
+ throwf("%s missing <address>", arg);
+ fBaseAddress = parseAddress(address);
+ uint64_t temp = ((fBaseAddress+fSegmentAlignment-1) & (-fSegmentAlignment));
+ if ( fBaseAddress != temp ) {
+ warning("-seg1addr not %lld byte aligned, rounding up", fSegmentAlignment);
+ fBaseAddress = temp;
+ }
+ }
+ else if ( strcmp(arg, "-e") == 0 ) {
+ fEntryName = argv[++i];
+ }
+ // Same as -@ from the FSF linker.
+ else if ( strcmp(arg, "-filelist") == 0 ) {
+ const char* path = argv[++i];
+ if ( (path == NULL) || (path[0] == '-') )
+ throw "-filelist missing <path>";
+ loadFileList(path);
+ }
+ else if ( strcmp(arg, "-keep_private_externs") == 0 ) {
+ fKeepPrivateExterns = true;
+ }
+ else if ( strcmp(arg, "-final_output") == 0 ) {
+ fFinalName = argv[++i];
+ }
+ // Ensure that all calls to exported symbols go through lazy pointers. Multi-module
+ // just ensures that this happens for cross object file boundaries.
+ else if ( (strcmp(arg, "-interposable") == 0) || (strcmp(arg, "-multi_module") == 0)) {
+ switch ( fInterposeMode ) {
+ case kInterposeNone:
+ case kInterposeAllExternal:
+ fInterposeMode = kInterposeAllExternal;
+ break;
+ case kInterposeSome:
+ // do nothing, -interposable_list overrides -interposable"
+ break;
+ }
+ }
+ else if ( strcmp(arg, "-interposable_list") == 0 ) {
+ fInterposeMode = kInterposeSome;
+ loadExportFile(argv[++i], "-interposable_list", fInterposeList);
+ }
+ // Default for -interposable/-multi_module/-single_module.
+ else if ( strcmp(arg, "-single_module") == 0 ) {
+ fInterposeMode = kInterposeNone;
+ }
+ else if ( strcmp(arg, "-exported_symbols_list") == 0 ) {
+ if ( fExportMode == kDontExportSome )
+ throw "can't use -exported_symbols_list and -unexported_symbols_list";
+ fExportMode = kExportSome;
+ loadExportFile(argv[++i], "-exported_symbols_list", fExportSymbols);
+ }
+ else if ( strcmp(arg, "-unexported_symbols_list") == 0 ) {
+ if ( fExportMode == kExportSome )
+ throw "can't use -unexported_symbols_list and -exported_symbols_list";
+ fExportMode = kDontExportSome;
+ loadExportFile(argv[++i], "-unexported_symbols_list", fDontExportSymbols);
+ }
+ else if ( strcmp(arg, "-exported_symbol") == 0 ) {
+ if ( fExportMode == kDontExportSome )
+ throw "can't use -exported_symbol and -unexported_symbols";
+ fExportMode = kExportSome;
+ fExportSymbols.insert(argv[++i]);
+ }
+ else if ( strcmp(arg, "-unexported_symbol") == 0 ) {
+ if ( fExportMode == kExportSome )
+ throw "can't use -unexported_symbol and -exported_symbol";
+ fExportMode = kDontExportSome;
+ fDontExportSymbols.insert(argv[++i]);
+ }
+ else if ( strcmp(arg, "-non_global_symbols_no_strip_list") == 0 ) {
+ if ( fLocalSymbolHandling == kLocalSymbolsSelectiveExclude )
+ throw "can't use -non_global_symbols_no_strip_list and -non_global_symbols_strip_list";
+ fLocalSymbolHandling = kLocalSymbolsSelectiveInclude;
+ loadExportFile(argv[++i], "-non_global_symbols_no_strip_list", fLocalSymbolsIncluded);
+ }
+ else if ( strcmp(arg, "-non_global_symbols_strip_list") == 0 ) {
+ if ( fLocalSymbolHandling == kLocalSymbolsSelectiveInclude )
+ throw "can't use -non_global_symbols_no_strip_list and -non_global_symbols_strip_list";
+ fLocalSymbolHandling = kLocalSymbolsSelectiveExclude;
+ loadExportFile(argv[++i], "-non_global_symbols_strip_list", fLocalSymbolsExcluded);
+ }
+ // ??? Deprecate
+ else if ( strcmp(arg, "-no_arch_warnings") == 0 ) {
+ fIgnoreOtherArchFiles = true;
+ }
+ else if ( strcmp(arg, "-force_cpusubtype_ALL") == 0 ) {
+ fForceSubtypeAll = true;
+ }
+ // Similar to -weak-l but uses the absolute path name to the library.
+ else if ( strcmp(arg, "-weak_library") == 0 ) {
+ FileInfo info = findFile(argv[++i]);
+ info.options.fWeakImport = true;
+ addLibrary(info);
+ }
+ else if ( strcmp(arg, "-lazy_library") == 0 ) {
+ FileInfo info = findFile(argv[++i]);
+ info.options.fLazyLoad = true;
+ addLibrary(info);
+ fUsingLazyDylibLinking = true;
+ }
+ else if ( strcmp(arg, "-framework") == 0 ) {
+ addLibrary(findFramework(argv[++i]));
+ }
+ else if ( strcmp(arg, "-weak_framework") == 0 ) {
+ FileInfo info = findFramework(argv[++i]);
+ info.options.fWeakImport = true;
+ addLibrary(info);
+ }
+ else if ( strcmp(arg, "-lazy_framework") == 0 ) {
+ FileInfo info = findFramework(argv[++i]);
+ info.options.fLazyLoad = true;
+ addLibrary(info);
+ fUsingLazyDylibLinking = true;
+ }
+ else if ( strcmp(arg, "-search_paths_first") == 0 ) {
+ // previously handled by buildSearchPaths()
+ }
+ else if ( strcmp(arg, "-undefined") == 0 ) {
+ setUndefinedTreatment(argv[++i]);
+ }
+ // Debugging output flag.
+ else if ( strcmp(arg, "-arch_multiple") == 0 ) {
+ fMessagesPrefixedWithArchitecture = true;
+ }
+ // Specify what to do with relocations in read only
+ // sections like .text. Could be errors, warnings,
+ // or suppressed. Currently we do nothing with the
+ // flag.
+ else if ( strcmp(arg, "-read_only_relocs") == 0 ) {
+ switch ( parseTreatment(argv[++i]) ) {
+ case kNULL:
+ case kInvalid:
+ throw "-read_only_relocs missing [ warning | error | suppress ]";
+ case kWarning:
+ fWarnTextRelocs = true;
+ fAllowTextRelocs = true;
+ break;
+ case kSuppress:
+ fWarnTextRelocs = false;
+ fAllowTextRelocs = true;
+ break;
+ case kError:
+ fWarnTextRelocs = false;
+ fAllowTextRelocs = false;
+ break;
+ }
+ }
+ else if ( strcmp(arg, "-sect_diff_relocs") == 0 ) {
+ warnObsolete(arg);
+ ++i;
+ }
+ // Warn, error or make strong a mismatch between weak
+ // and non-weak references.
+ else if ( strcmp(arg, "-weak_reference_mismatches") == 0 ) {
+ setWeakReferenceMismatchTreatment(argv[++i]);
+ }
+ // For a deployment target of 10.3 and earlier ld64 will
+ // prebind an executable with 0s in all addresses that
+ // are prebound. This can then be fixed up by update_prebinding
+ // later. Prebinding is less useful on 10.4 and greater.
+ else if ( strcmp(arg, "-prebind") == 0 ) {
+ fPrebind = true;
+ }
+ else if ( strcmp(arg, "-noprebind") == 0 ) {
+ warnObsolete(arg);
+ fPrebind = false;
+ }
+ else if ( strcmp(arg, "-prebind_allow_overlap") == 0 ) {
+ warnObsolete(arg);
+ }
+ else if ( strcmp(arg, "-prebind_all_twolevel_modules") == 0 ) {
+ warnObsolete(arg);
+ }
+ else if ( strcmp(arg, "-noprebind_all_twolevel_modules") == 0 ) {
+ warnObsolete(arg);
+ }
+ else if ( strcmp(arg, "-nofixprebinding") == 0 ) {
+ warnObsolete(arg);
+ }
+ // This should probably be deprecated when we respect -L and -F
+ // when searching for libraries.
+ else if ( strcmp(arg, "-dylib_file") == 0 ) {
+ addDylibOverride(argv[++i]);
+ }
+ // What to expand @executable_path to if found in dependent dylibs
+ else if ( strcmp(arg, "-executable_path") == 0 ) {
+ fExecutablePath = argv[++i];
+ if ( (fExecutablePath == NULL) || (fExecutablePath[0] == '-') )
+ throw "-executable_path missing <path>";
+ // if a directory was passed, add / to end
+ // <rdar://problem/5171880> ld64 can't find @executable _path relative dylibs from our umbrella frameworks
+ struct stat statBuffer;
+ if ( stat(fExecutablePath, &statBuffer) == 0 ) {
+ if ( (statBuffer.st_mode & S_IFMT) == S_IFDIR ) {
+ char* pathWithSlash = new char[strlen(fExecutablePath)+2];
+ strcpy(pathWithSlash, fExecutablePath);
+ strcat(pathWithSlash, "/");
+ fExecutablePath = pathWithSlash;
+ }
+ }
+ }
+ // Aligns all segments to the power of 2 boundary specified.
+ else if ( strcmp(arg, "-segalign") == 0 ) {
+ const char* size = argv[++i];
+ if ( size == NULL )
+ throw "-segalign missing <size>";
+ fSegmentAlignment = parseAddress(size);
+ uint8_t alignment = (uint8_t)__builtin_ctz(fSegmentAlignment);
+ uint32_t p2aligned = (1 << alignment);
+ if ( p2aligned != fSegmentAlignment ) {
+ warning("alignment for -segalign %s is not a power of two, using 0x%X", size, p2aligned);
+ fSegmentAlignment = p2aligned;
+ }
+ }
+ // Puts a specified segment at a particular address that must
+ // be a multiple of the segment alignment.
+ else if ( strcmp(arg, "-segaddr") == 0 ) {
+ SegmentStart seg;
+ seg.name = argv[++i];
+ if ( (seg.name == NULL) || (argv[i+1] == NULL) )
+ throw "-segaddr missing segName Adddress";
+ seg.address = parseAddress(argv[++i]);
+ uint64_t temp = seg.address & (-4096); // page align
+ if ( (seg.address != temp) )
+ warning("-segaddr %s not page aligned, rounding down", seg.name);
+ fCustomSegmentAddresses.push_back(seg);
+ }
+ // ??? Deprecate when we deprecate split-seg.
+ else if ( strcmp(arg, "-segs_read_only_addr") == 0 ) {
+ fBaseAddress = parseAddress(argv[++i]);
+ }
+ // ??? Deprecate when we deprecate split-seg.
+ else if ( strcmp(arg, "-segs_read_write_addr") == 0 ) {
+ fBaseWritableAddress = parseAddress(argv[++i]);
+ fSplitSegs = true;
+ }
+ // ??? Deprecate when we get rid of basing at build time.
+ else if ( strcmp(arg, "-seg_addr_table") == 0 ) {
+ const char* name = argv[++i];
+ if ( name == NULL )
+ throw "-seg_addr_table missing argument";
+ fSegAddrTablePath = name;
+ }
+ else if ( strcmp(arg, "-seg_addr_table_filename") == 0 ) {
+ warnObsolete(arg);
+ ++i;
+ }
+ else if ( strcmp(arg, "-segprot") == 0 ) {
+ SegmentProtect seg;
+ seg.name = argv[++i];
+ if ( (seg.name == NULL) || (argv[i+1] == NULL) || (argv[i+2] == NULL) )
+ throw "-segprot missing segName max-prot init-prot";
+ seg.max = parseProtection(argv[++i]);
+ seg.init = parseProtection(argv[++i]);
+ fCustomSegmentProtections.push_back(seg);
+ }
+ else if ( strcmp(arg, "-pagezero_size") == 0 ) {
+ const char* size = argv[++i];
+ if ( size == NULL )
+ throw "-pagezero_size missing <size>";
+ fZeroPageSize = parseAddress(size);
+ uint64_t temp = fZeroPageSize & (-4096); // page align
+ if ( (fZeroPageSize != temp) )
+ warning("-pagezero_size not page aligned, rounding down");
+ fZeroPageSize = temp;
+ }
+ else if ( strcmp(arg, "-stack_addr") == 0 ) {
+ const char* address = argv[++i];
+ if ( address == NULL )
+ throw "-stack_addr missing <address>";
+ fStackAddr = parseAddress(address);
+ }
+ else if ( strcmp(arg, "-stack_size") == 0 ) {
+ const char* size = argv[++i];
+ if ( size == NULL )
+ throw "-stack_size missing <address>";
+ fStackSize = parseAddress(size);
+ uint64_t temp = fStackSize & (-4096); // page align
+ if ( (fStackSize != temp) )
+ warning("-stack_size not page aligned, rounding down");
+ }
+ else if ( strcmp(arg, "-allow_stack_execute") == 0 ) {
+ fExecutableStack = true;
+ }
+ else if ( strcmp(arg, "-sectalign") == 0 ) {
+ if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) )
+ throw "-sectalign missing <segment> <section> <file-path>";
+ addSectionAlignment(argv[i+1], argv[i+2], argv[i+3]);
+ i += 3;
+ }
+ else if ( strcmp(arg, "-sectorder_detail") == 0 ) {
+ warnObsolete(arg);
+ }
+ else if ( strcmp(arg, "-sectobjectsymbols") == 0 ) {
+ warnObsolete(arg);
+ i += 2;
+ }
+ else if ( strcmp(arg, "-bundle_loader") == 0 ) {
+ 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 ) {
+ warnObsolete(arg);
+ }
+ else if ( strcmp(arg, "-twolevel_namespace_hints") == 0 ) {
+ // FIX FIX
+ }
+ // Use this flag to set default behavior for deployement targets.
+ else if ( strcmp(arg, "-macosx_version_min") == 0 ) {
+ setMacOSXVersionMin(argv[++i]);
+ }
+ else if ( strcmp(arg, "-iphoneos_version_min") == 0 ) {
+ setIPhoneVersionMin(argv[++i]);
+ }
+ else if ( strcmp(arg, "-multiply_defined") == 0 ) {
+ //warnObsolete(arg);
+ ++i;
+ }
+ else if ( strcmp(arg, "-multiply_defined_unused") == 0 ) {
+ warnObsolete(arg);
+ ++i;
+ }
+ else if ( strcmp(arg, "-nomultidefs") == 0 ) {
+ warnObsolete(arg);
+ }
+ // Display each file in which the argument symbol appears and whether
+ // the file defines or references it. This option takes an argument
+ // as -y<symbol> note that there is no space.
+ else if ( strncmp(arg, "-y", 2) == 0 ) {
+ warnObsolete("-y");
+ }
+ // Same output as -y, but output <arg> number of undefined symbols only.
+ else if ( strcmp(arg, "-Y") == 0 ) {
+ //warnObsolete(arg);
+ ++i;
+ }
+ // This option affects all objects linked into the final result.
+ else if ( strcmp(arg, "-m") == 0 ) {
+ warnObsolete(arg);
+ }
+ 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];
+ if ( name == NULL )
+ throw "-u missing argument";
+ fInitialUndefines.push_back(name);
+ }
+ else if ( strcmp(arg, "-U") == 0 ) {
+ const char* name = argv[++i];
+ if ( name == NULL )
+ throw "-U missing argument";
+ fAllowedUndefined.insert(name);
+ }
+ else if ( strcmp(arg, "-s") == 0 ) {
+ warnObsolete(arg);
+ fLocalSymbolHandling = kLocalSymbolsNone;
+ fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoNone;
+ }
+ else if ( strcmp(arg, "-x") == 0 ) {
+ fLocalSymbolHandling = kLocalSymbolsNone;
+ }
+ else if ( strcmp(arg, "-S") == 0 ) {
+ fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoNone;
+ }
+ else if ( strcmp(arg, "-X") == 0 ) {
+ warnObsolete(arg);
+ }
+ else if ( strcmp(arg, "-Si") == 0 ) {
+ warnObsolete(arg);
+ fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoFull;
+ }
+ else if ( strcmp(arg, "-b") == 0 ) {
+ warnObsolete(arg);
+ }
+ else if ( strcmp(arg, "-Sn") == 0 ) {
+ warnObsolete(arg);
+ fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoFull;
+ }
+ else if ( strcmp(arg, "-Sp") == 0 ) {
+ warnObsolete(arg);
+ }
+ else if ( strcmp(arg, "-dead_strip") == 0 ) {
+ fDeadStrip = kDeadStripOnPlusUnusedInits;
+ }
+ else if ( strcmp(arg, "-no_dead_strip_inits_and_terms") == 0 ) {
+ fDeadStrip = kDeadStripOn;
+ }
+ else if ( strcmp(arg, "-w") == 0 ) {
+ // previously handled by buildSearchPaths()
+ }
+ else if ( strcmp(arg, "-arch_errors_fatal") == 0 ) {
+ fErrorOnOtherArchFiles = true;
+ }
+ else if ( strcmp(arg, "-M") == 0 ) {
+ // FIX FIX
+ }
+ else if ( strcmp(arg, "-headerpad") == 0 ) {
+ const char* size = argv[++i];
+ if ( size == NULL )
+ throw "-headerpad missing argument";
+ fMinimumHeaderPad = parseAddress(size);
+ }
+ else if ( strcmp(arg, "-headerpad_max_install_names") == 0 ) {
+ fMaxMinimumHeaderPad = true;
+ }
+ else if ( strcmp(arg, "-t") == 0 ) {
+ fReaderOptions.fLogAllFiles = true;
+ }
+ else if ( strcmp(arg, "-whatsloaded") == 0 ) {
+ fReaderOptions.fLogObjectFiles = true;
+ }
+ else if ( strcmp(arg, "-A") == 0 ) {
+ warnObsolete(arg);
+ ++i;
+ }
+ else if ( strcmp(arg, "-umbrella") == 0 ) {
+ const char* name = argv[++i];
+ if ( name == NULL )
+ throw "-umbrella missing argument";
+ fUmbrellaName = name;
+ }
+ else if ( strcmp(arg, "-allowable_client") == 0 ) {
+ const char* name = argv[++i];
+
+ if ( name == NULL )
+ throw "-allowable_client missing argument";
+
+ fAllowableClients.push_back(name);
+ }
+ else if ( strcmp(arg, "-client_name") == 0 ) {
+ const char* name = argv[++i];
+
+ if ( name == NULL )
+ throw "-client_name missing argument";
+
+ fClientName = name;
+ }
+ else if ( strcmp(arg, "-sub_umbrella") == 0 ) {
+ const char* name = argv[++i];
+ if ( name == NULL )
+ throw "-sub_umbrella missing argument";
+ fSubUmbellas.push_back(name);
+ }
+ else if ( strcmp(arg, "-sub_library") == 0 ) {
+ const char* name = argv[++i];
+ if ( name == NULL )
+ throw "-sub_library missing argument";
+ fSubLibraries.push_back(name);
+ }
+ else if ( strcmp(arg, "-init") == 0 ) {
+ const char* name = argv[++i];
+ if ( name == NULL )
+ throw "-init missing argument";
+ fInitFunctionName = name;
+ }
+ else if ( strcmp(arg, "-dot") == 0 ) {
+ const char* name = argv[++i];
+ if ( name == NULL )
+ throw "-dot missing argument";
+ fDotOutputFile = name;
+ }
+ else if ( strcmp(arg, "-warn_commons") == 0 ) {
+ fWarnCommons = true;
+ }
+ else if ( strcmp(arg, "-commons") == 0 ) {
+ fCommonsMode = parseCommonsTreatment(argv[++i]);
+ }
+ else if ( strcmp(arg, "-keep_relocs") == 0 ) {
+ fKeepRelocations = true;
+ }
+ else if ( strcmp(arg, "-warn_stabs") == 0 ) {
+ fWarnStabs = true;
+ }
+ else if ( strcmp(arg, "-pause") == 0 ) {
+ fPause = true;
+ }
+ else if ( strcmp(arg, "-print_statistics") == 0 ) {
+ fStatistics = true;
+ }
+ else if ( strcmp(arg, "-d") == 0 ) {
+ fReaderOptions.fMakeTentativeDefinitionsReal = true;
+ }
+ else if ( strcmp(arg, "-v") == 0 ) {
+ // previously handled by buildSearchPaths()
+ }
+ else if ( strcmp(arg, "-Z") == 0 ) {
+ // previously handled by buildSearchPaths()
+ }
+ else if ( strcmp(arg, "-syslibroot") == 0 ) {
+ ++i;
+ // previously handled by buildSearchPaths()
+ }
+ else if ( strcmp(arg, "-no_uuid") == 0 ) {
+ fUUIDMode = kUUIDNone;
+ }
+ else if ( strcmp(arg, "-random_uuid") == 0 ) {
+ fUUIDMode = kUUIDRandom;
+ }
+ else if ( strcmp(arg, "-dtrace") == 0 ) {
+ const char* name = argv[++i];
+ if ( name == NULL )
+ throw "-dtrace missing argument";
+ fDtraceScriptName = name;
+ }
+ else if ( strcmp(arg, "-root_safe") == 0 ) {
+ fReaderOptions.fRootSafe = true;
+ }
+ else if ( strcmp(arg, "-setuid_safe") == 0 ) {
+ fReaderOptions.fSetuidSafe = true;
+ }
+ else if ( strcmp(arg, "-alias") == 0 ) {
+ ObjectFile::ReaderOptions::AliasPair pair;
+ pair.realName = argv[++i];
+ if ( pair.realName == NULL )
+ throw "missing argument to -alias";
+ pair.alias = argv[++i];
+ if ( pair.alias == NULL )
+ throw "missing argument to -alias";
+ fReaderOptions.fAliases.push_back(pair);
+ }
+ else if ( strcmp(arg, "-alias_list") == 0 ) {
+ parseAliasFile(argv[++i]);
+ }
+ // put this last so that it does not interfer with other options starting with 'i'
+ else if ( strncmp(arg, "-i", 2) == 0 ) {
+ const char* colon = strchr(arg, ':');
+ if ( colon == NULL )
+ throwf("unknown option: %s", arg);
+ ObjectFile::ReaderOptions::AliasPair pair;
+ char* temp = new char[colon-arg];
+ strlcpy(temp, &arg[2], colon-arg-1);
+ pair.realName = &colon[1];
+ pair.alias = temp;
+ fReaderOptions.fAliases.push_back(pair);
+ }
+ else if ( strcmp(arg, "-save-temps") == 0 ) {
+ fSaveTempFiles = true;
+ }
+ else if ( strcmp(arg, "-rpath") == 0 ) {
+ const char* path = argv[++i];
+ if ( path == NULL )
+ throw "missing argument to -rpath";
+ fRPaths.push_back(path);
+ }
+ else if ( strcmp(arg, "-read_only_stubs") == 0 ) {
+ fReadOnlyx86Stubs = true;
+ }
+ else if ( strcmp(arg, "-slow_stubs") == 0 ) {
+ warnObsolete(arg);
+ }
+ else if ( strcmp(arg, "-map") == 0 ) {
+ fMapPath = argv[++i];
+ if ( fMapPath == NULL )
+ throw "missing argument to -map";
+ }
+ else if ( strcmp(arg, "-pie") == 0 ) {
+ fPositionIndependentExecutable = true;
+ }
+ else if ( strncmp(arg, "-reexport-l", 11) == 0 ) {
+ FileInfo info = findLibrary(&arg[11], true);
+ info.options.fReExport = true;
+ addLibrary(info);
+ }
+ else if ( strcmp(arg, "-reexport_library") == 0 ) {
+ FileInfo info = findFile(argv[++i]);
+ info.options.fReExport = true;
+ addLibrary(info);
+ }
+ else if ( strcmp(arg, "-reexport_framework") == 0 ) {
+ FileInfo info = findFramework(argv[++i]);
+ info.options.fReExport = true;
+ addLibrary(info);
+ }
+ else if ( strcmp(arg, "-dead_strip_dylibs") == 0 ) {
+ fDeadStripDylibs = true;
+ }
+ else if ( strcmp(arg, "-no_implicit_dylibs") == 0 ) {
+ fReaderOptions.fImplicitlyLinkPublicDylibs = false;
+ }
+ else if ( strcmp(arg, "-new_linker") == 0 ) {
+ // ignore
+ }
+ else if ( strcmp(arg, "-no_encryption") == 0 ) {
+ fEncryptable = false;
+ }
+ else if ( strcmp(arg, "-no_compact_unwind") == 0 ) {
+ fReaderOptions.fAddCompactUnwindEncoding = false;
+ }
+ else if ( strcmp(arg, "-mllvm") == 0 ) {
+ const char* opts = argv[++i];
+ if ( opts == NULL )
+ throw "missing argument to -mllvm";
+ fLLVMOptions.push_back(opts);
+ }
+ else if ( strcmp(arg, "-no_order_inits") == 0 ) {
+ fReaderOptions.fAutoOrderInitializers = false;
+ }
+ else if ( strcmp(arg, "-no_order_data") == 0 ) {
+ fOrderData = false;
+ }
+ else if ( strcmp(arg, "-seg_page_size") == 0 ) {
+ SegmentSize seg;
+ seg.name = argv[++i];
+ if ( (seg.name == NULL) || (argv[i+1] == NULL) )
+ throw "-seg_page_size missing segName Adddress";
+ seg.size = parseAddress(argv[++i]);
+ uint64_t temp = seg.size & (-4096); // page align
+ if ( (seg.size != temp) )
+ warning("-seg_page_size %s not 4K aligned, rounding down", seg.name);
+ fCustomSegmentSizes.push_back(seg);
+ }
+ else if ( strcmp(arg, "-mark_dead_strippable_dylib") == 0 ) {
+ fMarkDeadStrippableDylib = true;
+ }
+ else if ( strcmp(arg, "-exported_symbols_order") == 0 ) {
+ loadSymbolOrderFile(argv[++i], fExportSymbolsOrder);
+ }
+ else if ( strcmp(arg, "-no_compact_linkedit") == 0 ) {
+ fMakeCompressedDyldInfo = false;
+ }
+ else if ( strcmp(arg, "-no_eh_labels") == 0 ) {
+ fReaderOptions.fNoEHLabels = true;
+ }
+ else if ( strcmp(arg, "-warn_compact_unwind") == 0 ) {
+ fReaderOptions.fWarnCompactUnwind = true;
+ }
+ else if ( strcmp(arg, "-allow_sub_type_mismatches") == 0 ) {
+ fAllowCpuSubtypeMismatches = true;
+ }
+ else {
+ throwf("unknown option: %s", arg);
+ }
+ }
+ else {
+ FileInfo info = findFile(arg);
+ if ( strcmp(&info.path[strlen(info.path)-2], ".a") == 0 )
+ addLibrary(info);
+ else
+ fInputFiles.push_back(info);
+ }
+ }
+
+ // if a -lazy option was used, implicitly link in lazydylib1.o
+ if ( fUsingLazyDylibLinking ) {
+ addLibrary(findLibrary("lazydylib1.o"));
+ }
+}
+
+
+
+//
+// -syslibroot <path> is used for SDK support.
+// The rule is that all search paths (both explicit and default) are
+// checked to see if they exist in the SDK. If so, that path is
+// replaced with the sdk prefixed path. If not, that search path
+// is used as is. If multiple -syslibroot options are specified
+// their directory structures are logically overlayed and files
+// from sdks specified earlier on the command line used before later ones.
+
+void Options::buildSearchPaths(int argc, const char* argv[])
+{
+ bool addStandardLibraryDirectories = true;
+ std::vector<const char*> libraryPaths;
+ std::vector<const char*> frameworkPaths;
+ libraryPaths.reserve(10);
+ frameworkPaths.reserve(10);
+ // scan through argv looking for -L, -F, -Z, and -syslibroot options
+ for(int i=0; i < argc; ++i) {
+ if ( (argv[i][0] == '-') && (argv[i][1] == 'L') )
+ libraryPaths.push_back(&argv[i][2]);
+ else if ( (argv[i][0] == '-') && (argv[i][1] == 'F') )
+ frameworkPaths.push_back(&argv[i][2]);
+ else if ( strcmp(argv[i], "-Z") == 0 )
+ addStandardLibraryDirectories = false;
+ else if ( strcmp(argv[i], "-v") == 0 ) {
+ fVerbose = true;
+ extern const char ldVersionString[];
+ fprintf(stderr, "%s", ldVersionString);
+ // if only -v specified, exit cleanly
+ if ( argc == 2 ) {
+#if LTO_SUPPORT
+ printLTOVersion(*this);
+#endif
+ exit(0);
+ }
+ }
+ else if ( strcmp(argv[i], "-syslibroot") == 0 ) {
+ const char* path = argv[++i];
+ if ( path == NULL )
+ throw "-syslibroot missing argument";
+ fSDKPaths.push_back(path);
+ }
+ else if ( strcmp(argv[i], "-search_paths_first") == 0 ) {
+ // ??? Deprecate when we get -Bstatic/-Bdynamic.
+ fLibrarySearchMode = kSearchDylibAndArchiveInEachDir;
+ }
+ else if ( strcmp(argv[i], "-w") == 0 ) {
+ sEmitWarnings = false;
+ }
+ }
+ int standardLibraryPathsStartIndex = libraryPaths.size();
+ int standardFrameworkPathsStartIndex = frameworkPaths.size();
+ if ( addStandardLibraryDirectories ) {
+ libraryPaths.push_back("/usr/lib");
+ libraryPaths.push_back("/usr/local/lib");
+
+ frameworkPaths.push_back("/Library/Frameworks/");
+ frameworkPaths.push_back("/System/Library/Frameworks/");
+ // <rdar://problem/5433882> remove /Network/Library/Frameworks from default search path
+ }
+
+ // <rdar://problem/5829579> Support for configure based hacks
+ // if last -syslibroot is /, then ignore all syslibroots
+ if ( fSDKPaths.size() > 0 ) {
+ if ( strcmp(fSDKPaths.back(), "/") == 0 ) {
+ fSDKPaths.clear();
+ }
+ }
+
+ // now merge sdk and library paths to make real search paths
+ fLibrarySearchPaths.reserve(libraryPaths.size()*(fSDKPaths.size()+1));
+ int libIndex = 0;
+ for (std::vector<const char*>::iterator it = libraryPaths.begin(); it != libraryPaths.end(); ++it, ++libIndex) {
+ const char* libDir = *it;
+ bool sdkOverride = false;
+ if ( libDir[0] == '/' ) {
+ char betterLibDir[PATH_MAX];
+ if ( strstr(libDir, "/..") != NULL ) {
+ if ( realpath(libDir, betterLibDir) != NULL )
+ libDir = strdup(betterLibDir);
+ }
+ const int libDirLen = strlen(libDir);
+ for (std::vector<const char*>::iterator sdkit = fSDKPaths.begin(); sdkit != fSDKPaths.end(); sdkit++) {
+ const char* sdkDir = *sdkit;
+ const int sdkDirLen = strlen(sdkDir);
+ char newPath[libDirLen + sdkDirLen+4];
+ strcpy(newPath, sdkDir);
+ if ( newPath[sdkDirLen-1] == '/' )
+ newPath[sdkDirLen-1] = '\0';
+ strcat(newPath, libDir);
+ struct stat statBuffer;
+ if ( stat(newPath, &statBuffer) == 0 ) {
+ fLibrarySearchPaths.push_back(strdup(newPath));
+ sdkOverride = true;
+ }
+ }
+ }
+ if ( !sdkOverride ) {
+ if ( (libIndex >= standardLibraryPathsStartIndex) && (fSDKPaths.size() == 1) ) {
+ // <rdar://problem/6438270> -syslibroot should skip standard search paths not in the SDK
+ // if one SDK is specified and a standard library path is not in the SDK, don't use it
+ }
+ else {
+ fLibrarySearchPaths.push_back(libDir);
+ }
+ }
+ }
+
+ // now merge sdk and framework paths to make real search paths
+ fFrameworkSearchPaths.reserve(frameworkPaths.size()*(fSDKPaths.size()+1));
+ int frameIndex = 0;
+ for (std::vector<const char*>::iterator it = frameworkPaths.begin(); it != frameworkPaths.end(); ++it, ++frameIndex) {
+ const char* frameworkDir = *it;
+ bool sdkOverride = false;
+ if ( frameworkDir[0] == '/' ) {
+ char betterFrameworkDir[PATH_MAX];
+ if ( strstr(frameworkDir, "/..") != NULL ) {
+ if ( realpath(frameworkDir, betterFrameworkDir) != NULL )
+ frameworkDir = strdup(betterFrameworkDir);
+ }
+ const int frameworkDirLen = strlen(frameworkDir);
+ for (std::vector<const char*>::iterator sdkit = fSDKPaths.begin(); sdkit != fSDKPaths.end(); sdkit++) {
+ const char* sdkDir = *sdkit;
+ const int sdkDirLen = strlen(sdkDir);
+ char newPath[frameworkDirLen + sdkDirLen+4];
+ strcpy(newPath, sdkDir);
+ if ( newPath[sdkDirLen-1] == '/' )
+ newPath[sdkDirLen-1] = '\0';
+ strcat(newPath, frameworkDir);
+ struct stat statBuffer;
+ if ( stat(newPath, &statBuffer) == 0 ) {
+ fFrameworkSearchPaths.push_back(strdup(newPath));
+ sdkOverride = true;
+ }
+ }
+ }
+ if ( !sdkOverride ) {
+ if ( (frameIndex >= standardFrameworkPathsStartIndex) && (fSDKPaths.size() == 1) ) {
+ // <rdar://problem/6438270> -syslibroot should skip standard search paths not in the SDK
+ // if one SDK is specified and a standard library path is not in the SDK, don't use it
+ }
+ else {
+ fFrameworkSearchPaths.push_back(frameworkDir);
+ }
+ }
+ }
+
+ if ( fVerbose ) {
+ fprintf(stderr,"Library search paths:\n");
+ for (std::vector<const char*>::iterator it = fLibrarySearchPaths.begin();
+ it != fLibrarySearchPaths.end();
+ it++)
+ fprintf(stderr,"\t%s\n", *it);
+ fprintf(stderr,"Framework search paths:\n");
+ for (std::vector<const char*>::iterator it = fFrameworkSearchPaths.begin();
+ it != fFrameworkSearchPaths.end();
+ it++)
+ fprintf(stderr,"\t%s\n", *it);
+ }
+}
+
+// this is run before the command line is parsed
+void Options::parsePreCommandLineEnvironmentSettings()
+{
+ if ((getenv("LD_TRACE_ARCHIVES") != NULL)
+ || (getenv("RC_TRACE_ARCHIVES") != NULL))
+ fReaderOptions.fTraceArchives = true;
+
+ if ((getenv("LD_TRACE_DYLIBS") != NULL)
+ || (getenv("RC_TRACE_DYLIBS") != NULL)) {
+ fReaderOptions.fTraceDylibs = true;
+ fReaderOptions.fTraceIndirectDylibs = true;
+ }
+
+ if (getenv("RC_TRACE_DYLIB_SEARCHING") != NULL) {
+ fTraceDylibSearching = true;
+ }
+
+ if (getenv("LD_PRINT_OPTIONS") != NULL)
+ fPrintOptions = true;
+
+ if (fReaderOptions.fTraceDylibs || fReaderOptions.fTraceArchives)
+ fReaderOptions.fTraceOutputFile = getenv("LD_TRACE_FILE");
+
+ if (getenv("LD_PRINT_ORDER_FILE_STATISTICS") != NULL)
+ fPrintOrderFileStatistics = true;
+
+ if (getenv("LD_SPLITSEGS_NEW_LIBRARIES") != NULL)
+ fSplitSegs = true;
+
+ if (getenv("LD_NO_ENCRYPT") != NULL)
+ fEncryptable = false;
+
+ if (getenv("LD_ALLOW_CPU_SUBTYPE_MISMATCHES") != NULL)
+ fAllowCpuSubtypeMismatches = true;
+
+ // for now disable compressed linkedit functionality
+ if ( getenv("LD_NO_COMPACT_LINKEDIT") != NULL ) {
+ fMakeCompressedDyldInfo = false;
+ fMakeClassicDyldInfo = true;
+ }
+
+ sWarningsSideFilePath = getenv("LD_WARN_FILE");
+}
+
+
+// this is run after the command line is parsed
+void Options::parsePostCommandLineEnvironmentSettings()
+{
+ // when building a dynamic main executable, default any use of @executable_path to output path
+ if ( fExecutablePath == NULL && (fOutputKind == kDynamicExecutable) ) {
+ fExecutablePath = fOutputFile;
+ }
+
+ // allow build system to set default seg_addr_table
+ if ( fSegAddrTablePath == NULL )
+ fSegAddrTablePath = getenv("LD_SEG_ADDR_TABLE");
+
+ // allow build system to turn on prebinding
+ if ( !fPrebind ) {
+ fPrebind = ( getenv("LD_PREBIND") != NULL );
+ }
+
+ // allow build system to force on dead-code-stripping
+ if ( fDeadStrip == kDeadStripOff ) {
+ if ( getenv("LD_DEAD_STRIP") != NULL ) {
+ switch (fOutputKind) {
+ case Options::kDynamicLibrary:
+ case Options::kDynamicExecutable:
+ case Options::kDynamicBundle:
+ fDeadStrip = kDeadStripOn;
+ break;
+ case Options::kPreload:
+ case Options::kObjectFile:
+ case Options::kDyld:
+ case Options::kStaticExecutable:
+ case Options::kKextBundle:
+ break;
+ }
+ }
+ }
+
+ // allow build system to force on -warn_commons
+ if ( getenv("LD_WARN_COMMONS") != NULL )
+ fWarnCommons = true;
+}
+
+void Options::reconfigureDefaults()
+{
+ // sync reader options
+ switch ( fOutputKind ) {
+ case Options::kObjectFile:
+ fReaderOptions.fForFinalLinkedImage = false;
+ break;
+ case Options::kDyld:
+ fReaderOptions.fForDyld = true;
+ fReaderOptions.fForFinalLinkedImage = true;
+ fReaderOptions.fNoEHLabels = true;
+ break;
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ case Options::kKextBundle:
+ fReaderOptions.fForFinalLinkedImage = true;
+ fReaderOptions.fNoEHLabels = true;
+ break;
+ case Options::kDynamicExecutable:
+ case Options::kStaticExecutable:
+ case Options::kPreload:
+ fReaderOptions.fLinkingMainExecutable = true;
+ fReaderOptions.fForFinalLinkedImage = true;
+ fReaderOptions.fNoEHLabels = true;
+ break;
+ }
+
+ // set default min OS version
+ if ( (fReaderOptions.fMacVersionMin == ObjectFile::ReaderOptions::kMinMacVersionUnset)
+ && (fReaderOptions.fIPhoneVersionMin == ObjectFile::ReaderOptions::kMinIPhoneVersionUnset) ) {
+ // if neither -macosx_version_min nor -iphoneos_version_min used, try environment variables
+ const char* macVers = getenv("MACOSX_DEPLOYMENT_TARGET");
+ const char* iPhoneVers = getenv("IPHONEOS_DEPLOYMENT_TARGET");
+ if ( macVers != NULL )
+ setMacOSXVersionMin(macVers);
+ else if ( iPhoneVers != NULL )
+ setIPhoneVersionMin(iPhoneVers);
+ else {
+ // if still nothing, set default based on architecture
+ switch ( fArchitecture ) {
+ case CPU_TYPE_I386:
+ case CPU_TYPE_X86_64:
+ case CPU_TYPE_POWERPC:
+ fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_6; // FIX FIX, this really should be a check of the OS version the linker is running o
+ break;
+ case CPU_TYPE_ARM:
+ fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_0;
+ break;
+ }
+ }
+ }
+
+
+ // adjust min based on architecture
+ switch ( fArchitecture ) {
+ case CPU_TYPE_I386:
+ if ( fReaderOptions.fMacVersionMin < ObjectFile::ReaderOptions::k10_4 ) {
+ //warning("-macosx_version_min should be 10.4 or later for i386");
+ fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_4;
+ }
+ break;
+ case CPU_TYPE_POWERPC64:
+ if ( fReaderOptions.fMacVersionMin < ObjectFile::ReaderOptions::k10_4 ) {
+ //warning("-macosx_version_min should be 10.4 or later for ppc64");
+ fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_4;
+ }
+ break;
+ case CPU_TYPE_X86_64:
+ if ( fReaderOptions.fMacVersionMin < ObjectFile::ReaderOptions::k10_4 ) {
+ //warning("-macosx_version_min should be 10.4 or later for x86_64");
+ fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_4;
+ }
+ break;
+ }
+
+ // adjust kext type based on architecture
+ if ( fOutputKind == kKextBundle ) {
+ switch ( fArchitecture ) {
+ case CPU_TYPE_X86_64:
+ // x86_64 uses new MH_KEXT_BUNDLE type
+ fMakeClassicDyldInfo = true;
+ fMakeCompressedDyldInfo = false;
+ fAllowTextRelocs = true;
+ fUndefinedTreatment = kUndefinedDynamicLookup;
+ break;
+ case CPU_TYPE_POWERPC:
+ case CPU_TYPE_I386:
+ case CPU_TYPE_ARM:
+ // use .o files
+ fOutputKind = kObjectFile;
+ break;
+ }
+ }
+
+ // disable implicit dylibs when targeting 10.3
+ // <rdar://problem/5451987> add option to disable implicit load commands for indirectly used public dylibs
+ if ( !minOS(ObjectFile::ReaderOptions::k10_4, ObjectFile::ReaderOptions::k2_0) )
+ fReaderOptions.fImplicitlyLinkPublicDylibs = false;
+
+
+ // allow build system to force linker to ignore -prebind
+ if ( getenv("LD_FORCE_NO_PREBIND") != NULL )
+ fPrebind = false;
+
+ // allow build system to force linker to ignore -seg_addr_table
+ if ( getenv("LD_FORCE_NO_SEG_ADDR_TABLE") != NULL )
+ fSegAddrTablePath = NULL;
+
+ // check for base address specified externally
+ if ( (fSegAddrTablePath != NULL) && (fOutputKind == Options::kDynamicLibrary) ) {
+ parseSegAddrTable(fSegAddrTablePath, this->installPath());
+ // HACK to support seg_addr_table entries that are physical paths instead of install paths
+ if ( fBaseAddress == 0 ) {
+ if ( strcmp(this->installPath(), "/usr/lib/libstdc++.6.dylib") == 0 ) {
+ parseSegAddrTable(fSegAddrTablePath, "/usr/lib/libstdc++.6.0.4.dylib");
+ if ( fBaseAddress == 0 )
+ parseSegAddrTable(fSegAddrTablePath, "/usr/lib/libstdc++.6.0.9.dylib");
+ }
+
+ else if ( strcmp(this->installPath(), "/usr/lib/libz.1.dylib") == 0 )
+ parseSegAddrTable(fSegAddrTablePath, "/usr/lib/libz.1.2.3.dylib");
+
+ else if ( strcmp(this->installPath(), "/usr/lib/libutil.dylib") == 0 )
+ parseSegAddrTable(fSegAddrTablePath, "/usr/lib/libutil1.0.dylib");
+ }
+ }
+
+ // split segs only allowed for dylibs
+ if ( fSplitSegs ) {
+ // split seg only supported for ppc, i386, and arm.
+ switch ( fArchitecture ) {
+ case CPU_TYPE_POWERPC:
+ case CPU_TYPE_I386:
+ if ( fOutputKind != Options::kDynamicLibrary )
+ fSplitSegs = false;
+ // make sure read and write segments are proper distance apart
+ if ( fSplitSegs && (fBaseWritableAddress-fBaseAddress != 0x10000000) )
+ fBaseWritableAddress = fBaseAddress + 0x10000000;
+ break;
+ case CPU_TYPE_ARM:
+ if ( fOutputKind != Options::kDynamicLibrary ) {
+ fSplitSegs = false;
+ }
+ else {
+ // make sure read and write segments are proper distance apart
+ if ( fSplitSegs && (fBaseWritableAddress-fBaseAddress != 0x08000000) )
+ fBaseWritableAddress = fBaseAddress + 0x08000000;
+ }
+ break;
+ default:
+ fSplitSegs = false;
+ fBaseAddress = 0;
+ fBaseWritableAddress = 0;
+ }
+ }
+
+ // <rdar://problem/6138961> -r implies no prebinding for all architectures
+ if ( fOutputKind == Options::kObjectFile )
+ fPrebind = false;
+
+ // disable prebinding depending on arch and min OS version
+ if ( fPrebind ) {
+ switch ( fArchitecture ) {
+ case CPU_TYPE_POWERPC:
+ case CPU_TYPE_I386:
+ if ( fReaderOptions.fMacVersionMin == ObjectFile::ReaderOptions::k10_4 ) {
+ // in 10.4 only split seg dylibs are prebound
+ if ( (fOutputKind != Options::kDynamicLibrary) || ! fSplitSegs )
+ fPrebind = false;
+ }
+ else if ( fReaderOptions.fMacVersionMin >= ObjectFile::ReaderOptions::k10_5 ) {
+ // in 10.5 nothing is prebound
+ fPrebind = false;
+ }
+ else {
+ // in 10.3 and earlier only dylibs and main executables could be prebound
+ switch ( fOutputKind ) {
+ case Options::kDynamicExecutable:
+ case Options::kDynamicLibrary:
+ // only main executables and dylibs can be prebound
+ break;
+ case Options::kStaticExecutable:
+ case Options::kDynamicBundle:
+ case Options::kObjectFile:
+ case Options::kDyld:
+ case Options::kPreload:
+ case Options::kKextBundle:
+ // disable prebinding for everything else
+ fPrebind = false;
+ break;
+ }
+ }
+ break;
+ case CPU_TYPE_POWERPC64:
+ case CPU_TYPE_X86_64:
+ fPrebind = false;
+ break;
+ case CPU_TYPE_ARM:
+ switch ( fOutputKind ) {
+ case Options::kDynamicExecutable:
+ case Options::kDynamicLibrary:
+ // only main executables and dylibs can be prebound
+ break;
+ case Options::kStaticExecutable:
+ case Options::kDynamicBundle:
+ case Options::kObjectFile:
+ case Options::kDyld:
+ case Options::kPreload:
+ case Options::kKextBundle:
+ // disable prebinding for everything else
+ fPrebind = false;
+ break;
+ }
+ break;
+ }
+ }
+
+ // only prebound images can be split-seg
+ if ( fSplitSegs && !fPrebind )
+ fSplitSegs = false;
+
+ // determine if info for shared region should be added
+ if ( fOutputKind == Options::kDynamicLibrary ) {
+ if ( minOS(ObjectFile::ReaderOptions::k10_5, ObjectFile::ReaderOptions::k3_0) )
+ if ( !fPrebind )
+ if ( (strncmp(this->installPath(), "/usr/lib/", 9) == 0)
+ || (strncmp(this->installPath(), "/System/Library/", 16) == 0) )
+ fSharedRegionEligible = true;
+ }
+
+ // figure out if module table is needed for compatibility with old ld/dyld
+ if ( fOutputKind == Options::kDynamicLibrary ) {
+ switch ( fArchitecture ) {
+ case CPU_TYPE_POWERPC: // 10.3 and earlier dyld requires a module table
+ case CPU_TYPE_I386: // ld_classic for 10.4.x requires a module table
+ if ( fReaderOptions.fMacVersionMin <= ObjectFile::ReaderOptions::k10_5 )
+ fNeedsModuleTable = true;
+ break;
+ case CPU_TYPE_ARM:
+ if ( fPrebind )
+ fNeedsModuleTable = true; // redo_prebinding requires a module table
+ break;
+ }
+ }
+
+ // <rdar://problem/5366363> -r -x implies -S
+ if ( (fOutputKind == Options::kObjectFile) && (fLocalSymbolHandling == kLocalSymbolsNone) )
+ fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoNone;
+
+ // choose how to process unwind info
+ switch ( fArchitecture ) {
+ case CPU_TYPE_I386:
+ case CPU_TYPE_X86_64:
+ switch ( fOutputKind ) {
+ case Options::kObjectFile:
+ case Options::kStaticExecutable:
+ case Options::kPreload:
+ case Options::kKextBundle:
+ fReaderOptions.fAddCompactUnwindEncoding = false;
+ break;
+ case Options::kDyld:
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ case Options::kDynamicExecutable:
+ //if ( fReaderOptions.fAddCompactUnwindEncoding && (fReaderOptions.fVersionMin >= ObjectFile::ReaderOptions::k10_6) )
+ // fReaderOptions.fRemoveDwarfUnwindIfCompactExists = true;
+ break;
+ }
+ break;
+ case CPU_TYPE_POWERPC:
+ case CPU_TYPE_POWERPC64:
+ case CPU_TYPE_ARM:
+ fReaderOptions.fAddCompactUnwindEncoding = false;
+ fReaderOptions.fRemoveDwarfUnwindIfCompactExists = false;
+ break;
+ case 0:
+ // if -arch is missing, assume we don't want compact unwind info
+ fReaderOptions.fAddCompactUnwindEncoding = false;
+ break;
+ }
+
+ // only ARM main executables can be encrypted
+ if ( fOutputKind != Options::kDynamicExecutable )
+ fEncryptable = false;
+ if ( fArchitecture != CPU_TYPE_ARM )
+ fEncryptable = false;
+
+ // don't move inits in dyld because dyld wants certain
+ // entries point at stable locations at the start of __text
+ if ( fOutputKind == Options::kDyld )
+ fReaderOptions.fAutoOrderInitializers = false;
+
+
+ // disable __data ordering for some output kinds
+ switch ( fOutputKind ) {
+ case Options::kObjectFile:
+ case Options::kDyld:
+ case Options::kStaticExecutable:
+ case Options::kPreload:
+ case Options::kKextBundle:
+ fOrderData = false;
+ break;
+ case Options::kDynamicExecutable:
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ break;
+ }
+
+ // only use compressed LINKEDIT for x86_64 and i386
+ if ( fMakeCompressedDyldInfo ) {
+ switch (fArchitecture) {
+ case CPU_TYPE_I386:
+ case CPU_TYPE_X86_64:
+ if ( fReaderOptions.fMacVersionMin >= ObjectFile::ReaderOptions::k10_6 )
+ fMakeClassicDyldInfo = false;
+ else if ( fReaderOptions.fMacVersionMin < ObjectFile::ReaderOptions::k10_5 )
+ fMakeCompressedDyldInfo = false;
+ break;
+ case CPU_TYPE_POWERPC:
+ case CPU_TYPE_ARM:
+ case CPU_TYPE_POWERPC64:
+ default:
+ fMakeCompressedDyldInfo = false;
+ }
+ }
+
+ // only use compressed LINKEDIT for final linked images
+ if ( fMakeCompressedDyldInfo ) {
+ switch ( fOutputKind ) {
+ case Options::kDynamicExecutable:
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ break;
+ case Options::kPreload:
+ case Options::kStaticExecutable:
+ case Options::kObjectFile:
+ case Options::kDyld:
+ case Options::kKextBundle:
+ fMakeCompressedDyldInfo = false;
+ break;
+ }
+ }
+ fReaderOptions.fMakeCompressedDyldInfo = fMakeCompressedDyldInfo;
+
+ // only ARM enforces that cpu-sub-types must match
+ if ( fArchitecture != CPU_TYPE_ARM )
+ fAllowCpuSubtypeMismatches = true;
+}
+
+void Options::checkIllegalOptionCombinations()
+{
+ // check -undefined setting
+ switch ( fUndefinedTreatment ) {
+ case kUndefinedError:
+ case kUndefinedDynamicLookup:
+ // always legal
+ break;
+ case kUndefinedWarning:
+ case kUndefinedSuppress:
+ // requires flat namespace
+ if ( fNameSpace == kTwoLevelNameSpace )
+ throw "can't use -undefined warning or suppress with -twolevel_namespace";
+ break;
+ }
+
+ // unify -sub_umbrella with dylibs
+ for (std::vector<const char*>::iterator it = fSubUmbellas.begin(); it != fSubUmbellas.end(); it++) {
+ const char* subUmbrella = *it;
+ bool found = false;
+ for (std::vector<Options::FileInfo>::iterator fit = fInputFiles.begin(); fit != fInputFiles.end(); fit++) {
+ Options::FileInfo& info = *fit;
+ const char* lastSlash = strrchr(info.path, '/');
+ if ( lastSlash == NULL )
+ lastSlash = info.path - 1;
+ if ( strcmp(&lastSlash[1], subUmbrella) == 0 ) {
+ info.options.fReExport = true;
+ found = true;
+ break;
+ }
+ }
+ if ( ! found )
+ warning("-sub_umbrella %s does not match a supplied dylib", subUmbrella);
+ }
+
+ // unify -sub_library with dylibs
+ for (std::vector<const char*>::iterator it = fSubLibraries.begin(); it != fSubLibraries.end(); it++) {
+ const char* subLibrary = *it;
+ bool found = false;
+ for (std::vector<Options::FileInfo>::iterator fit = fInputFiles.begin(); fit != fInputFiles.end(); fit++) {
+ Options::FileInfo& info = *fit;
+ const char* lastSlash = strrchr(info.path, '/');
+ if ( lastSlash == NULL )
+ lastSlash = info.path - 1;
+ const char* dot = strchr(&lastSlash[1], '.');
+ if ( dot == NULL )
+ dot = &lastSlash[strlen(lastSlash)];
+ if ( strncmp(&lastSlash[1], subLibrary, dot-lastSlash-1) == 0 ) {
+ info.options.fReExport = true;
+ found = true;
+ break;
+ }
+ }
+ if ( ! found )
+ warning("-sub_library %s does not match a supplied dylib", subLibrary);
+ }
+
+ // sync reader options
+ if ( fNameSpace != kTwoLevelNameSpace )
+ fReaderOptions.fFlatNamespace = true;
+
+ // check -stack_addr
+ if ( fStackAddr != 0 ) {
+ switch (fArchitecture) {
+ case CPU_TYPE_I386:
+ case CPU_TYPE_POWERPC:
+ case CPU_TYPE_ARM:
+ if ( fStackAddr > 0xFFFFFFFF )
+ 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 )
+ throw "-stack_addr must be multiples of 4K";
+ if ( fStackSize == 0 )
+ throw "-stack_addr must be used with -stack_size";
+ }
+
+ // check -stack_size
+ if ( fStackSize != 0 ) {
+ switch (fArchitecture) {
+ case CPU_TYPE_I386:
+ case CPU_TYPE_POWERPC:
+ if ( fStackSize > 0xFFFFFFFF )
+ throw "-stack_size must be < 4G for 32-bit processes";
+ if ( fStackAddr == 0 ) {
+ fStackAddr = 0xC0000000;
+ }
+ if ( (fStackAddr > 0xB0000000) && ((fStackAddr-fStackSize) < 0xB0000000) )
+ warning("custom stack placement overlaps and will disable shared region");
+ break;
+ case CPU_TYPE_ARM:
+ if ( fStackSize > 0xFFFFFFFF )
+ throw "-stack_size must be < 4G for 32-bit processes";
+ if ( fStackAddr == 0 )
+ fStackAddr = 0x30000000;
+ if ( fStackAddr > 0x40000000)
+ throw "-stack_addr must be < 1G for arm";
+ case CPU_TYPE_POWERPC64:
+ case CPU_TYPE_X86_64:
+ if ( fStackAddr == 0 ) {
+ fStackAddr = 0x00007FFF5C000000LL;
+ }
+ break;
+ }
+ if ( (fStackSize & -4096) != fStackSize )
+ throw "-stack_size must be multiples of 4K";
+ switch ( fOutputKind ) {
+ case Options::kDynamicExecutable:
+ case Options::kStaticExecutable:
+ // custom stack size only legal when building main executable
+ break;
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ case Options::kObjectFile:
+ case Options::kDyld:
+ case Options::kPreload:
+ case Options::kKextBundle:
+ throw "-stack_size option can only be used when linking a main executable";
+ }
+ }
+
+ // check that -allow_stack_execute is only used with main executables
+ if ( fExecutableStack ) {
+ switch ( fOutputKind ) {
+ case Options::kDynamicExecutable:
+ case Options::kStaticExecutable:
+ // -allow_stack_execute size only legal when building main executable
+ break;
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ case Options::kObjectFile:
+ case Options::kDyld:
+ case Options::kPreload:
+ case Options::kKextBundle:
+ throw "-allow_stack_execute option can only be used when linking a main executable";
+ }
+ }
+
+ // check -client_name is only used when making a bundle or main executable
+ if ( fClientName != NULL ) {
+ switch ( fOutputKind ) {
+ case Options::kDynamicExecutable:
+ case Options::kDynamicBundle:
+ break;
+ case Options::kStaticExecutable:
+ case Options::kDynamicLibrary:
+ case Options::kObjectFile:
+ case Options::kDyld:
+ case Options::kPreload:
+ case Options::kKextBundle:
+ throw "-client_name can only be used with -bundle";
+ }
+ }
+
+ // check -init is only used when building a dylib
+ if ( (fInitFunctionName != NULL) && (fOutputKind != Options::kDynamicLibrary) )
+ throw "-init can only be used with -dynamiclib";
+
+ // check -bundle_loader only used with -bundle
+ if ( (fBundleLoader != NULL) && (fOutputKind != Options::kDynamicBundle) )
+ throw "-bundle_loader can only be used with -bundle";
+
+ // check -dtrace not used with -r
+ if ( (fDtraceScriptName != NULL) && (fOutputKind == Options::kObjectFile) )
+ throw "-dtrace can only be used when creating final linked images";
+
+ // check -d can only be used with -r
+ if ( fReaderOptions.fMakeTentativeDefinitionsReal && (fOutputKind != Options::kObjectFile) )
+ throw "-d can only be used with -r";
+
+ // check that -root_safe is not used with -r
+ if ( fReaderOptions.fRootSafe && (fOutputKind == Options::kObjectFile) )
+ throw "-root_safe cannot be used with -r";
+
+ // check that -setuid_safe is not used with -r
+ if ( fReaderOptions.fSetuidSafe && (fOutputKind == Options::kObjectFile) )
+ throw "-setuid_safe cannot be used with -r";
+
+ // make sure all required exported symbols exist
+ std::vector<const char*> impliedExports;
+ for (NameSet::iterator it=fExportSymbols.regularBegin(); it != fExportSymbols.regularEnd(); it++) {
+ const char* name = *it;
+ // never export .eh symbols
+ const int len = strlen(name);
+ if ( (strcmp(&name[len-3], ".eh") == 0) || (strncmp(name, ".objc_category_name_", 20) == 0) )
+ warning("ignoring %s in export list", name);
+ else
+ fInitialUndefines.push_back(name);
+ if ( strncmp(name, ".objc_class_name_", 17) == 0 ) {
+ // rdar://problem/4718189 map ObjC class names to new runtime names
+ switch (fArchitecture) {
+ case CPU_TYPE_POWERPC64:
+ case CPU_TYPE_X86_64:
+ case CPU_TYPE_ARM:
+ char* temp;
+ asprintf(&temp, "_OBJC_CLASS_$_%s", &name[17]);
+ impliedExports.push_back(temp);
+ asprintf(&temp, "_OBJC_METACLASS_$_%s", &name[17]);
+ impliedExports.push_back(temp);
+ break;
+ }
+ }
+ }
+ for (std::vector<const char*>::iterator it=impliedExports.begin(); it != impliedExports.end(); it++) {
+ const char* name = *it;
+ fExportSymbols.insert(name);
+ fInitialUndefines.push_back(name);
+ }
+
+ // make sure that -init symbol exist
+ if ( fInitFunctionName != NULL )
+ fInitialUndefines.push_back(fInitFunctionName);
+
+ // check custom segments
+ if ( fCustomSegmentAddresses.size() != 0 ) {
+ // verify no segment is in zero page
+ if ( fZeroPageSize != ULLONG_MAX ) {
+ for (std::vector<SegmentStart>::iterator it = fCustomSegmentAddresses.begin(); it != fCustomSegmentAddresses.end(); ++it) {
+ if ( (it->address >= 0) && (it->address < fZeroPageSize) )
+ throwf("-segaddr %s 0x%X conflicts with -pagezero_size", it->name, it->address);
+ }
+ }
+ // verify no duplicates
+ for (std::vector<SegmentStart>::iterator it = fCustomSegmentAddresses.begin(); it != fCustomSegmentAddresses.end(); ++it) {
+ for (std::vector<SegmentStart>::iterator it2 = fCustomSegmentAddresses.begin(); it2 != fCustomSegmentAddresses.end(); ++it2) {
+ if ( (it->address == it2->address) && (it != it2) )
+ throwf("duplicate -segaddr addresses for %s and %s", it->name, it2->name);
+ }
+ // a custom segment address of zero will disable the use of a zero page
+ if ( it->address == 0 )
+ fZeroPageSize = 0;
+ }
+ }
+
+ if ( fZeroPageSize == ULLONG_MAX ) {
+ // zero page size not specified on command line, set default
+ switch (fArchitecture) {
+ case CPU_TYPE_I386:
+ case CPU_TYPE_POWERPC:
+ case CPU_TYPE_ARM:
+ // first 4KB for 32-bit architectures
+ fZeroPageSize = 0x1000;
+ break;
+ case CPU_TYPE_POWERPC64:
+ // first 4GB for ppc64 on 10.5
+ if ( fReaderOptions.fMacVersionMin >= ObjectFile::ReaderOptions::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:
+ case Options::kPreload:
+ case Options::kKextBundle:
+ if ( fZeroPageSize != 0 )
+ 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";
+
+ // can't use -rpath unless targeting 10.5 or later
+ if ( fRPaths.size() > 0 ) {
+ if ( !minOS(ObjectFile::ReaderOptions::k10_5, ObjectFile::ReaderOptions::k2_0) )
+ throw "-rpath can only be used when targeting Mac OS X 10.5 or later";
+ switch ( fOutputKind ) {
+ case Options::kDynamicExecutable:
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ break;
+ case Options::kStaticExecutable:
+ case Options::kObjectFile:
+ case Options::kDyld:
+ case Options::kPreload:
+ case Options::kKextBundle:
+ throw "-rpath can only be used when creating a dynamic final linked image";
+ }
+ }
+
+ // check -pie is only used when building a dynamic main executable for 10.5
+ if ( fPositionIndependentExecutable ) {
+ switch ( fOutputKind ) {
+ case Options::kDynamicExecutable:
+ case Options::kPreload:
+ break;
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ warning("-pie being ignored. It is only used when linking a main executable");
+ break;
+ case Options::kStaticExecutable:
+ case Options::kObjectFile:
+ case Options::kDyld:
+ case Options::kKextBundle:
+ throw "-pie can only be used when linking a main executable";
+ }
+ if ( !minOS(ObjectFile::ReaderOptions::k10_5, ObjectFile::ReaderOptions::k2_0) )
+ throw "-pie can only be used when targeting Mac OS X 10.5 or later";
+ }
+
+ // check -read_only_relocs is not used with x86_64
+ if ( fAllowTextRelocs ) {
+ if ( (fArchitecture == CPU_TYPE_X86_64) && (fOutputKind != kKextBundle) ) {
+ warning("-read_only_relocs cannot be used with x86_64");
+ fAllowTextRelocs = false;
+ }
+ }
+
+ // check -mark_auto_dead_strip is only used with dylibs
+ if ( fMarkDeadStrippableDylib ) {
+ if ( fOutputKind != Options::kDynamicLibrary ) {
+ warning("-mark_auto_dead_strip can only be used when creating a dylib");
+ fMarkDeadStrippableDylib = false;
+ }
+ }
+
+}
+
+
+void Options::checkForClassic(int argc, const char* argv[])
+{
+ // scan options
+ bool archFound = false;
+ bool staticFound = false;
+ bool dtraceFound = false;
+ bool kextFound = false;
+ bool rFound = false;
+ bool creatingMachKernel = false;
+ bool newLinker = false;
+
+ // build command line buffer in case ld crashes
+ for(int i=1; i < argc; ++i) {
+ strlcat(crashreporterBuffer, argv[i], 1000);
+ strlcat(crashreporterBuffer, " ", 1000);
+ }
+
+ for(int i=0; i < argc; ++i) {
+ const char* arg = argv[i];
+ if ( arg[0] == '-' ) {
+ if ( strcmp(arg, "-arch") == 0 ) {
+ parseArch(argv[++i]);
+ archFound = true;
+ }
+ else if ( strcmp(arg, "-static") == 0 ) {
+ staticFound = true;
+ }
+ else if ( strcmp(arg, "-kext") == 0 ) {
+ kextFound = true;
+ }
+ else if ( strcmp(arg, "-dtrace") == 0 ) {
+ dtraceFound = true;
+ }
+ else if ( strcmp(arg, "-r") == 0 ) {
+ rFound = true;
+ }
+ else if ( strcmp(arg, "-new_linker") == 0 ) {
+ newLinker = true;
+ }
+ else if ( strcmp(arg, "-classic_linker") == 0 ) {
+ // ld_classic does not understand this option, so remove it
+ for(int j=i; j < argc; ++j)
+ argv[j] = argv[j+1];
+ this->gotoClassicLinker(argc-1, argv);
+ }
+ else if ( strcmp(arg, "-o") == 0 ) {
+ const char* outfile = argv[++i];
+ if ( (outfile != NULL) && (strstr(outfile, "/mach_kernel") != NULL) )
+ creatingMachKernel = true;
+ }
+ }
+ }
+
+ // -dtrace only supported by new linker
+ if( dtraceFound )
+ return;
+
+ if( archFound ) {
+ switch ( fArchitecture ) {
+ case CPU_TYPE_POWERPC:
+ case CPU_TYPE_I386:
+ case CPU_TYPE_ARM:
+// if ( staticFound && (rFound || !creatingMachKernel) ) {
+ if ( (staticFound || kextFound) && !newLinker ) {
+ // this environment variable will disable use of ld_classic for -static links
+ if ( getenv("LD_NO_CLASSIC_LINKER_STATIC") == NULL ) {
+ // ld_classic does not support -iphoneos_version_min, so change
+ for(int j=0; j < argc; ++j) {
+ if ( strcmp(argv[j], "-iphoneos_version_min") == 0) {
+ argv[j] = "-macosx_version_min";
+ if ( j < argc-1 )
+ argv[j+1] = "10.5";
+ break;
+ }
+ }
+ // ld classic does not understand -kext (change to -static -r)
+ if ( kextFound ) {
+ for(int j=0; j < argc; ++j) {
+ if ( strcmp(argv[j], "-kext") == 0)
+ argv[j] = "-r";
+ else if ( strcmp(argv[j], "-dynamic") == 0)
+ argv[j] = "-static";
+ }
+ }
+ this->gotoClassicLinker(argc, argv);
+ }
+ }
+ break;
+ }
+ }
+ else {
+ // work around for VSPTool
+ if ( staticFound )
+ this->gotoClassicLinker(argc, argv);
+ }
+
+}
+
+void Options::gotoClassicLinker(int argc, const char* argv[])
+{
+ argv[0] = "ld_classic";
+ char path[PATH_MAX];
+ uint32_t bufSize = PATH_MAX;
+ if ( _NSGetExecutablePath(path, &bufSize) != -1 ) {
+ char* lastSlash = strrchr(path, '/');
+ if ( lastSlash != NULL ) {
+ strcpy(lastSlash+1, "ld_classic");
+ execvp(path, (char**)argv);
+ }
+ }
+ // in case of error in above, try searching for ld_classic via PATH
+ execvp(argv[0], (char**)argv);
+ fprintf(stderr, "can't exec ld_classic\n");
+ exit(1);
+}
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2005-2007 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifndef __OPTIONS__
+#define __OPTIONS__
+
+
+#include <stdint.h>
+#include <mach/machine.h>
+
+#include <vector>
+#include <ext/hash_set>
+#include <ext/hash_map>
+
+#include "ObjectFile.h"
+
+extern void throwf (const char* format, ...) __attribute__ ((noreturn));
+extern void warning(const char* format, ...);
+
+class LibraryOptions
+{
+public:
+ LibraryOptions() : fWeakImport(false), fReExport(false), fBundleLoader(false), fLazyLoad(false), fForceLoad(false) {}
+ // for dynamic libraries
+ bool fWeakImport;
+ bool fReExport;
+ bool fBundleLoader;
+ bool fLazyLoad;
+ // for static libraries
+ bool fForceLoad;
+};
+
+
+
+//
+// The public interface to the Options class is the abstract representation of what work the linker
+// should do.
+//
+// This abstraction layer will make it easier to support a future where the linker is a shared library
+// invoked directly from Xcode. The target settings in Xcode would be used to directly construct an Options
+// object (without building a command line which is then parsed).
+//
+//
+class Options
+{
+public:
+ Options(int argc, const char* argv[]);
+ ~Options();
+
+ enum OutputKind { kDynamicExecutable, kStaticExecutable, kDynamicLibrary, kDynamicBundle, kObjectFile, kDyld, kPreload, kKextBundle };
+ enum NameSpace { kTwoLevelNameSpace, kFlatNameSpace, kForceFlatNameSpace };
+ // Standard treatment for many options.
+ enum Treatment { kError, kWarning, kSuppress, kNULL, kInvalid };
+ enum UndefinedTreatment { kUndefinedError, kUndefinedWarning, kUndefinedSuppress, kUndefinedDynamicLookup };
+ enum WeakReferenceMismatchTreatment { kWeakReferenceMismatchError, kWeakReferenceMismatchWeak,
+ kWeakReferenceMismatchNonWeak };
+ enum CommonsMode { kCommonsIgnoreDylibs, kCommonsOverriddenByDylibs, kCommonsConflictsDylibsError };
+ enum DeadStripMode { kDeadStripOff, kDeadStripOn, kDeadStripOnPlusUnusedInits };
+ enum UUIDMode { kUUIDNone, kUUIDRandom, kUUIDContent };
+ enum LocalSymbolHandling { kLocalSymbolsAll, kLocalSymbolsNone, kLocalSymbolsSelectiveInclude, kLocalSymbolsSelectiveExclude };
+
+ struct FileInfo {
+ const char* path;
+ uint64_t fileLen;
+ time_t modTime;
+ LibraryOptions options;
+ };
+
+ struct ExtraSection {
+ const char* segmentName;
+ const char* sectionName;
+ const char* path;
+ const uint8_t* data;
+ uint64_t dataLen;
+ };
+
+ struct SectionAlignment {
+ const char* segmentName;
+ const char* sectionName;
+ uint8_t alignment;
+ };
+
+ struct OrderedSymbol {
+ const char* symbolName;
+ const char* objectFileName;
+ };
+
+ struct SegmentStart {
+ const char* name;
+ uint64_t address;
+ };
+
+ struct SegmentSize {
+ const char* name;
+ uint64_t size;
+ };
+
+ struct SegmentProtect {
+ const char* name;
+ uint32_t max;
+ uint32_t init;
+ };
+
+ struct DylibOverride {
+ const char* installName;
+ const char* useInstead;
+ };
+
+
+ const ObjectFile::ReaderOptions& readerOptions();
+ const char* getOutputFilePath();
+ std::vector<FileInfo>& getInputFiles();
+
+ cpu_type_t architecture() { return fArchitecture; }
+ bool preferSubArchitecture() { return fHasPreferredSubType; }
+ cpu_subtype_t subArchitecture() { return fSubArchitecture; }
+ bool allowSubArchitectureMismatches() { return fAllowCpuSubtypeMismatches; }
+ OutputKind outputKind();
+ bool prebind();
+ bool bindAtLoad();
+ bool fullyLoadArchives();
+ NameSpace nameSpace();
+ const char* installPath(); // only for kDynamicLibrary
+ uint32_t currentVersion(); // only for kDynamicLibrary
+ uint32_t compatibilityVersion(); // only for kDynamicLibrary
+ const char* entryName(); // only for kDynamicExecutable or kStaticExecutable
+ const char* executablePath();
+ uint64_t baseAddress();
+ bool keepPrivateExterns(); // only for kObjectFile
+ bool needsModuleTable(); // only for kDynamicLibrary
+ bool interposable(const char* name);
+ bool hasExportRestrictList(); // -exported_symbol or -unexported_symbol
+ bool hasExportMaskList(); // just -exported_symbol
+ bool hasWildCardExportRestrictList();
+ bool allGlobalsAreDeadStripRoots();
+ bool shouldExport(const char*);
+ bool ignoreOtherArchInputFiles();
+ bool forceCpuSubtypeAll();
+ bool traceDylibs();
+ bool traceArchives();
+ DeadStripMode deadStrip();
+ UndefinedTreatment undefinedTreatment();
+ ObjectFile::ReaderOptions::MacVersionMin macosxVersionMin() { return fReaderOptions.fMacVersionMin; }
+ ObjectFile::ReaderOptions::IPhoneVersionMin iphoneOSVersionMin() { return fReaderOptions.fIPhoneVersionMin; }
+ bool minOS(ObjectFile::ReaderOptions::MacVersionMin mac, ObjectFile::ReaderOptions::IPhoneVersionMin iPhoneOS);
+ bool messagesPrefixedWithArchitecture();
+ Treatment picTreatment();
+ WeakReferenceMismatchTreatment weakReferenceMismatchTreatment();
+ const char* umbrellaName();
+ std::vector<const char*>& allowableClients();
+ const char* clientName();
+ const char* initFunctionName(); // only for kDynamicLibrary
+ const char* dotOutputFile();
+ uint64_t zeroPageSize();
+ bool hasCustomStack();
+ uint64_t customStackSize();
+ uint64_t customStackAddr();
+ bool hasExecutableStack();
+ std::vector<const char*>& initialUndefines();
+ bool printWhyLive(const char* name);
+ uint32_t minimumHeaderPad();
+ uint64_t segmentAlignment() { return fSegmentAlignment; }
+ bool maxMminimumHeaderPad() { return fMaxMinimumHeaderPad; }
+ std::vector<ExtraSection>& extraSections();
+ std::vector<SectionAlignment>& sectionAlignments();
+ CommonsMode commonsMode();
+ bool warnCommons();
+ bool keepRelocations();
+ FileInfo findFile(const char* path);
+ UUIDMode getUUIDMode() { return fUUIDMode; }
+ bool warnStabs();
+ bool pauseAtEnd() { return fPause; }
+ bool printStatistics() { return fStatistics; }
+ bool printArchPrefix() { return fMessagesPrefixedWithArchitecture; }
+ void gotoClassicLinker(int argc, const char* argv[]);
+ bool sharedRegionEligible() { return fSharedRegionEligible; }
+ bool printOrderFileStatistics() { return fPrintOrderFileStatistics; }
+ const char* dTraceScriptName() { return fDtraceScriptName; }
+ bool dTrace() { return (fDtraceScriptName != NULL); }
+ std::vector<OrderedSymbol>& orderedSymbols() { return fOrderedSymbols; }
+ bool splitSeg() { return fSplitSegs; }
+ uint64_t baseWritableAddress() { return fBaseWritableAddress; }
+ std::vector<SegmentStart>& customSegmentAddresses() { return fCustomSegmentAddresses; }
+ std::vector<SegmentSize>& customSegmentSizes() { return fCustomSegmentSizes; }
+ std::vector<SegmentProtect>& customSegmentProtections() { return fCustomSegmentProtections; }
+ bool saveTempFiles() { return fSaveTempFiles; }
+ const std::vector<const char*>& rpaths() { return fRPaths; }
+ bool readOnlyx86Stubs() { return fReadOnlyx86Stubs; }
+ std::vector<DylibOverride>& dylibOverrides() { return fDylibOverrides; }
+ const char* generatedMapPath() { return fMapPath; }
+ bool positionIndependentExecutable() { return fPositionIndependentExecutable; }
+ Options::FileInfo findFileUsingPaths(const char* path);
+ bool deadStripDylibs() { return fDeadStripDylibs; }
+ bool allowedUndefined(const char* name) { return ( fAllowedUndefined.find(name) != fAllowedUndefined.end() ); }
+ bool someAllowedUndefines() { return (fAllowedUndefined.size() != 0); }
+ LocalSymbolHandling localSymbolHandling() { return fLocalSymbolHandling; }
+ bool keepLocalSymbol(const char* symbolName);
+ bool allowTextRelocs() { return fAllowTextRelocs; }
+ bool warnAboutTextRelocs() { return fWarnTextRelocs; }
+ bool usingLazyDylibLinking() { return fUsingLazyDylibLinking; }
+ bool verbose() { return fVerbose; }
+ bool makeEncryptable() { return fEncryptable; }
+ bool needsUnwindInfoSection() { return fReaderOptions.fAddCompactUnwindEncoding; }
+ std::vector<const char*>& llvmOptions() { return fLLVMOptions; }
+ bool makeClassicDyldInfo() { return fMakeClassicDyldInfo; }
+ bool makeCompressedDyldInfo() { return fMakeCompressedDyldInfo; }
+ bool hasExportedSymbolOrder();
+ bool exportedSymbolOrder(const char* sym, unsigned int* order);
+ bool orderData() { return fOrderData; }
+ bool errorOnOtherArchFiles() { return fErrorOnOtherArchFiles; }
+ bool markAutoDeadStripDylib() { return fMarkDeadStrippableDylib; }
+ bool removeEHLabels() { return fReaderOptions.fNoEHLabels; }
+
+private:
+ class CStringEquals
+ {
+ public:
+ bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
+ };
+ typedef __gnu_cxx::hash_map<const char*, unsigned int, __gnu_cxx::hash<const char*>, CStringEquals> NameToOrder;
+ typedef __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> NameSet;
+ enum ExportMode { kExportDefault, kExportSome, kDontExportSome };
+ enum LibrarySearchMode { kSearchDylibAndArchiveInEachDir, kSearchAllDirsForDylibsThenAllDirsForArchives };
+ enum InterposeMode { kInterposeNone, kInterposeAllExternal, kInterposeSome };
+
+ class SetWithWildcards {
+ public:
+ void insert(const char*);
+ bool contains(const char*);
+ bool hasWildCards() { return !fWildCard.empty(); }
+ NameSet::iterator regularBegin() { return fRegular.begin(); }
+ NameSet::iterator regularEnd() { return fRegular.end(); }
+ private:
+ static bool hasWildCards(const char*);
+ bool wildCardMatch(const char* pattern, const char* candidate);
+ bool inCharRange(const char*& range, unsigned char c);
+
+ NameSet fRegular;
+ std::vector<const char*> fWildCard;
+ };
+
+
+ void parse(int argc, const char* argv[]);
+ void checkIllegalOptionCombinations();
+ void buildSearchPaths(int argc, const char* argv[]);
+ void parseArch(const char* architecture);
+ FileInfo findLibrary(const char* rootName, bool dylibsOnly=false);
+ FileInfo findFramework(const char* frameworkName);
+ FileInfo findFramework(const char* rootName, const char* suffix);
+ bool checkForFile(const char* format, const char* dir, const char* rootName,
+ FileInfo& result);
+ uint32_t parseVersionNumber(const char*);
+ void parseSectionOrderFile(const char* segment, const char* section, const char* path);
+ void parseOrderFile(const char* path, bool cstring);
+ void addSection(const char* segment, const char* section, const char* path);
+ void addSubLibrary(const char* name);
+ void loadFileList(const char* fileOfPaths);
+ uint64_t parseAddress(const char* addr);
+ void loadExportFile(const char* fileOfExports, const char* option, SetWithWildcards& set);
+ void parseAliasFile(const char* fileOfAliases);
+ void parsePreCommandLineEnvironmentSettings();
+ void parsePostCommandLineEnvironmentSettings();
+ void setUndefinedTreatment(const char* treatment);
+ void setMacOSXVersionMin(const char* version);
+ void setIPhoneVersionMin(const char* version);
+ void setWeakReferenceMismatchTreatment(const char* treatment);
+ void addDylibOverride(const char* paths);
+ void addSectionAlignment(const char* segment, const char* section, const char* alignment);
+ CommonsMode parseCommonsTreatment(const char* mode);
+ Treatment parseTreatment(const char* treatment);
+ void reconfigureDefaults();
+ void checkForClassic(int argc, const char* argv[]);
+ void parseSegAddrTable(const char* segAddrPath, const char* installPath);
+ void addLibrary(const FileInfo& info);
+ void warnObsolete(const char* arg);
+ uint32_t parseProtection(const char* prot);
+ void loadSymbolOrderFile(const char* fileOfExports, NameToOrder& orderMapping);
+
+
+ ObjectFile::ReaderOptions fReaderOptions;
+ const char* fOutputFile;
+ std::vector<Options::FileInfo> fInputFiles;
+ cpu_type_t fArchitecture;
+ cpu_subtype_t fSubArchitecture;
+ OutputKind fOutputKind;
+ bool fHasPreferredSubType;
+ bool fPrebind;
+ bool fBindAtLoad;
+ bool fKeepPrivateExterns;
+ bool fNeedsModuleTable;
+ bool fIgnoreOtherArchFiles;
+ bool fErrorOnOtherArchFiles;
+ bool fForceSubtypeAll;
+ InterposeMode fInterposeMode;
+ DeadStripMode fDeadStrip;
+ NameSpace fNameSpace;
+ uint32_t fDylibCompatVersion;
+ uint32_t fDylibCurrentVersion;
+ const char* fDylibInstallName;
+ const char* fFinalName;
+ const char* fEntryName;
+ uint64_t fBaseAddress;
+ uint64_t fBaseWritableAddress;
+ bool fSplitSegs;
+ SetWithWildcards fExportSymbols;
+ SetWithWildcards fDontExportSymbols;
+ SetWithWildcards fInterposeList;
+ NameToOrder fExportSymbolsOrder;
+ ExportMode fExportMode;
+ LibrarySearchMode fLibrarySearchMode;
+ UndefinedTreatment fUndefinedTreatment;
+ bool fMessagesPrefixedWithArchitecture;
+ WeakReferenceMismatchTreatment fWeakReferenceMismatchTreatment;
+ std::vector<const char*> fSubUmbellas;
+ std::vector<const char*> fSubLibraries;
+ std::vector<const char*> fAllowableClients;
+ std::vector<const char*> fRPaths;
+ const char* fClientName;
+ const char* fUmbrellaName;
+ const char* fInitFunctionName;
+ const char* fDotOutputFile;
+ const char* fExecutablePath;
+ const char* fBundleLoader;
+ const char* fDtraceScriptName;
+ const char* fSegAddrTablePath;
+ const char* fMapPath;
+ uint64_t fZeroPageSize;
+ uint64_t fStackSize;
+ uint64_t fStackAddr;
+ bool fExecutableStack;
+ uint32_t fMinimumHeaderPad;
+ uint64_t fSegmentAlignment;
+ CommonsMode fCommonsMode;
+ UUIDMode fUUIDMode;
+ SetWithWildcards fLocalSymbolsIncluded;
+ SetWithWildcards fLocalSymbolsExcluded;
+ LocalSymbolHandling fLocalSymbolHandling;
+ bool fWarnCommons;
+ bool fVerbose;
+ bool fKeepRelocations;
+ bool fWarnStabs;
+ bool fTraceDylibSearching;
+ bool fPause;
+ bool fStatistics;
+ bool fPrintOptions;
+ bool fSharedRegionEligible;
+ bool fPrintOrderFileStatistics;
+ bool fReadOnlyx86Stubs;
+ bool fPositionIndependentExecutable;
+ bool fMaxMinimumHeaderPad;
+ bool fDeadStripDylibs;
+ bool fAllowTextRelocs;
+ bool fWarnTextRelocs;
+ bool fUsingLazyDylibLinking;
+ bool fEncryptable;
+ bool fOrderData;
+ bool fMarkDeadStrippableDylib;
+ bool fMakeClassicDyldInfo;
+ bool fMakeCompressedDyldInfo;
+ bool fNoEHLabels;
+ bool fAllowCpuSubtypeMismatches;
+ std::vector<const char*> fInitialUndefines;
+ NameSet fAllowedUndefined;
+ NameSet fWhyLive;
+ std::vector<ExtraSection> fExtraSections;
+ std::vector<SectionAlignment> fSectionAlignments;
+ std::vector<OrderedSymbol> fOrderedSymbols;
+ std::vector<SegmentStart> fCustomSegmentAddresses;
+ std::vector<SegmentSize> fCustomSegmentSizes;
+ std::vector<SegmentProtect> fCustomSegmentProtections;
+ std::vector<DylibOverride> fDylibOverrides;
+ std::vector<const char*> fLLVMOptions;
+ std::vector<const char*> fLibrarySearchPaths;
+ std::vector<const char*> fFrameworkSearchPaths;
+ std::vector<const char*> fSDKPaths;
+ bool fSaveTempFiles;
+};
+
+
+
+#endif // __OPTIONS__
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+#ifndef __SECTCREATE__
+#define __SECTCREATE__
+
+
+#include "ObjectFile.h"
+
+namespace SectCreate {
+
+ extern ObjectFile::Reader* MakeReader(const char* segmentName, const char* sectionName, const char* path, const uint8_t fileContent[], uint64_t fileLength);
+
+};
+
+
+#endif
+
+
+
+
--- /dev/null
+/*
+ * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#ifndef KLD
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include "dwarf2.h"
+#include "debugline.h"
+
+struct line_reader_data
+{
+ bool little_endian;
+
+ /* From the line number information header. */
+ uint8_t minimum_instruction_length;
+ int8_t line_base;
+ uint8_t line_range;
+ uint8_t opcode_base;
+ const uint8_t * standard_opcode_lengths;
+ size_t numdir;
+ const uint8_t * * dirnames;
+ size_t numfile_orig;
+ size_t numfile; /* As updated during execution of the table. */
+ const uint8_t * * filenames;
+
+ /* Current position in the line table. */
+ const uint8_t * cpos;
+ /* End of this part of the line table. */
+ const uint8_t * end;
+ /* Start of the line table. */
+ const uint8_t * init;
+
+ struct line_info cur;
+};
+
+/* Read in a word of fixed size, which may be unaligned, in the
+ appropriate endianness. */
+#define read_16(p) (lnd->little_endian \
+ ? ((p)[1] << 8 | (p)[0]) \
+ : ((p)[0] << 8 | (p)[1]))
+#define read_32(p) (lnd->little_endian \
+ ? ((p)[3] << 24 | (p)[2] << 16 | (p)[1] << 8 | (p)[0]) \
+ : ((p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3]))
+#define read_64(p) (lnd->little_endian \
+ ? ((uint64_t) (p)[7] << 56 | (uint64_t) (p)[6] << 48 \
+ | (uint64_t) (p)[5] << 40 | (uint64_t) (p)[4] << 32 \
+ | (uint64_t) (p)[3] << 24 | (uint64_t) (p)[2] << 16u \
+ | (uint64_t) (p)[1] << 8 | (uint64_t) (p)[0]) \
+ : ((uint64_t) (p)[0] << 56 | (uint64_t) (p)[1] << 48 \
+ | (uint64_t) (p)[2] << 40 | (uint64_t) (p)[3] << 32 \
+ | (uint64_t) (p)[4] << 24 | (uint64_t) (p)[5] << 16u \
+ | (uint64_t) (p)[6] << 8 | (uint64_t) (p)[7]))
+
+/* Skip over a LEB128 value (signed or unsigned). */
+static void
+skip_leb128 (struct line_reader_data * leb)
+{
+ while (leb->cpos != leb->end && *leb->cpos >= 0x80)
+ leb->cpos++;
+ if (leb->cpos != leb->end)
+ leb->cpos++;
+}
+
+/* Read a ULEB128 into a 64-bit word. Return (uint64_t)-1 on overflow
+ or error. On overflow, skip past the rest of the uleb128. */
+static uint64_t
+read_uleb128 (struct line_reader_data * leb)
+{
+ uint64_t result = 0;
+ int bit = 0;
+
+ do {
+ uint64_t b;
+
+ if (leb->cpos == leb->end)
+ return (uint64_t) -1;
+
+ b = *leb->cpos & 0x7f;
+
+ if (bit >= 64 || b << bit >> bit != b)
+ result = (uint64_t) -1;
+ else
+ result |= b << bit, bit += 7;
+ } while (*leb->cpos++ >= 0x80);
+ return result;
+}
+
+
+/* Read a SLEB128 into a 64-bit word. Return 0 on overflow or error
+ (which is not very helpful). On overflow, skip past the rest of
+ the SLEB128. For negative numbers, this actually overflows when
+ under -2^62, but since this is used for line numbers that ought to
+ be OK... */
+static int64_t
+read_sleb128 (struct line_reader_data * leb)
+{
+ const uint8_t * start_pos = leb->cpos;
+ uint64_t v = read_uleb128 (leb);
+ uint64_t signbit;
+
+ if (v >= 1ull << 63)
+ return 0;
+ if (leb->cpos - start_pos > 9)
+ return v;
+
+ signbit = 1ull << ((leb->cpos - start_pos) * 7 - 1);
+
+ return v | -(v & signbit);
+}
+
+/* Free a line_reader_data structure. */
+void
+line_free (struct line_reader_data * lnd)
+{
+ if (! lnd)
+ return;
+ if (lnd->dirnames)
+ free (lnd->dirnames);
+ if (lnd->filenames)
+ free (lnd->filenames);
+ free (lnd);
+}
+
+/* Return the pathname of the file in S, or NULL on error.
+ The result will have been allocated with malloc. */
+
+char *
+line_file (struct line_reader_data *lnd, uint64_t n)
+{
+ const uint8_t * prev_pos = lnd->cpos;
+ size_t filelen, dirlen;
+ uint64_t dir;
+ char * result;
+
+ /* I'm not sure if this is actually an error. */
+ if (n == 0
+ || n > lnd->numfile)
+ return NULL;
+
+ filelen = strlen ((const char *)lnd->filenames[n - 1]);
+ lnd->cpos = lnd->filenames[n - 1] + filelen + 1;
+ dir = read_uleb128 (lnd);
+ lnd->cpos = prev_pos;
+ if (dir == 0
+ || lnd->filenames[n - 1][0] == '/')
+ return strdup ((const char *)lnd->filenames[n - 1]);
+ else if (dir > lnd->numdir)
+ return NULL;
+
+ dirlen = strlen ((const char *) lnd->dirnames[dir - 1]);
+ result = malloc (dirlen + filelen + 2);
+ memcpy (result, lnd->dirnames[dir - 1], dirlen);
+ result[dirlen] = '/';
+ memcpy (result + dirlen + 1, lnd->filenames[n - 1], filelen);
+ result[dirlen + 1 + filelen] = '\0';
+ return result;
+}
+
+/* Initialize a state S. Return FALSE on error. */
+
+static void
+init_state (struct line_info *s)
+{
+ s->file = 1;
+ s->line = 1;
+ s->col = 0;
+ s->pc = 0;
+ s->end_of_sequence = false;
+}
+
+/* Read a debug_line section. */
+
+struct line_reader_data *
+line_open (const uint8_t * debug_line, size_t debug_line_size,
+ int little_endian)
+{
+ struct line_reader_data * lnd = NULL;
+ bool dwarf_size_64;
+
+ uint64_t lnd_length, header_length;
+ const uint8_t * table_start;
+
+ if (debug_line_size < 12)
+ return NULL;
+
+ lnd = malloc (sizeof (struct line_reader_data));
+ if (! lnd)
+ return NULL;
+ bzero(lnd, sizeof(struct line_reader_data));
+
+ lnd->little_endian = little_endian;
+ lnd->cpos = debug_line;
+
+ lnd_length = read_32 (lnd->cpos);
+ lnd->cpos += 4;
+ if (lnd_length == 0xffffffff)
+ {
+ lnd_length = read_64 (lnd->cpos);
+ lnd->cpos += 8;
+ dwarf_size_64 = true;
+ }
+ else if (lnd_length > 0xfffffff0)
+ /* Not a format we understand. */
+ goto error;
+ else
+ dwarf_size_64 = false;
+
+ if (debug_line_size < lnd_length + (dwarf_size_64 ? 12 : 4)
+ || lnd_length < (dwarf_size_64 ? 15 : 11))
+ /* Too small. */
+ goto error;
+
+ if (read_16 (lnd->cpos) != 2)
+ /* Unknown line number format. */
+ goto error;
+ lnd->cpos += 2;
+
+ header_length = dwarf_size_64 ? (uint64_t)read_64(lnd->cpos) : (uint64_t)read_32(lnd->cpos);
+ lnd->cpos += dwarf_size_64 ? 8 : 4;
+ if (lnd_length < header_length + (lnd->cpos - debug_line)
+ || header_length < 7)
+ goto error;
+
+ lnd->minimum_instruction_length = lnd->cpos[0];
+ /* Ignore default_is_stmt. */
+ lnd->line_base = lnd->cpos[2];
+ lnd->line_range = lnd->cpos[3];
+ lnd->opcode_base = lnd->cpos[4];
+
+ if (lnd->opcode_base == 0)
+ /* Every valid line number program must use at least opcode 0
+ for DW_LNE_end_sequence. */
+ goto error;
+
+ lnd->standard_opcode_lengths = lnd->cpos + 5;
+ if (header_length < (uint64_t)(5 + (lnd->opcode_base - 1)))
+ /* Header not long enough. */
+ goto error;
+ lnd->cpos += 5 + lnd->opcode_base - 1;
+ lnd->end = debug_line + header_length + (dwarf_size_64 ? 22 : 10);
+
+ /* Make table of offsets to directory names. */
+ table_start = lnd->cpos;
+ lnd->numdir = 0;
+ while (lnd->cpos != lnd->end && *lnd->cpos)
+ {
+ lnd->cpos = memchr (lnd->cpos, 0, lnd->end - lnd->cpos);
+ if (! lnd->cpos)
+ goto error;
+ lnd->cpos++;
+ lnd->numdir++;
+ }
+ if (lnd->cpos == lnd->end)
+ goto error;
+ lnd->dirnames = malloc (lnd->numdir * sizeof (const uint8_t *));
+ if (! lnd->dirnames)
+ goto error;
+ lnd->numdir = 0;
+ lnd->cpos = table_start;
+ while (*lnd->cpos)
+ {
+ lnd->dirnames[lnd->numdir++] = lnd->cpos;
+ lnd->cpos = memchr (lnd->cpos, 0, lnd->end - lnd->cpos) + 1;
+ }
+ lnd->cpos++;
+
+ /* Make table of offsets to file entries. */
+ table_start = lnd->cpos;
+ lnd->numfile = 0;
+ while (lnd->cpos != lnd->end && *lnd->cpos)
+ {
+ lnd->cpos = memchr (lnd->cpos, 0, lnd->end - lnd->cpos);
+ if (! lnd->cpos)
+ goto error;
+ lnd->cpos++;
+ skip_leb128 (lnd);
+ skip_leb128 (lnd);
+ skip_leb128 (lnd);
+ lnd->numfile++;
+ }
+ if (lnd->cpos == lnd->end)
+ goto error;
+ lnd->filenames = malloc (lnd->numfile * sizeof (const uint8_t *));
+ if (! lnd->filenames)
+ goto error;
+ lnd->numfile = 0;
+ lnd->cpos = table_start;
+ while (*lnd->cpos)
+ {
+ lnd->filenames[lnd->numfile++] = lnd->cpos;
+ lnd->cpos = memchr (lnd->cpos, 0, lnd->end - lnd->cpos) + 1;
+ skip_leb128 (lnd);
+ skip_leb128 (lnd);
+ skip_leb128 (lnd);
+ }
+ lnd->cpos++;
+
+ lnd->numfile_orig = lnd->numfile;
+ lnd->cpos = lnd->init = lnd->end;
+ lnd->end = debug_line + lnd_length + (dwarf_size_64 ? 12 : 4);
+
+ init_state (&lnd->cur);
+
+ return lnd;
+
+ error:
+ line_free (lnd);
+ return NULL;
+}
+
+/* Reset back to the beginning. */
+void
+line_reset (struct line_reader_data * lnd)
+{
+ lnd->cpos = lnd->init;
+ lnd->numfile = lnd->numfile_orig;
+ init_state (&lnd->cur);
+}
+
+/* Is there no more line data available? */
+int
+line_at_eof (struct line_reader_data * lnd)
+{
+ return lnd->cpos == lnd->end;
+}
+
+static bool
+next_state (struct line_reader_data *lnd)
+{
+ if (lnd->cur.end_of_sequence)
+ init_state (&lnd->cur);
+
+ for (;;)
+ {
+ uint8_t op;
+ uint64_t tmp;
+
+ if (lnd->cpos == lnd->end)
+ return false;
+ op = *lnd->cpos++;
+ if (op >= lnd->opcode_base)
+ {
+ op -= lnd->opcode_base;
+
+ lnd->cur.line += op % lnd->line_range + lnd->line_base;
+ lnd->cur.pc += (op / lnd->line_range
+ * lnd->minimum_instruction_length);
+ return true;
+ }
+ else switch (op)
+ {
+ case DW_LNS_extended_op:
+ {
+ uint64_t sz = read_uleb128 (lnd);
+ const uint8_t * op = lnd->cpos;
+
+ if ((uint64_t)(lnd->end - op) < sz || sz == 0)
+ return false;
+ lnd->cpos += sz;
+ switch (*op++)
+ {
+ case DW_LNE_end_sequence:
+ lnd->cur.end_of_sequence = true;
+ return true;
+
+ case DW_LNE_set_address:
+ if (sz == 9)
+ lnd->cur.pc = read_64 (op);
+ else if (sz == 5)
+ lnd->cur.pc = read_32 (op);
+ else
+ return false;
+ break;
+
+ case DW_LNE_define_file:
+ {
+ const uint8_t * * filenames;
+ filenames = realloc
+ (lnd->filenames,
+ (lnd->numfile + 1) * sizeof (const uint8_t *));
+ if (! filenames)
+ return false;
+ /* Check for zero-termination. */
+ if (! memchr (op, 0, lnd->cpos - op))
+ return false;
+ filenames[lnd->numfile++] = op;
+ lnd->filenames = filenames;
+
+ /* There's other data here, like file sizes and modification
+ times, but we don't need to read it so skip it. */
+ }
+ break;
+
+ default:
+ /* Don't understand it, so skip it. */
+ break;
+ }
+ break;
+ }
+
+ case DW_LNS_copy:
+ //fprintf(stderr, "DW_LNS_copy\n");
+ return true;
+ case DW_LNS_advance_pc:
+ //fprintf(stderr, "DW_LNS_advance_pc\n");
+ tmp = read_uleb128 (lnd);
+ if (tmp == (uint64_t) -1)
+ return false;
+ lnd->cur.pc += tmp * lnd->minimum_instruction_length;
+ break;
+ case DW_LNS_advance_line:
+ //fprintf(stderr, "DW_LNS_advance_line\n");
+ lnd->cur.line += read_sleb128 (lnd);
+ break;
+ case DW_LNS_set_file:
+ //fprintf(stderr, "DW_LNS_set_file\n");
+ lnd->cur.file = read_uleb128 (lnd);
+ break;
+ case DW_LNS_set_column:
+ //fprintf(stderr, "DW_LNS_set_column\n");
+ lnd->cur.col = read_uleb128 (lnd);
+ break;
+ case DW_LNS_const_add_pc:
+ //fprintf(stderr, "DW_LNS_const_add_pc\n");
+ lnd->cur.pc += ((255 - lnd->opcode_base) / lnd->line_range
+ * lnd->minimum_instruction_length);
+ break;
+ case DW_LNS_fixed_advance_pc:
+ //fprintf(stderr, "DW_LNS_fixed_advance_pc\n");
+ if (lnd->end - lnd->cpos < 2)
+ return false;
+ lnd->cur.pc += read_16 (lnd->cpos);
+ lnd->cpos += 2;
+ break;
+ default:
+ {
+ /* Don't know what it is, so skip it. */
+ int i;
+ for (i = 0; i < lnd->standard_opcode_lengths[op - 1]; i++)
+ skip_leb128 (lnd);
+ break;
+ }
+ }
+ }
+}
+
+
+/* Set RESULT to the next 'interesting' line state, as indicated
+ by STOP, or return FALSE on error. The final (end-of-sequence)
+ line state is always considered interesting. */
+int
+line_next (struct line_reader_data * lnd,
+ struct line_info * result,
+ enum line_stop_constants stop)
+{
+ for (;;)
+ {
+ struct line_info prev = lnd->cur;
+
+ if (! next_state (lnd))
+ return false;
+
+ if (lnd->cur.end_of_sequence)
+ break;
+ if (stop == line_stop_always)
+ break;
+ if ((stop & line_stop_pc) && lnd->cur.pc != prev.pc)
+ break;
+ if ((stop & line_stop_pos_mask) && lnd->cur.file != prev.file)
+ break;
+ if ((stop & line_stop_pos_mask) >= line_stop_line
+ && lnd->cur.line != prev.line)
+ break;
+ if ((stop & line_stop_pos_mask) >= line_stop_col
+ && lnd->cur.col != prev.col)
+ break;
+ }
+ *result = lnd->cur;
+ return true;
+}
+
+/* Find the region (START->pc through END->pc) in the debug_line
+ information which contains PC. This routine starts searching at
+ the current position (which is returned as END), and will go all
+ the way around the debug_line information. It will return false if
+ an error occurs or if there is no matching region; these may be
+ distinguished by looking at START->end_of_sequence, which will be
+ false on error and true if there was no matching region.
+ You could write this routine using line_next, but this version
+ will be slightly more efficient, and of course more convenient. */
+
+int
+line_find_addr (struct line_reader_data * lnd,
+ struct line_info * start,
+ struct line_info * end,
+ uint64_t pc)
+{
+ const uint8_t * startpos;
+ struct line_info prev;
+
+ if (lnd->cur.end_of_sequence && lnd->cpos == lnd->end)
+ line_reset (lnd);
+
+ startpos = lnd->cpos;
+
+ do {
+ prev = lnd->cur;
+ if (! next_state (lnd))
+ {
+ start->end_of_sequence = false;
+ return false;
+ }
+ if (lnd->cur.end_of_sequence && lnd->cpos == lnd->end)
+ line_reset (lnd);
+ if (lnd->cpos == startpos)
+ {
+ start->end_of_sequence = true;
+ return false;
+ }
+ } while (lnd->cur.pc <= pc || prev.pc > pc || prev.end_of_sequence);
+ *start = prev;
+ *end = lnd->cur;
+ return true;
+}
+#endif /* ! KLD */
+
--- /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@
+ */
+#include <stdint.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Information about a line.
+ DIRECTORY is to be ignored if FILENAME is absolute.
+ PC will be relative to the file the debug_line section is in. */
+struct line_info
+{
+ uint64_t file;
+ int64_t line;
+ uint64_t col;
+ uint64_t pc;
+ int end_of_sequence;
+};
+
+/* Opaque status structure for the line readers. */
+struct line_reader_data;
+
+/* Create a line_reader_data, given address and size of the debug_line section.
+ SIZE may be (size_t)-1 if unknown, although this suppresses checking
+ for an incorrectly large size in the debug_line section.
+ LITTLE_ENDIAN is set if the debug_line section is for a little-endian
+ machine.
+ Returns NULL on error. */
+struct line_reader_data * line_open (const uint8_t * debug_line,
+ size_t debug_line_size,
+ int little_endian);
+
+/* The STOP parameter to line_next is one of line_stop_{file,line,col},
+ perhaps ORed with line_stop_pc; or line_stop_atend, or line_stop_always. */
+enum line_stop_constants {
+ line_stop_atend = 0, /* Stop only at the end of a sequence. */
+ line_stop_file = 1, /* Stop if DIRECTORY or FILENAME change. */
+ line_stop_line = 2, /* Stop if LINE, DIRECTORY, or FILENAME change. */
+ line_stop_col = 3, /* Stop if COL, LINE, DIRECTORY, or FILENAME change. */
+ line_stop_pos_mask = 3,
+ line_stop_pc = 4, /* Stop if PC changes. */
+ line_stop_always = 8 /* Stop always. */
+};
+
+/* Either return FALSE on an error, in which case the line_reader_data
+ may be invalid and should be passed immediately to line_free; or
+ fill RESULT with the first 'interesting' line, as determined by STOP.
+ The last line data in a sequence is always considered 'interesting'. */
+int line_next (struct line_reader_data * lnd,
+ struct line_info * result,
+ enum line_stop_constants stop);
+
+/* Find the region (START->pc through END->pc) in the debug_line
+ information which contains PC. This routine starts searching at
+ the current position (which is returned as END), and will go all
+ the way around the debug_line information. It will return false if
+ an error occurs or if there is no matching region; these may be
+ distinguished by looking at START->end_of_sequence, which will be
+ false on error and true if there was no matching region.
+ You could write this routine using line_next, but this version
+ will be slightly more efficient, and of course more convenient. */
+
+int line_find_addr (struct line_reader_data * lnd,
+ struct line_info * start,
+ struct line_info * end,
+ uint64_t pc);
+
+/* Return TRUE if there is more line data to be fetched.
+ If line_next has not been called or it has been called but did not
+ set END_OF_SEQUENCE, you can assume there is more line data,
+ but it's safe to call this routine anyway. */
+int line_at_eof (struct line_reader_data * lnd);
+
+/* Return the pathname of the file in S, or NULL on error.
+ The result will have been allocated with malloc. */
+char * line_file (struct line_reader_data *lnd, uint64_t file);
+
+/* Reset the line_reader_data: go back to the beginning. */
+void line_reset (struct line_reader_data * lnd);
+
+/* Free a line_reader_data structure. */
+void line_free (struct line_reader_data * lnd);
+
+#ifdef __cplusplus
+}
+#endif
+
--- /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@
+ */
+/* These constants were taken from version 3 of the DWARF standard,
+ which is Copyright (c) 2005 Free Standards Group, and
+ Copyright (c) 1992, 1993 UNIX International, Inc. */
+
+#ifndef __DWARF2_H__
+#define __DWARF2_H__
+
+/* This is not a complete list. */
+enum {
+ DW_TAG_compile_unit = 17,
+ DW_TAG_partial_unit = 60
+};
+
+/* This is not a complete list. */
+enum {
+ DW_AT_sibling = 1,
+ DW_AT_name = 3,
+ DW_AT_stmt_list = 16,
+ DW_AT_comp_dir = 27
+};
+
+enum {
+ DW_FORM_addr = 1,
+ DW_FORM_block2 = 3,
+ DW_FORM_block4,
+ DW_FORM_data2,
+ DW_FORM_data4,
+ DW_FORM_data8,
+ DW_FORM_string,
+ DW_FORM_block,
+ DW_FORM_block1,
+ DW_FORM_data1,
+ DW_FORM_flag,
+ DW_FORM_sdata,
+ DW_FORM_strp,
+ DW_FORM_udata,
+ DW_FORM_ref_addr,
+ DW_FORM_ref1,
+ DW_FORM_ref2,
+ DW_FORM_ref4,
+ DW_FORM_ref8,
+ DW_FORM_ref_udata,
+ DW_FORM_indirect /* 22 */
+};
+
+enum {
+ DW_LNS_extended_op = 0,
+ DW_LNS_copy,
+ DW_LNS_advance_pc,
+ DW_LNS_advance_line,
+ DW_LNS_set_file,
+ DW_LNS_set_column,
+ DW_LNS_negate_stmt,
+ DW_LNS_set_basic_block,
+ DW_LNS_const_add_pc,
+ DW_LNS_fixed_advance_pc,
+ DW_LNS_set_prologue_end,
+ DW_LNS_set_epilogue_begin,
+ DW_LNS_set_isa
+};
+
+enum {
+ DW_LNE_end_sequence = 1,
+ DW_LNE_set_address,
+ DW_LNE_define_file
+};
+
+#endif
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-*
+ * Copyright (c) 2005-2009 Apple 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@
+ */
+
+// start temp HACK for cross builds
+extern "C" double log2 ( double );
+#define __MATH__
+// end temp HACK for cross builds
+
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#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>
+#include <mach/mach_init.h>
+#include <mach/mach_host.h>
+#include <dlfcn.h>
+
+#include <string>
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+#include <list>
+#include <algorithm>
+#include <ext/hash_map>
+#include <dlfcn.h>
+#include <AvailabilityMacros.h>
+
+#include "configure.h"
+#include "Options.h"
+
+#include "ObjectFile.h"
+
+#include "MachOReaderRelocatable.hpp"
+#include "ArchiveReader.hpp"
+#include "MachOReaderDylib.hpp"
+#include "MachOWriterExecutable.hpp"
+
+
+#if LTO_SUPPORT
+#include "LTOReader.hpp"
+#endif
+
+#include "OpaqueSection.hpp"
+
+
+class CStringComparor
+{
+public:
+ bool operator()(const char* left, const char* right) const { return (strcmp(left, right) < 0); }
+};
+
+class CStringEquals
+{
+public:
+ bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
+};
+
+class Section : public ObjectFile::Section
+{
+public:
+ static Section* find(const char* sectionName, const char* segmentName, bool zeroFill, bool createIfNeeded=true);
+ static void assignIndexes();
+ const char* getName() { return fSectionName; }
+private:
+ Section(const char* sectionName, const char* segmentName, bool zeroFill);
+
+ struct Sorter {
+ static int segmentOrdinal(const char* segName);
+ bool operator()(Section* left, Section* right);
+ };
+
+ typedef __gnu_cxx::hash_map<const char*, uint32_t, __gnu_cxx::hash<const char*>, CStringEquals> NameToOrdinal;
+ typedef __gnu_cxx::hash_map<const char*, class Section*, __gnu_cxx::hash<const char*>, CStringEquals> NameToSection;
+ //typedef std::map<const char*, class Section*, CStringComparor> NameToSection;
+
+ char fSectionName[18];
+ char fSegmentName[18];
+ bool fZeroFill;
+
+ static NameToSection fgMapping;
+ static std::vector<Section*> fgSections;
+ static NameToOrdinal fgSegmentDiscoverOrder;
+};
+
+Section::NameToSection Section::fgMapping;
+std::vector<Section*> Section::fgSections;
+Section::NameToOrdinal Section::fgSegmentDiscoverOrder;
+
+Section::Section(const char* sectionName, const char* segmentName, bool zeroFill)
+ : fZeroFill(zeroFill)
+{
+ strlcpy(fSectionName, sectionName, sizeof(fSectionName));
+ strlcpy(fSegmentName, segmentName, sizeof(fSegmentName));
+
+ this->fIndex = fgSections.size() + 20; // room for 20 standard sections
+ // special placement of some sections
+ if ( strcmp(segmentName, "__TEXT") == 0 ) {
+ // sort unwind info to end of segment
+ if ( strcmp(sectionName, "__eh_frame") == 0 )
+ this->fIndex = INT_MAX;
+ else if ( strcmp(sectionName, "__unwind_info") == 0 )
+ this->fIndex = INT_MAX-1;
+ else if ( strcmp(sectionName, "__gcc_except_tab") == 0 )
+ this->fIndex = INT_MAX-2;
+ }
+ else if ( strcmp(segmentName, "__DATA") == 0 ) {
+ // sort sections dyld will touch to start of segment
+ if ( strcmp(sectionName, "__dyld") == 0 )
+ this->fIndex = 1;
+ else if ( strcmp(sectionName, "__program_vars") == 0 )
+ this->fIndex = 1;
+ else if ( strcmp(sectionName, "__mod_init_func") == 0 )
+ this->fIndex = 2;
+ else if ( strcmp(sectionName, "__nl_symbol_ptr") == 0 )
+ this->fIndex = 3;
+ else if ( strcmp(sectionName, "__la_symbol_ptr") == 0 )
+ this->fIndex = 4;
+ else if ( strcmp(sectionName, "__const") == 0 )
+ this->fIndex = 5;
+ else if ( strcmp(sectionName, "__cfstring") == 0 )
+ this->fIndex = 6;
+ else if ( strcmp(sectionName, "__gcc_except_tab") == 0 )
+ this->fIndex = 7;
+ else if ( strcmp(sectionName, "__objc_data") == 0 )
+ this->fIndex = 8;
+ else if ( strcmp(sectionName, "__objc_msgrefs") == 0 )
+ this->fIndex = 9;
+ else if ( strcmp(sectionName, "__objc_protorefs") == 0 )
+ this->fIndex = 10;
+ else if ( strcmp(sectionName, "__objc_selrefs") == 0 )
+ this->fIndex = 11;
+ else if ( strcmp(sectionName, "__objc_classrefs") == 0 )
+ this->fIndex = 12;
+ else if ( strcmp(sectionName, "__objc_superrefs") == 0 )
+ this->fIndex = 13;
+ else if ( strcmp(sectionName, "__objc_const") == 0 )
+ this->fIndex = 14;
+ else if ( strcmp(sectionName, "__objc_classlist") == 0 )
+ this->fIndex = 15;
+ else if ( strcmp(sectionName, "__objc_nlclslist") == 0 )
+ this->fIndex = 16;
+ else if ( strcmp(sectionName, "__objc_catlist") == 0 )
+ this->fIndex = 17;
+ else if ( strcmp(sectionName, "__objc_protolist") == 0 )
+ this->fIndex = 18;
+ else if ( strcmp(sectionName, "__objc_imageinfo") == 0 )
+ this->fIndex = 19;
+
+ }
+
+ //fprintf(stderr, "new Section(%s, %s) => %p, %u\n", sectionName, segmentName, this, this->getIndex());
+}
+
+Section* Section::find(const char* sectionName, const char* segmentName, bool zeroFill, bool createIfNeeded)
+{
+ NameToSection::iterator pos = fgMapping.find(sectionName);
+ if ( pos != fgMapping.end() ) {
+ if ( strcmp(pos->second->fSegmentName, segmentName) == 0 )
+ return pos->second;
+ // otherwise same section name is used in different segments, look slow way
+ for (std::vector<Section*>::iterator it=fgSections.begin(); it != fgSections.end(); it++) {
+ if ( (strcmp((*it)->fSectionName, sectionName) == 0) && (strcmp((*it)->fSegmentName, segmentName) == 0) )
+ return *it;
+ }
+ }
+
+ if ( !createIfNeeded )
+ return NULL;
+
+ // does not exist, so make a new one
+ Section* sect = new Section(sectionName, segmentName, zeroFill);
+ fgMapping[sectionName] = sect;
+ fgSections.push_back(sect);
+
+ if ( (strcmp(sectionName, "__text") == 0) && (strcmp(segmentName, "__TEXT") == 0) ) {
+ // special case __StaticInit to be right after __text
+ find("__StaticInit", "__TEXT", false);
+ }
+
+ // remember segment discovery order
+ if ( fgSegmentDiscoverOrder.find(segmentName) == fgSegmentDiscoverOrder.end() )
+ fgSegmentDiscoverOrder[segmentName] = fgSegmentDiscoverOrder.size();
+
+ return sect;
+}
+
+int Section::Sorter::segmentOrdinal(const char* segName)
+{
+ if ( strcmp(segName, "__HEADER") == 0 )
+ return 1;
+ if ( strcmp(segName, "__PAGEZERO") == 0 )
+ return 1;
+ if ( strcmp(segName, "__TEXT") == 0 )
+ return 2;
+ if ( strcmp(segName, "__DATA") == 0 )
+ return 3;
+ if ( strcmp(segName, "__OBJC") == 0 )
+ return 4;
+ if ( strcmp(segName, "__OBJC2") == 0 )
+ return 5;
+ if ( strcmp(segName, "__LINKEDIT") == 0 )
+ return INT_MAX; // linkedit segment should always sort last
+ else
+ return fgSegmentDiscoverOrder[segName]+6;
+}
+
+
+bool Section::Sorter::operator()(Section* left, Section* right)
+{
+ // Segment is primary sort key
+ int leftSegOrdinal = segmentOrdinal(left->fSegmentName);
+ int rightSegOrdinal = segmentOrdinal(right->fSegmentName);
+ if ( leftSegOrdinal < rightSegOrdinal )
+ return true;
+ if ( leftSegOrdinal > rightSegOrdinal )
+ return false;
+
+ // zerofill section sort to the end
+ if ( !left->fZeroFill && right->fZeroFill )
+ return true;
+ if ( left->fZeroFill && !right->fZeroFill )
+ return false;
+
+ // section discovery order is last sort key
+ return left->fIndex < right->fIndex;
+}
+
+void Section::assignIndexes()
+{
+ //printf("unsorted sections:\n");
+ //for (std::vector<Section*>::iterator it=fgSections.begin(); it != fgSections.end(); it++) {
+ // printf("section: name=%s, segment: name=%s, discovery order=%d\n", (*it)->fSectionName, (*it)->fSegmentName, (*it)->fIndex);
+ //}
+
+ // sort it
+ std::sort(fgSections.begin(), fgSections.end(), Section::Sorter());
+
+ // assign correct section ordering to each Section object
+ unsigned int newOrder = 1;
+ for (std::vector<Section*>::iterator it=fgSections.begin(); it != fgSections.end(); it++)
+ (*it)->fIndex = newOrder++;
+
+ //printf("sorted sections:\n");
+ //for (std::vector<Section*>::iterator it=fgSections.begin(); it != fgSections.end(); it++) {
+ // printf("section: index=%d, obj=%p, name=%s\n", (*it)->fIndex, (*it), (*it)->fSectionName);
+ //}
+}
+
+class Linker : public ObjectFile::Reader::DylibHander {
+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, const Options::FileInfo& );
+ void setOutputFile(ExecutableFile::Writer* writer);
+ void link();
+ void optimize();
+
+ // implemenation from ObjectFile::Reader::DylibHander
+ virtual ObjectFile::Reader* findDylib(const char* installPath, const char* fromPath);
+
+private:
+ struct WhyLiveBackChain
+ {
+ WhyLiveBackChain* previous;
+ ObjectFile::Atom* referer;
+ };
+
+ ObjectFile::Reader* createReader(const Options::FileInfo&);
+ void addAtom(ObjectFile::Atom& atom);
+ void addAtoms(std::vector<class ObjectFile::Atom*>& atoms);
+ void buildAtomList();
+ void adjustScope();
+ void processDylibs();
+ void markDead(ObjectFile::Atom* atom);
+ void updateConstraints(ObjectFile::Reader* reader);
+ void loadAndResolve();
+ void processDTrace();
+ void checkObjC();
+ void loadUndefines();
+ void checkUndefines();
+ void resolveReferences();
+ void deadStripResolve();
+ void addLiveRoot(const char* name);
+ void moveToFrontOfSection(ObjectFile::Atom* atom);
+ ObjectFile::Atom* findAtom(const Options::OrderedSymbol& pair);
+ void logArchive(ObjectFile::Reader* reader);
+ void sortSections();
+ void sortAtoms();
+ void tweakLayout();
+ void writeDotOutput();
+ static bool minimizeStab(ObjectFile::Reader::Stab& stab);
+ static const char* truncateStabString(const char* str);
+ void collectDebugInfo();
+ void writeOutput();
+ ObjectFile::Atom* entryPoint(bool orInit);
+ ObjectFile::Atom* dyldClassicHelper();
+ ObjectFile::Atom* dyldCompressedHelper();
+ ObjectFile::Atom* dyldLazyLibraryHelper();
+ const char* assureFullPath(const char* path);
+ void markLive(ObjectFile::Atom& atom, Linker::WhyLiveBackChain* previous);
+ void collectStabs(ObjectFile::Reader* reader, std::map<const class ObjectFile::Atom*, uint32_t>& atomOrdinals);
+ void synthesizeDebugNotes(std::vector<class ObjectFile::Atom*>& allAtomsByReader);
+ void printStatistics();
+ void printTime(const char* msg, uint64_t partTime, uint64_t totalTime);
+ char* commatize(uint64_t in, char* out);
+ void getVMInfo(vm_statistics_data_t& info);
+ cpu_type_t inferArchitecture();
+ void checkDylibClientRestrictions(ObjectFile::Reader* reader);
+ void logDylib(ObjectFile::Reader* reader, bool indirect);
+
+ void resolve(ObjectFile::Reference* reference);
+ void resolveFrom(ObjectFile::Reference* reference);
+ std::vector<class ObjectFile::Atom*>* addJustInTimeAtoms(const char* name, bool searchDylibs, bool searchArchives, bool okToMakeProxy);
+ void addJustInTimeAtomsAndMarkLive(const char* name);
+
+ ObjectFile::Reader* addDylib(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen);
+ ObjectFile::Reader* addObject(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen);
+ ObjectFile::Reader* addArchive(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen);
+
+ void logTraceInfo(const char* format, ...);
+
+
+ class SymbolTable
+ {
+ public:
+ typedef __gnu_cxx::hash_map<const char*, ObjectFile::Atom*, __gnu_cxx::hash<const char*>, CStringEquals> Mapper;
+
+ SymbolTable(Linker&);
+ void require(const char* name);
+ bool add(ObjectFile::Atom& atom);
+ ObjectFile::Atom* find(const char* name);
+ unsigned int getRequireCount() { return fRequireCount; }
+ void getUndefinesNames(std::vector<const char*>& undefines);
+ void getTentativesNames(std::vector<const char*>& tents);
+ bool hasExternalTentativeDefinitions() { return fHasExternalTentativeDefinitions; }
+ bool hasExternalWeakDefinitions() { return fHasExternalWeakDefinitions; }
+ void setHasExternalWeakDefinitions(bool value) { fHasExternalWeakDefinitions = value; }
+ Mapper::iterator begin() { return fTable.begin(); }
+ Mapper::iterator end() { return fTable.end(); }
+
+ private:
+ Linker& fOwner;
+ Mapper fTable;
+ unsigned int fRequireCount;
+ bool fHasExternalTentativeDefinitions;
+ bool fHasExternalWeakDefinitions;
+ };
+
+ class AtomSorter
+ {
+ public:
+ AtomSorter(std::map<const ObjectFile::Atom*, uint32_t>* map, std::set<const ObjectFile::Atom*>& inits,
+ std::set<const ObjectFile::Atom*>& terms) :
+ fOverriddenOrdinalMap(map), fInitializerSet(inits), fTerminatorSet(terms) {}
+ bool operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right);
+ private:
+ std::map<const ObjectFile::Atom*, uint32_t>* fOverriddenOrdinalMap;
+ std::set<const ObjectFile::Atom*>& fInitializerSet;
+ std::set<const ObjectFile::Atom*>& fTerminatorSet;
+ };
+
+ typedef std::map<const char*, uint32_t, CStringComparor> SectionOrder;
+
+ struct DTraceProbeInfo {
+ DTraceProbeInfo(const ObjectFile::Atom* a, uint32_t o, const char* n) : atom(a), offset(o), probeName(n) {}
+ const ObjectFile::Atom* atom;
+ uint32_t offset;
+ const char* probeName;
+ };
+ typedef __gnu_cxx::hash_map<const char*, std::vector<DTraceProbeInfo>, __gnu_cxx::hash<const char*>, CStringEquals> ProviderToProbes;
+ typedef __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> CStringSet;
+ typedef __gnu_cxx::hash_map<const char*, ObjectFile::Reader*, __gnu_cxx::hash<const char*>, CStringEquals> InstallNameToReader;
+
+ struct IndirectLibrary {
+ const char* path;
+ uint64_t fileLen;
+ ObjectFile::Reader* reader;
+ std::set<ObjectFile::Reader*> parents;
+ ObjectFile::Reader* reExportedViaDirectLibrary;
+ };
+
+ ObjectFile::Reader* findDirectLibraryWhichReExports(struct IndirectLibrary& indirectLib);
+
+ Options fOptions;
+ SymbolTable fGlobalSymbolTable;
+ uint32_t fNextInputOrdinal;
+ std::vector<class ObjectFile::Reader*> fInputFiles;
+ ExecutableFile::Writer* fOutputFile;
+ InstallNameToReader fDylibMap;
+ std::map<ObjectFile::Reader*,LibraryOptions> fDylibOptionsMap;
+ std::set<ObjectFile::Reader*> fDylibsProcessed;
+ ObjectFile::Reader* fBundleLoaderReader;
+ std::vector<class ObjectFile::Reader*> fReadersThatHaveSuppliedAtoms;
+ std::vector<class ObjectFile::Atom*> fAllAtoms;
+ std::set<class ObjectFile::Reader*> fArchiveReaders;
+ std::set<class ObjectFile::Reader*> fArchiveReadersLogged;
+ std::set<class ObjectFile::Atom*> fDeadAtoms;
+ std::set<ObjectFile::Atom*> fLiveAtoms;
+ std::set<ObjectFile::Atom*> fLiveRootAtoms;
+ std::set<const ObjectFile::Atom*> fInitializerAtoms;
+ std::set<const ObjectFile::Atom*> fTerminatorAtoms;
+ std::set<const ObjectFile::Atom*> fRegularDefAtomsThatOverrideADylibsWeakDef;
+ std::vector<class ObjectFile::Reader::Stab> fStabs;
+ std::vector<class ObjectFile::Atom*> fAtomsWithUnresolvedReferences;
+ std::set<class ObjectFile::Atom*> fAtomsOverriddenByLateLoads;
+ bool fInitialLoadsDone;
+ bool fCreateUUID;
+ bool fCanScatter;
+ SectionOrder fSectionOrder;
+ cpu_type_t fArchitecture;
+ const char* fArchitectureName;
+ bool fArchitectureInferred;
+ bool fDirectLibrariesComplete;
+ bool fBiggerThanTwoGigOutput;
+ uint64_t fOutputFileSize;
+ uint64_t fTotalZeroFillSize;
+ uint64_t fTotalSize;
+ uint64_t fStartTime;
+ uint64_t fStartCreateReadersTime;
+ uint64_t fStartCreateWriterTime;
+ uint64_t fStartBuildAtomsTime;
+ uint64_t fStartLoadAndResolveTime;
+ uint64_t fStartSortTime;
+ uint64_t fStartDebugTime;
+ uint64_t fStartWriteTime;
+ uint64_t fEndTime;
+ uint64_t fTotalObjectSize;
+ uint64_t fTotalArchiveSize;
+ uint32_t fTotalObjectLoaded;
+ uint32_t fTotalArchivesLoaded;
+ uint32_t fTotalDylibsLoaded;
+ vm_statistics_data_t fStartVMInfo;
+ ObjectFile::Reader::ObjcConstraint fCurrentObjCConstraint;
+ ObjectFile::Reader::CpuConstraint fCurrentCpuConstraint;
+ bool fObjcReplacmentClasses;
+ bool fAllDirectDylibsLoaded;
+};
+
+
+Linker::Linker(int argc, const char* argv[])
+ : fOptions(argc, argv), fGlobalSymbolTable(*this), fNextInputOrdinal(1), fOutputFile(NULL), fBundleLoaderReader(NULL),
+ fInitialLoadsDone(false), fCreateUUID(fOptions.outputKind() != Options::kObjectFile), fCanScatter(true),
+ fArchitecture(0), fArchitectureInferred(false), fDirectLibrariesComplete(false), fBiggerThanTwoGigOutput(false),
+ fOutputFileSize(0), fTotalZeroFillSize(0), fTotalSize(0), fTotalObjectSize(0),
+ fTotalArchiveSize(0), fTotalObjectLoaded(0), fTotalArchivesLoaded(0), fTotalDylibsLoaded(0),
+ fCurrentObjCConstraint(ObjectFile::Reader::kObjcNone), fCurrentCpuConstraint(ObjectFile::Reader::kCpuAny),
+ fObjcReplacmentClasses(false), fAllDirectDylibsLoaded(false)
+{
+ 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;
+ case CPU_TYPE_ARM:
+ fArchitectureName = "arm";
+ 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.
+ // the first one found is presumably the architecture to link
+ uint8_t buffer[sizeof(mach_header_64)];
+ std::vector<Options::FileInfo>& files = fOptions.getInputFiles();
+ for (std::vector<Options::FileInfo>::iterator it = files.begin(); it != files.end(); ++it) {
+ int fd = ::open(it->path, O_RDONLY, 0);
+ if ( fd != -1 ) {
+ ssize_t amount = read(fd, buffer, sizeof(buffer));
+ ::close(fd);
+ if ( amount >= (ssize_t)sizeof(buffer) ) {
+ if ( mach_o::relocatable::Reader<ppc>::validFile(buffer) ) {
+ //warning("-arch not used, infering -arch ppc based on %s", it->path);
+ return CPU_TYPE_POWERPC;
+ }
+ else if ( mach_o::relocatable::Reader<ppc64>::validFile(buffer) ) {
+ //warning("-arch not used, infering -arch ppc64 based on %s", it->path);
+ return CPU_TYPE_POWERPC64;
+ }
+ else if ( mach_o::relocatable::Reader<x86>::validFile(buffer) ) {
+ //warning("-arch not used, infering -arch i386 based on %s", it->path);
+ return CPU_TYPE_I386;
+ }
+ else if ( mach_o::relocatable::Reader<x86_64>::validFile(buffer) ) {
+ //warning("-arch not used, infering -arch x86_64 based on %s", it->path);
+ return CPU_TYPE_X86_64;
+ }
+ else if ( mach_o::relocatable::Reader<arm>::validFile(buffer) ) {
+ //warning("-arch not used, infering -arch arm based on %s", it->path);
+ return CPU_TYPE_ARM;
+ }
+ }
+ }
+ }
+
+ // no thin .o files found, so default to same architecture this was built as
+ warning("-arch not specified");
+#if __ppc__
+ return CPU_TYPE_POWERPC;
+#elif __i386__
+ return CPU_TYPE_I386;
+#elif __ppc64__
+ return CPU_TYPE_POWERPC64;
+#elif __x86_64__
+ return CPU_TYPE_X86_64;
+#elif __arm__
+ return CPU_TYPE_ARM;
+#else
+ #error unknown default architecture
+#endif
+}
+
+
+void Linker::addInputFile(ObjectFile::Reader* reader, const Options::FileInfo& info)
+{
+ fInputFiles.push_back(reader);
+ fDylibOptionsMap[reader] = info.options;
+}
+
+void Linker::setOutputFile(ExecutableFile::Writer* writer)
+{
+ 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()
+{
+ fStartLoadAndResolveTime = mach_absolute_time();
+ 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::optimize()
+{
+ // give each reader a chance to do any optimizations
+ bool didSomething = false;
+ std::vector<class ObjectFile::Atom*> newAtoms;
+ std::vector<const char *> additionalUndefines;
+ std::vector<class ObjectFile::Atom*> newlyDeadAtoms;
+ for (std::vector<class ObjectFile::Reader*>::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) {
+ didSomething |= (*it)->optimize(fAllAtoms, newAtoms, additionalUndefines, fDeadAtoms, newlyDeadAtoms, fNextInputOrdinal,
+ fOutputFile, entryPoint(true), fOptions.llvmOptions(),
+ fOptions.allGlobalsAreDeadStripRoots(), (int)fOptions.outputKind(), fOptions.verbose(),
+ fOptions.saveTempFiles(), fOptions.getOutputFilePath(), fOptions.positionIndependentExecutable(),
+ fOptions.allowTextRelocs());
+ }
+
+ // only do next steps if some optimization was actually done
+ if ( didSomething ) {
+ // add all newly created atoms to fAllAtoms and update symbol table
+ this->addAtoms(newAtoms);
+
+ // add dead atoms to dead list and remove from fAllAtoms
+ for(std::vector<class ObjectFile::Atom*>::iterator itr = newlyDeadAtoms.begin(); itr != newlyDeadAtoms.end(); ++itr)
+ markDead(*itr);
+ fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), InSet(fDeadAtoms)), fAllAtoms.end());
+
+ // Make sure all atoms have a section. Atoms that were not originally in a mach-o file could
+ // not have their section set until now.
+ for(std::vector<class ObjectFile::Atom*>::iterator itr = fAllAtoms.begin(); itr != fAllAtoms.end(); ++itr) {
+ ObjectFile::Atom *atom = *itr;
+ if ( atom->getSection() == NULL )
+ atom->setSection(Section::find(atom->getSectionName(), atom->getSegment().getName(), atom->isZeroFill()));
+ }
+
+ // resolve new undefines
+ for(std::vector<const char*>::iterator riter = additionalUndefines.begin(); riter != additionalUndefines.end(); ++riter) {
+ const char *targetName = *riter;
+ //fprintf(stderr, "LTO additional undefine: %s\n", targetName);
+ ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName);
+ if ( target == NULL) {
+ // mark that this symbol is needed
+ fGlobalSymbolTable.require(targetName);
+ // try to find it in some library
+ this->addJustInTimeAtoms(targetName, true, true, true);
+ }
+ }
+
+ if ( fOptions.deadStrip() != Options::kDeadStripOff ) {
+ // LTO may optimize away some atoms, so dead stripping must be redone
+ fLiveAtoms.clear();
+ this->deadStripResolve();
+ }
+ else {
+ // LTO may require new library symbols to be loaded, so redo
+ this->checkUndefines();
+ this->resolveReferences();
+ }
+ }
+}
+
+
+void Linker::adjustScope()
+{
+ // if -exported_symbols_list is used, demoted to hidden, symbols that are not in it
+ if ( fOptions.hasExportRestrictList() ) {
+ // The use of an -export file means the previous computation of fHasExternalWeakDefinitions could change
+ fGlobalSymbolTable.setHasExternalWeakDefinitions(false);
+ for(std::vector<class ObjectFile::Atom*>::iterator itr = fAllAtoms.begin(); itr != fAllAtoms.end(); ++itr) {
+ ObjectFile::Atom *atom = *itr;
+ ObjectFile::Atom::Scope scope = atom->getScope();
+ const char* name = atom->getName();
+ if ( name != NULL ) {
+ if ( scope == ObjectFile::Atom::scopeGlobal ) {
+ // check for globals that are downgraded to hidden
+ if ( !fOptions.shouldExport(name) ) {
+ atom->setScope(ObjectFile::Atom::scopeLinkageUnit);
+ //fprintf(stderr, "demote %s to hidden\n", name);
+ }
+ else if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) {
+ // we do have an exported weak symbol, turn WEAK_DEFINES back on
+ fGlobalSymbolTable.setHasExternalWeakDefinitions(true);
+ }
+ }
+ else if ( scope == ObjectFile::Atom::scopeLinkageUnit ) {
+ // check for hiddens that were requested to be exported
+ if ( fOptions.hasExportMaskList() && fOptions.shouldExport(name) ) {
+ warning("cannot export hidden symbol %s from %s", name, atom->getFile()->getPath());
+ }
+ }
+ }
+ }
+ }
+
+ // linking is done, so demote hidden symbols to static
+ if ( (fOptions.outputKind() == Options::kObjectFile) && fOptions.keepPrivateExterns() ) {
+ // ld -r -keep_private_externs does not move hidden symbols to static
+ }
+ else {
+ for(std::vector<class ObjectFile::Atom*>::iterator itr = fAllAtoms.begin(); itr != fAllAtoms.end(); ++itr) {
+ ObjectFile::Atom *atom = *itr;
+ // <rdar://problem/4637139> hidden common symbols cannot be demoted to static
+ if ( (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit) && (atom->getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition) ) {
+ atom->setScope(ObjectFile::Atom::scopeTranslationUnit);
+ //fprintf(stderr, "demote %s to static\n", atom->getDisplayName());
+ }
+ }
+ }
+}
+
+void Linker::link()
+{
+ this->buildAtomList();
+ this->loadAndResolve();
+ this->optimize();
+ this->adjustScope();
+ this->checkObjC();
+ this->processDTrace();
+ this->tweakLayout();
+ this->sortSections();
+ this->sortAtoms();
+ this->writeDotOutput();
+ this->collectDebugInfo();
+ this->writeOutput();
+ this->printStatistics();
+
+ if ( fOptions.pauseAtEnd() )
+ sleep(10);
+}
+
+void Linker::printTime(const char* msg, uint64_t partTime, uint64_t totalTime)
+{
+ static uint64_t sUnitsPerSecond = 0;
+ if ( sUnitsPerSecond == 0 ) {
+ struct mach_timebase_info timeBaseInfo;
+ if ( mach_timebase_info(&timeBaseInfo) == KERN_SUCCESS ) {
+ sUnitsPerSecond = 1000000000ULL * timeBaseInfo.denom / timeBaseInfo.numer;
+ //fprintf(stderr, "sUnitsPerSecond=%llu\n", sUnitsPerSecond);
+ }
+ }
+ if ( partTime < sUnitsPerSecond ) {
+ uint32_t milliSecondsTimeTen = (partTime*10000)/sUnitsPerSecond;
+ uint32_t milliSeconds = milliSecondsTimeTen/10;
+ uint32_t percentTimesTen = (partTime*1000)/totalTime;
+ uint32_t percent = percentTimesTen/10;
+ fprintf(stderr, "%s: %u.%u milliseconds (%u.%u%%)\n", msg, milliSeconds, milliSecondsTimeTen-milliSeconds*10, percent, percentTimesTen-percent*10);
+ }
+ else {
+ uint32_t secondsTimeTen = (partTime*10)/sUnitsPerSecond;
+ uint32_t seconds = secondsTimeTen/10;
+ uint32_t percentTimesTen = (partTime*1000)/totalTime;
+ uint32_t percent = percentTimesTen/10;
+ fprintf(stderr, "%s: %u.%u seconds (%u.%u%%)\n", msg, seconds, secondsTimeTen-seconds*10, percent, percentTimesTen-percent*10);
+ }
+}
+
+char* Linker::commatize(uint64_t in, char* out)
+{
+ char* result = out;
+ char rawNum[30];
+ sprintf(rawNum, "%llu", in);
+ const int rawNumLen = strlen(rawNum);
+ for(int i=0; i < rawNumLen-1; ++i) {
+ *out++ = rawNum[i];
+ if ( ((rawNumLen-i) % 3) == 1 )
+ *out++ = ',';
+ }
+ *out++ = rawNum[rawNumLen-1];
+ *out = '\0';
+ return result;
+}
+
+void Linker::getVMInfo(vm_statistics_data_t& info)
+{
+ mach_msg_type_number_t count = sizeof(vm_statistics_data_t) / sizeof(natural_t);
+ kern_return_t error = host_statistics(mach_host_self(), HOST_VM_INFO,
+ (host_info_t)&info, &count);
+ if (error != KERN_SUCCESS) {
+ bzero(&info, sizeof(vm_statistics_data_t));
+ }
+}
+
+void Linker::printStatistics()
+{
+ fEndTime = mach_absolute_time();
+ if ( fOptions.printStatistics() ) {
+ vm_statistics_data_t endVMInfo;
+ getVMInfo(endVMInfo);
+
+ uint64_t totalTime = fEndTime - fStartTime;
+ printTime("ld total time", totalTime, totalTime);
+ printTime(" option parsing time", fStartCreateReadersTime - fStartTime, totalTime);
+ printTime(" object file processing",fStartCreateWriterTime - fStartCreateReadersTime, totalTime);
+ printTime(" output file setup", fStartBuildAtomsTime - fStartCreateWriterTime, totalTime);
+ printTime(" build atom list", fStartLoadAndResolveTime - fStartBuildAtomsTime, totalTime);
+ printTime(" resolve references", fStartSortTime - fStartLoadAndResolveTime, totalTime);
+ printTime(" sort output", fStartDebugTime - fStartSortTime, totalTime);
+ printTime(" process debug info", fStartWriteTime - fStartDebugTime, totalTime);
+ printTime(" write output", fEndTime - fStartWriteTime, totalTime);
+ fprintf(stderr, "pageins=%u, pageouts=%u, faults=%u\n", endVMInfo.pageins-fStartVMInfo.pageins,
+ endVMInfo.pageouts-fStartVMInfo.pageouts, endVMInfo.faults-fStartVMInfo.faults);
+ char temp[40];
+ fprintf(stderr, "processed %3u object files, totaling %15s bytes\n", fTotalObjectLoaded, commatize(fTotalObjectSize, temp));
+ fprintf(stderr, "processed %3u archive files, totaling %15s bytes\n", fTotalArchivesLoaded, commatize(fTotalArchiveSize, temp));
+ fprintf(stderr, "processed %3u dylib files\n", fTotalDylibsLoaded);
+ fprintf(stderr, "wrote output file totaling %15s bytes\n", commatize(fOutputFileSize, temp));
+ }
+}
+
+inline void Linker::addAtom(ObjectFile::Atom& atom)
+{
+ // add to list of all atoms
+ fAllAtoms.push_back(&atom);
+
+ 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->getTargetBinding() == ObjectFile::Reference::kUnboundByName )
+ fGlobalSymbolTable.require(reference->getTargetName());
+ if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName )
+ fGlobalSymbolTable.require(reference->getFromTargetName());
+ }
+ // update total size info (except for __ZEROPAGE atom)
+ if ( atom.getSegment().isContentReadable() ) {
+ fTotalSize += atom.getSize();
+ if ( atom.isZeroFill() )
+ fTotalZeroFillSize += atom.getSize();
+ }
+ }
+ else {
+ if ( atom.dontDeadStrip() )
+ fLiveRootAtoms.insert(&atom);
+ }
+
+ // if in global namespace, add atom itself to symbol table
+ ObjectFile::Atom::Scope scope = atom.getScope();
+ const char* name = atom.getName();
+ if ( (scope != ObjectFile::Atom::scopeTranslationUnit) && (name != NULL) ) {
+ // add to symbol table
+ fGlobalSymbolTable.add(atom);
+ }
+
+ // record section orders so output file can have same order
+ if (atom.getSectionName())
+ atom.setSection(Section::find(atom.getSectionName(), atom.getSegment().getName(), atom.isZeroFill()));
+}
+
+
+void Linker::markDead(ObjectFile::Atom* atom)
+{
+ //fprintf(stderr, "markDead(%p) %s from %s\n", atom, atom->getDisplayName(), atom->getFile()->getPath());
+ fDeadAtoms.insert(atom);
+
+ // <rdar://problem/6578360> -dead_strip inhibits weak coalescing in no_dead_strip section
+ if ( fLiveRootAtoms.count(atom) != 0 ) {
+ fLiveRootAtoms.erase(atom);
+ }
+
+ //
+ // The kGroupSubordinate reference kind is used to model group comdat.
+ // The "signature" atom in the group has a kGroupSubordinate reference to
+ // all other members of the group. So, if the signature atom is
+ // coalesced away, all other atoms in the group should also be removed.
+ //
+ std::vector<class ObjectFile::Reference*>& references = atom->getReferences();
+ for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) {
+ ObjectFile::Reference* ref = *rit;
+ if ( ref->getKind() == 2 /*kGroupSubordinate*/ ) { // FIX FIX
+ ObjectFile::Atom* targetAtom = &(ref->getTarget());
+ //fprintf(stderr, " markDead(%p) subordinate %s\n", targetAtom, targetAtom->getDisplayName());
+ if ( targetAtom == NULL ) {
+ warning("%s has a group reference to %s but is not bound", atom->getDisplayName(), ref->getTargetName());
+ }
+ else {
+ if ( targetAtom->getScope() != ObjectFile::Atom::scopeTranslationUnit ) {
+ // ok for .eh symbols to be not static in -r mode
+ if ( (fOptions.outputKind() != Options::kObjectFile) || (strcmp(targetAtom->getSectionName(), "__eh_frame") != 0) )
+ warning("%s is in a comdat group but its scope is not static", targetAtom->getDisplayName());
+ }
+ this->markDead(targetAtom);
+ }
+ }
+ }
+}
+
+void Linker::updateConstraints(ObjectFile::Reader* reader)
+{
+ // check objc objects were compiled compatibly
+ ObjectFile::Reader::ObjcConstraint objcAddition = reader->getObjCConstraint();
+ if ( reader->getInstallPath() == NULL ) {
+ // adding a .o file
+ switch ( objcAddition ) {
+ case ObjectFile::Reader::kObjcNone:
+ break;
+ case ObjectFile::Reader::kObjcRetainRelease:
+ if ( fCurrentObjCConstraint == ObjectFile::Reader::kObjcGC )
+ throwf("%s built with incompatible Garbage Collection settings to link with previous .o files", reader->getPath());
+ fCurrentObjCConstraint = ObjectFile::Reader::kObjcRetainRelease;
+ break;
+ case ObjectFile::Reader::kObjcRetainReleaseOrGC:
+ if ( fCurrentObjCConstraint == ObjectFile::Reader::kObjcNone )
+ fCurrentObjCConstraint = ObjectFile::Reader::kObjcRetainReleaseOrGC;
+ break;
+ case ObjectFile::Reader::kObjcGC:
+ if ( fCurrentObjCConstraint == ObjectFile::Reader::kObjcRetainRelease )
+ throwf("%s built with incompatible Garbage Collection settings to link with previous .o files", reader->getPath());
+ fCurrentObjCConstraint = ObjectFile::Reader::kObjcGC;
+ break;
+ }
+ }
+ if ( reader->objcReplacementClasses() )
+ fObjcReplacmentClasses = true;
+
+ // check cpu sub-types for stricter sub-type
+ fCurrentCpuConstraint = (ObjectFile::Reader::CpuConstraint)reader->updateCpuConstraint(fCurrentCpuConstraint);
+}
+
+inline void Linker::addAtoms(std::vector<class ObjectFile::Atom*>& atoms)
+{
+ bool scanAll = fOptions.readerOptions().fFullyLoadArchives || fOptions.readerOptions().fLoadAllObjcObjectsFromArchives;
+ bool first = true;
+ for (std::vector<ObjectFile::Atom*>::iterator it=atoms.begin(); it != atoms.end(); it++) {
+ // usually we only need to get the first atom's reader, but
+ // with -all_load all atoms from all .o files come come back together
+ // so we need to scan all atoms
+ if ( first || scanAll ) {
+ // update fReadersThatHaveSuppliedAtoms
+ ObjectFile::Reader* reader = (*it)->getFile();
+ if ( std::find(fReadersThatHaveSuppliedAtoms.begin(), fReadersThatHaveSuppliedAtoms.end(), reader)
+ == fReadersThatHaveSuppliedAtoms.end() ) {
+ fReadersThatHaveSuppliedAtoms.push_back(reader);
+ updateConstraints(reader);
+ }
+ }
+ this->addAtom(**it);
+ first = false;
+ }
+}
+
+void Linker::logArchive(ObjectFile::Reader* reader)
+{
+ if ( (fArchiveReaders.count(reader) != 0) && (fArchiveReadersLogged.count(reader) == 0) ) {
+ fArchiveReadersLogged.insert(reader);
+ const char* fullPath = reader->getPath();
+ char realName[MAXPATHLEN];
+ if ( realpath(fullPath, realName) != NULL )
+ fullPath = realName;
+ logTraceInfo("[Logging for XBS] Used static archive: %s\n", fullPath);
+ }
+}
+
+
+void Linker::buildAtomList()
+{
+ fStartBuildAtomsTime = mach_absolute_time();
+ // add initial undefines from -u option
+ std::vector<const char*>& initialUndefines = fOptions.initialUndefines();
+ for (std::vector<const char*>::iterator it=initialUndefines.begin(); it != initialUndefines.end(); it++) {
+ fGlobalSymbolTable.require(*it);
+ }
+
+ // writer can contribute atoms
+ this->addAtoms(fOutputFile->getAtoms());
+
+ // each reader contributes atoms
+ for (std::vector<class ObjectFile::Reader*>::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) {
+ ObjectFile::Reader* reader = *it;
+ std::vector<class ObjectFile::Atom*>& atoms = reader->getAtoms();
+ this->addAtoms(atoms);
+ if ( fOptions.readerOptions().fTraceArchives && (atoms.size() != 0) )
+ logArchive(reader);
+ }
+
+ // extra command line section always at end
+ std::vector<Options::ExtraSection>& extraSections = fOptions.extraSections();
+ for( std::vector<Options::ExtraSection>::iterator it=extraSections.begin(); it != extraSections.end(); ++it) {
+ this->addAtoms((new opaque_section::Reader(it->segmentName, it->sectionName, it->path, it->data, it->dataLen, fNextInputOrdinal))->getAtoms());
+ fNextInputOrdinal += it->dataLen;
+ }
+
+ // done with all .o files on command line
+ // everything loaded from now on is a just-in-time atom
+ fInitialLoadsDone = true;
+}
+
+static const char* pathLeafName(const char* path)
+{
+ const char* shortPath = strrchr(path, '/');
+ if ( shortPath == NULL )
+ return path;
+ else
+ return &shortPath[1];
+}
+
+
+void Linker::loadUndefines()
+{
+ // keep looping until no more undefines were added in last loop
+ unsigned int undefineCount = 0xFFFFFFFF;
+ while ( undefineCount != fGlobalSymbolTable.getRequireCount() ) {
+ undefineCount = fGlobalSymbolTable.getRequireCount();
+ std::vector<const char*> undefineNames;
+ fGlobalSymbolTable.getUndefinesNames(undefineNames);
+ for(std::vector<const char*>::iterator it = undefineNames.begin(); it != undefineNames.end(); ++it) {
+ // load for previous undefine may also have loaded this undefine, so check again
+ if ( fGlobalSymbolTable.find(*it) == NULL ) {
+ std::vector<class ObjectFile::Atom*>* atoms = this->addJustInTimeAtoms(*it, true, true, true);
+ if ( atoms != NULL )
+ delete atoms;
+ }
+ }
+ // <rdar://problem/5894163> need to search archives for overrides of common symbols
+ if ( fGlobalSymbolTable.hasExternalTentativeDefinitions() ) {
+ bool searchDylibs = (fOptions.commonsMode() == Options::kCommonsOverriddenByDylibs);
+ std::vector<const char*> tentativeDefinitionNames;
+ fGlobalSymbolTable.getTentativesNames(tentativeDefinitionNames);
+ for(std::vector<const char*>::iterator it = tentativeDefinitionNames.begin(); it != tentativeDefinitionNames.end(); ++it) {
+ // load for previous tentative may also have overridden this tentative, so check again
+ ObjectFile::Atom* tent = fGlobalSymbolTable.find(*it);
+ if ( (tent != NULL) && (tent->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition) ) {
+ std::vector<class ObjectFile::Atom*>* atoms = this->addJustInTimeAtoms(*it, searchDylibs, true, false);
+ if ( atoms != NULL )
+ delete atoms;
+ }
+ }
+ }
+ }
+}
+
+// temp hack for rdar://problem/4718189 map ObjC class names to new runtime names
+class ExportedObjcClass
+{
+public:
+ ExportedObjcClass(Options& opt) : fOptions(opt) {}
+
+ bool operator()(const char* name) const {
+ if ( fOptions.shouldExport(name) ) {
+ if ( strncmp(name, ".objc_class_name_", 17) == 0 )
+ return true;
+ if ( strncmp(name, "_OBJC_CLASS_$_", 14) == 0 )
+ return true;
+ if ( strncmp(name, "_OBJC_METACLASS_$_", 18) == 0 )
+ return true;
+ }
+ //fprintf(stderr, "%s is not exported\n", name);
+ return false;
+ }
+private:
+ Options& fOptions;
+};
+
+
+void Linker::checkUndefines()
+{
+ // error out on any remaining undefines
+ bool doPrint = true;
+ bool doError = true;
+ switch ( fOptions.undefinedTreatment() ) {
+ case Options::kUndefinedError:
+ break;
+ case Options::kUndefinedDynamicLookup:
+ doError = false;
+ break;
+ case Options::kUndefinedWarning:
+ doError = false;
+ break;
+ case Options::kUndefinedSuppress:
+ doError = false;
+ doPrint = false;
+ break;
+ }
+ std::vector<const char*> unresolvableUndefines;
+ fGlobalSymbolTable.getUndefinesNames(unresolvableUndefines);
+
+ // temp hack for rdar://problem/4718189 map ObjC class names to new runtime names
+ // ignore unresolved references to Objc class names that are listed in -exported_symbols_list
+ if ( fOptions.hasExportRestrictList() )
+ unresolvableUndefines.erase(std::remove_if(unresolvableUndefines.begin(), unresolvableUndefines.end(), ExportedObjcClass(fOptions)), unresolvableUndefines.end());
+
+ const int unresolvableCount = unresolvableUndefines.size();
+ int unresolvableExportsCount = 0;
+ if ( unresolvableCount != 0 ) {
+ if ( doPrint ) {
+ 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);
+ // scan all atoms for references
+ bool foundAtomReference = false;
+ for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) {
+ ObjectFile::Atom* atom = *it;
+ std::vector<class ObjectFile::Reference*>& references = atom->getReferences();
+ for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) {
+ ObjectFile::Reference* reference = *rit;
+ if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) {
+ if ( strcmp(reference->getTargetName(), name) == 0 ) {
+ fprintf(stderr, " %s in %s\n", atom->getDisplayName(), pathLeafName(atom->getFile()->getPath()));
+ foundAtomReference = true;
+ }
+ }
+ if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) {
+ if ( strcmp(reference->getFromTargetName(), name) == 0 ) {
+ fprintf(stderr, " %s in %s\n", atom->getDisplayName(), pathLeafName(atom->getFile()->getPath()));
+ foundAtomReference = true;
+ }
+ }
+ }
+ }
+ // scan command line options
+ if ( !foundAtomReference && fOptions.hasExportRestrictList() && fOptions.shouldExport(name) ) {
+ fprintf(stderr, " -exported_symbols_list command line option\n");
+ ++unresolvableExportsCount;
+ }
+ }
+ }
+ if ( doError )
+ throw "symbol(s) not found";
+ }
+
+ // for each tentative definition in symbol table look for dylib that exports same symbol name
+ if ( fGlobalSymbolTable.hasExternalTentativeDefinitions() ) {
+ for (SymbolTable::Mapper::iterator it=fGlobalSymbolTable.begin(); it != fGlobalSymbolTable.end(); ++it) {
+ ObjectFile::Atom* atom = it->second;
+ if ( (atom != NULL) && (atom->getDefinitionKind()==ObjectFile::Atom::kTentativeDefinition)
+ && (atom->getScope() == ObjectFile::Atom::scopeGlobal) ) {
+ // look for dylibs that export same name as used by global tentative definition
+ addJustInTimeAtoms(atom->getName(), true, false, false);
+ }
+ }
+ }
+
+
+ // record any overrides of weak symbols any linked dylib
+ for (SymbolTable::Mapper::iterator it=fGlobalSymbolTable.begin(); it != fGlobalSymbolTable.end(); ++it) {
+ ObjectFile::Atom* atom = it->second;
+ if ( (atom != NULL) && (atom->getDefinitionKind()==ObjectFile::Atom::kRegularDefinition)
+ && (atom->getScope() == ObjectFile::Atom::scopeGlobal) ) {
+ const char* name = atom->getName();
+ //fprintf(stderr, "looking for dylibs with a weak %s\n", name);
+ // look for dylibs with weak exports of the same name
+ for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) {
+ ObjectFile::Reader* reader = it->second;
+ if ( reader->hasWeakExternals() ) {
+ std::vector<class ObjectFile::Atom*>* dylibAtoms = reader->getJustInTimeAtomsFor(name);
+ if ( dylibAtoms != NULL ) {
+ //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() );
+ // if this is a weak definition in a dylib
+ if ( (dylibAtoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) {
+ fRegularDefAtomsThatOverrideADylibsWeakDef.insert(atom);
+ }
+ }
+ }
+ }
+ }
+ }
+
+}
+
+
+
+std::vector<class ObjectFile::Atom*>* Linker::addJustInTimeAtoms(const char* name, bool searchDylibs, bool searchArchives, bool okToMakeProxy)
+{
+ //fprintf(stderr, "addJustInTimeAtoms(%s, searchDylibs=%d, searchArchives=%d)\n", name, searchDylibs, searchArchives );
+ // when creating final linked image, writer gets first chance
+ if ( fOptions.outputKind() != Options::kObjectFile ) {
+ std::vector<class ObjectFile::Atom*>* atoms = fOutputFile->getJustInTimeAtomsFor(name);
+ if ( atoms != NULL ) {
+ this->addAtoms(*atoms);
+ //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, fOutputFile->getPath() );
+ return atoms; // found a definition, no need to search anymore
+ }
+ }
+
+ // give readers a chance
+ for (std::vector<class ObjectFile::Reader*>::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) {
+ ObjectFile::Reader* reader = *it;
+ if ( reader != NULL ) {
+ // if this reader is a static archive that has the symbol we need, pull in all atoms in that module
+ // if this reader is a dylib that exports the symbol we need, have it synthesize an atom for us.
+ //fprintf(stderr, "addJustInTimeAtoms(%s), looking in reader %s\n", name, reader->getPath() );
+ bool isDylibReader = (reader->getInstallPath() != NULL);
+ if ( isDylibReader ? searchDylibs : searchArchives ) {
+ std::vector<class ObjectFile::Atom*>* atoms = reader->getJustInTimeAtomsFor(name);
+ if ( atoms != NULL ) {
+ this->addAtoms(*atoms);
+ //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() );
+ if ( !isDylibReader && fOptions.readerOptions().fTraceArchives ) {
+ logArchive(reader);
+ }
+ // if this is a weak definition in a dylib
+ if ( isDylibReader && (atoms->size() == 1) && (atoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) {
+ // keep looking for a non-weak definition
+ }
+ else {
+ // found a definition, no need to search anymore
+ return atoms;
+ }
+ }
+ }
+ }
+ }
+
+ // for two level namesapce, give all implicitly link dylibs a chance
+ if ( fOptions.nameSpace() == Options::kTwoLevelNameSpace ) {
+ for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) {
+ if ( it->second->implicitlyLinked() ) {
+ //fprintf(stderr, "addJustInTimeAtoms(%s), looking in implicitly linked %s\n", name, it->second->getPath() );
+ std::vector<class ObjectFile::Atom*>* atoms = it->second->getJustInTimeAtomsFor(name);
+ if ( atoms != NULL ) {
+ this->addAtoms(*atoms);
+ //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() );
+ // if this is a weak definition in a dylib
+ if ( (atoms->size() == 1) && (atoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) {
+ // keep looking for a non-weak definition
+ }
+ else {
+ // found a definition, no need to search anymore
+ return atoms;
+ }
+ }
+ }
+ }
+ }
+
+ // for flat namespace, give indirect dylibs
+ if ( fOptions.nameSpace() != Options::kTwoLevelNameSpace ) {
+ for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) {
+ if ( ! it->second->explicitlyLinked() ) {
+ std::vector<class ObjectFile::Atom*>* atoms = it->second->getJustInTimeAtomsFor(name);
+ if ( atoms != NULL ) {
+ this->addAtoms(*atoms);
+ //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() );
+ return atoms; // found a definition, no need to search anymore
+ }
+ }
+ }
+ }
+
+ // writer creates a proxy in two cases:
+ // 1) ld -r is being used to create a .o file
+ // 2) -undefined dynamic_lookup is being used
+ // 3) -U _foo is being used
+ // 4) x86_64 kext bundle is being created
+ if ( (fOptions.outputKind() == Options::kObjectFile)
+ || ((fOptions.undefinedTreatment() != Options::kUndefinedError) && okToMakeProxy)
+ || (fOptions.someAllowedUndefines() && okToMakeProxy)
+ || (fOptions.outputKind() == Options::kKextBundle) ) {
+ ObjectFile::Atom* atom = fOutputFile->getUndefinedProxyAtom(name);
+ if ( atom != NULL ) {
+ this->addAtom(*atom);
+ return NULL;
+ }
+ }
+ //fprintf(stderr, "addJustInTimeAtoms(%s) => not found\n", name);
+ return NULL;
+}
+
+void Linker::resolve(ObjectFile::Reference* reference)
+{
+ // look in global symbol table
+ const char* targetName = reference->getTargetName();
+ ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName);
+ if ( target == NULL ) {
+ throwf("unexpected undefined symbol: %s", targetName);
+ }
+ reference->setTarget(*target, reference->getTargetOffset());
+}
+
+void Linker::resolveFrom(ObjectFile::Reference* reference)
+{
+ // handle references that have two (from and to) targets
+ const char* fromTargetName = reference->getFromTargetName();
+ ObjectFile::Atom* fromTarget = fGlobalSymbolTable.find(fromTargetName);
+ if ( fromTarget == NULL ) {
+ throwf("unexpected undefined symbol: %s", fromTargetName);
+ }
+ reference->setFromTarget(*fromTarget);
+}
+
+
+void Linker::resolveReferences()
+{
+ // note: the atom list may grow during this loop as libraries supply needed atoms
+ for (unsigned int j=0; j < fAllAtoms.size(); ++j) {
+ ObjectFile::Atom* atom = fAllAtoms[j];
+ std::vector<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->getTargetBinding() == ObjectFile::Reference::kUnboundByName )
+ this->resolve(reference);
+ if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName )
+ this->resolveFrom(reference);
+ }
+ }
+}
+
+
+// used to remove stabs associated with atoms that won't be in output file
+class NotInSet
+{
+public:
+ NotInSet(std::set<ObjectFile::Atom*>& theSet) : fSet(theSet) {}
+
+ bool operator()(const ObjectFile::Reader::Stab& stab) const {
+ if ( stab.atom == NULL )
+ return false; // leave stabs that are not associated with any atome
+ else
+ return ( fSet.count(stab.atom) == 0 );
+ }
+
+private:
+ std::set<ObjectFile::Atom*>& fSet;
+};
+
+
+class NotLive
+{
+public:
+ NotLive(std::set<ObjectFile::Atom*>& set) : fLiveAtoms(set) {}
+
+ bool operator()(ObjectFile::Atom*& atom) const {
+ //if ( fLiveAtoms.count(atom) == 0 )
+ // fprintf(stderr, "dead strip %s\n", atom->getDisplayName());
+ return ( fLiveAtoms.count(atom) == 0 );
+ }
+private:
+ std::set<ObjectFile::Atom*>& fLiveAtoms;
+};
+
+
+void Linker::addJustInTimeAtomsAndMarkLive(const char* name)
+{
+ //fprintf(stderr, "addJustInTimeAtomsAndMarkLive(%s)\n", name);
+ std::vector<class ObjectFile::Atom*>* atoms = this->addJustInTimeAtoms(name, true, true, true);
+ if ( atoms != NULL ) {
+ if ( fOptions.allGlobalsAreDeadStripRoots() ) {
+ for (std::vector<ObjectFile::Atom*>::iterator it=atoms->begin(); it != atoms->end(); it++) {
+ ObjectFile::Atom* atom = *it;
+ if ( atom->getScope() == ObjectFile::Atom::scopeGlobal ) {
+ WhyLiveBackChain rootChain;
+ rootChain.previous = NULL;
+ rootChain.referer = atom;
+ this->markLive(*atom, &rootChain);
+ }
+ }
+ }
+ delete atoms;
+ }
+}
+
+void Linker::markLive(ObjectFile::Atom& atom, struct Linker::WhyLiveBackChain* previous)
+{
+ //fprintf(stderr, "markLive(%p)\n", &atom);
+ if ( fLiveAtoms.count(&atom) == 0 ) {
+ // if -why_live cares about this symbol, then dump chain
+ if ( (previous->referer != NULL) && fOptions.printWhyLive(previous->referer->getDisplayName()) ) {
+ int depth = 0;
+ for(WhyLiveBackChain* p = previous; p != NULL; p = p->previous, ++depth) {
+ for(int i=depth; i > 0; --i)
+ fprintf(stderr, " ");
+ fprintf(stderr, "%p %s from %s\n", p->referer, p->referer->getDisplayName(), p->referer->getFile()->getPath());
+ }
+ }
+ // set up next chain
+ WhyLiveBackChain thisChain;
+ thisChain.previous = previous;
+ // this atom is live
+ fLiveAtoms.insert(&atom);
+ // update total size info (except for __ZEROPAGE atom)
+ if ( atom.getSegment().isContentReadable() ) {
+ fTotalSize += atom.getSize();
+ if ( atom.isZeroFill() )
+ fTotalZeroFillSize += atom.getSize();
+ }
+ // and all atoms it references
+ 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->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) {
+ // look in global symbol table
+ const char* targetName = reference->getTargetName();
+ ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName);
+ if ( (target == NULL) || (target->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition) ) {
+ // load archives or dylibs
+ this->addJustInTimeAtomsAndMarkLive(targetName);
+ }
+ // look again
+ target = fGlobalSymbolTable.find(targetName);
+ if ( target != NULL ) {
+ reference->setTarget(*target, reference->getTargetOffset());
+ }
+ else {
+ // mark as undefined, for later error processing
+ fAtomsWithUnresolvedReferences.push_back(&atom);
+ fGlobalSymbolTable.require(targetName);
+ }
+ }
+ switch ( reference->getTargetBinding() ) {
+ case ObjectFile::Reference::kBoundDirectly:
+ case ObjectFile::Reference::kBoundByName:
+ thisChain.referer = &reference->getTarget();
+ markLive(reference->getTarget(), &thisChain);
+ break;
+ case ObjectFile::Reference::kDontBind:
+ case ObjectFile::Reference::kUnboundByName:
+ // do nothing
+ break;
+ }
+ // do the same as above, for "from target"
+ if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) {
+ // look in global symbol table
+ const char* targetName = reference->getFromTargetName();
+ ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName);
+ if ( (target == NULL) || (target->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition) ) {
+ // load archives or dylibs
+ this->addJustInTimeAtomsAndMarkLive(targetName);
+ }
+ // look again
+ target = fGlobalSymbolTable.find(targetName);
+ if ( target != NULL ) {
+ reference->setFromTarget(*target);
+ }
+ else {
+ // mark as undefined, for later error processing
+ fGlobalSymbolTable.require(targetName);
+ }
+ }
+ switch ( reference->getFromTargetBinding() ) {
+ case ObjectFile::Reference::kBoundDirectly:
+ case ObjectFile::Reference::kBoundByName:
+ thisChain.referer = &reference->getFromTarget();
+ markLive(reference->getFromTarget(), &thisChain);
+ break;
+ case ObjectFile::Reference::kUnboundByName:
+ case ObjectFile::Reference::kDontBind:
+ // do nothing
+ break;
+ }
+ }
+ }
+}
+
+
+void Linker::addLiveRoot(const char* name)
+{
+ ObjectFile::Atom* target = fGlobalSymbolTable.find(name);
+ if ( target == NULL ) {
+ this->addJustInTimeAtomsAndMarkLive(name);
+ target = fGlobalSymbolTable.find(name);
+ }
+ if ( target != NULL )
+ fLiveRootAtoms.insert(target);
+}
+
+void Linker::moveToFrontOfSection(ObjectFile::Atom* atom)
+{
+ // check if already moved to front
+ if ( fInitializerAtoms.find(atom) == fInitializerAtoms.end() ) {
+ // don't re-order initializers from .o files without MH_SUBSECTIONS_VIA_SYMBOLS
+ // since that could make all atoms in the file look like initializers
+ if ( atom->getFile()->canScatterAtoms() ) {
+ //fprintf(stdout, "marking as initializer: %s\n", atom->getDisplayName());
+ fInitializerAtoms.insert(atom);
+ // mark all functions that this function references
+ std::vector<class ObjectFile::Reference*>& references = atom->getReferences();
+ for (std::vector<ObjectFile::Reference*>::const_iterator rit=references.begin(); rit != references.end(); rit++) {
+ ObjectFile::Atom* childAtom = &((*rit)->getTarget());
+ if ( childAtom != NULL ) {
+ if ( (*rit)->isBranch() ) {
+ this->moveToFrontOfSection(childAtom);
+ }
+ else if ( (childAtom->getName() != NULL) && (strncmp(childAtom->getName(), "___tcf_", 7) == 0) ) {
+ //fprintf(stdout, "marking as terminator: %s\n", childAtom->getDisplayName());
+ fTerminatorAtoms.insert(childAtom);
+ }
+ }
+ }
+ }
+ }
+}
+
+void Linker::deadStripResolve()
+{
+ // add main() to live roots
+ ObjectFile::Atom* entryPoint = this->entryPoint(false);
+ if ( entryPoint != NULL )
+ fLiveRootAtoms.insert(entryPoint);
+
+ // add dyld_stub_binding_helper/dyld_stub_binder to live roots
+ ObjectFile::Atom* dyldHelper = this->dyldClassicHelper();
+ if ( dyldHelper != NULL )
+ fLiveRootAtoms.insert(dyldHelper);
+ dyldHelper = this->dyldCompressedHelper();
+ if ( dyldHelper != NULL )
+ fLiveRootAtoms.insert(dyldHelper);
+
+ // if using lazy dylib loading, add dyld_lazy_dylib_stub_binding_helper() to live roots
+ if ( fOptions.usingLazyDylibLinking() ) {
+ ObjectFile::Atom* dyldLazyDylibHelper = this->dyldLazyLibraryHelper();
+ if ( dyldLazyDylibHelper != NULL )
+ fLiveRootAtoms.insert(dyldLazyDylibHelper);
+ }
+
+ // 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);
+
+ // if -exported_symbols_list that has wildcards, we need to find all matches and make them the roots
+ // <rdar://problem/5524973>
+ if ( fOptions.hasWildCardExportRestrictList() ) {
+ for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) {
+ ObjectFile::Atom* atom = *it;
+ if ( (atom->getScope() == ObjectFile::Atom::scopeGlobal)
+ && (fDeadAtoms.count(atom) == 0)
+ && fOptions.shouldExport(atom->getName()) )
+ fLiveRootAtoms.insert(atom);
+ }
+ }
+
+ // 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;
+ if ( (atom->getScope() == ObjectFile::Atom::scopeGlobal) && (fDeadAtoms.count(atom) == 0) )
+ fLiveRootAtoms.insert(atom);
+ }
+ }
+
+ // 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.referer = *it;
+ markLive(**it, &rootChain);
+ }
+
+ // it is possible that there are unresolved references that can be resolved now
+ // this can happen if the first reference to a common symbol in an archive.
+ // common symbols are not in the archive TOC, but the .o could have been pulled in later.
+ // <rdar://problem/4654131> ld64 while linking cc1 [ when dead_strip is ON]
+ for (std::vector<ObjectFile::Atom*>::iterator it=fAtomsWithUnresolvedReferences.begin(); it != fAtomsWithUnresolvedReferences.end(); it++) {
+ std::vector<class ObjectFile::Reference*>& references = (*it)->getReferences();
+ for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) {
+ ObjectFile::Reference* reference = *rit;
+ if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) {
+ ObjectFile::Atom* target = fGlobalSymbolTable.find(reference->getTargetName());
+ if ( target != NULL ) {
+ reference->setTarget(*target, reference->getTargetOffset());
+ fLiveAtoms.insert(target);
+ // by just adding this atom to fLiveAtoms set, we are assuming it has no
+ // references, which is true for commons.
+ if ( target->getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition )
+ warning("internal error %s is not a tentative definition", target->getDisplayName());
+ }
+ }
+ if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) {
+ ObjectFile::Atom* target = fGlobalSymbolTable.find(reference->getFromTargetName());
+ if ( target != NULL ) {
+ reference->setFromTarget(*target);
+ fLiveAtoms.insert(target);
+ // by just adding this atom to fLiveAtoms set, we are assuming it has no
+ // references, which is true for commons.
+ if ( target->getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition )
+ warning("internal error %s is not a tentative definition", target->getDisplayName());
+ }
+ }
+ }
+ }
+
+ // It is possible that some weak symbols were overridden by lazily load objects from archives
+ // and we have some atoms that still refer to the overridden ones.
+ // In that case we need to go back and rebind
+ if ( fAtomsOverriddenByLateLoads.size() > 0 ) {
+ for (std::set<ObjectFile::Atom*>::iterator it=fLiveAtoms.begin(); it != fLiveAtoms.end(); ++it) {
+ ObjectFile::Atom* atom = *it;
+ std::vector<class ObjectFile::Reference*>& references = atom->getReferences();
+ for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); ++rit) {
+ ObjectFile::Reference* reference = *rit;
+ ObjectFile::Atom* toTarget = &reference->getTarget();
+ if ( fAtomsOverriddenByLateLoads.count(toTarget) ) {
+ //fprintf(stderr, "change reference in %p from %p to %p\n", atom, toTarget, fGlobalSymbolTable.find(toTarget->getName()));
+ reference->setTarget(*fGlobalSymbolTable.find(toTarget->getName()), reference->getTargetOffset());
+ }
+ ObjectFile::Atom* fromTarget = &reference->getFromTarget();
+ if ( (fromTarget != NULL) && fAtomsOverriddenByLateLoads.count(fromTarget) ) {
+ //fprintf(stderr, "change from reference in %p from %p to %p\n", atom, fromTarget, fGlobalSymbolTable.find(fromTarget->getName()));
+ reference->setTarget(*fGlobalSymbolTable.find(fromTarget->getName()), reference->getFromTargetOffset());
+ }
+ }
+ }
+
+ // make sure overriders are live if the atom they overrid was live
+ for (std::set<ObjectFile::Atom*>::iterator it=fAtomsOverriddenByLateLoads.begin(); it != fAtomsOverriddenByLateLoads.end(); ++it) {
+ ObjectFile::Atom* overriderAtom = *it;
+ if ( fLiveAtoms.count(overriderAtom) ) {
+ WhyLiveBackChain rootChain;
+ rootChain.previous = NULL;
+ rootChain.referer = *it;
+ markLive(*fGlobalSymbolTable.find(overriderAtom->getName()), &rootChain);
+ }
+ }
+
+ // remove overridden atoms from fLiveAtoms
+ fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), InSet(fAtomsOverriddenByLateLoads)), fAllAtoms.end());
+ fAtomsOverriddenByLateLoads.clear();
+ // remove dead atoms from fLiveAtoms
+ fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), InSet(fDeadAtoms)), fAllAtoms.end());
+ }
+
+ // now remove all non-live atoms from fAllAtoms
+ fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), NotLive(fLiveAtoms)), fAllAtoms.end());
+}
+
+void Linker::checkObjC()
+{
+ // check dylibs
+ switch ( fCurrentObjCConstraint ) {
+ case ObjectFile::Reader::kObjcNone:
+ // can link against any dylib
+ break;
+ case ObjectFile::Reader::kObjcRetainRelease:
+ // cannot link against GC-only dylibs
+ for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) {
+ if ( it->second->explicitlyLinked() ) {
+ if ( it->second->getObjCConstraint() == ObjectFile::Reader::kObjcGC )
+ throwf("this linkage unit uses Retain/Release. It cannot link against the GC-only dylib: %s", it->second->getPath());
+ }
+ }
+ break;
+ case ObjectFile::Reader::kObjcRetainReleaseOrGC:
+ // can link against GC or RR dylibs
+ break;
+ case ObjectFile::Reader::kObjcGC:
+ // cannot link against RR-only dylibs
+ for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) {
+ if ( it->second->explicitlyLinked() ) {
+ if ( it->second->getObjCConstraint() == ObjectFile::Reader::kObjcRetainRelease )
+ throwf("this linkage unit requires GC. It cannot link against Retain/Release dylib: %s", it->second->getPath());
+ }
+ }
+ break;
+ }
+
+ // synthesize __OBJC __image_info atom if needed
+ if ( fCurrentObjCConstraint != ObjectFile::Reader::kObjcNone ) {
+ this->addAtom(fOutputFile->makeObjcInfoAtom(fCurrentObjCConstraint, fObjcReplacmentClasses));
+ }
+}
+
+
+static uint8_t pcRelKind(cpu_type_t arch)
+{
+ switch ( arch ) {
+ case CPU_TYPE_POWERPC:
+ return ppc::kPointerDiff32;
+ case CPU_TYPE_POWERPC64:
+ return ppc64::kPointerDiff32;
+ case CPU_TYPE_I386:
+ return x86::kPointerDiff;
+ case CPU_TYPE_X86_64:
+ return x86_64::kPointerDiff32;
+ case CPU_TYPE_ARM:
+ return arm::kPointerDiff;
+ }
+ throw "uknown architecture";
+}
+
+typedef uint8_t* (*oldcreatedof_func_t) (const char*, cpu_type_t, unsigned int, const char*[], const char*[], uint64_t offsetsInDOF[], size_t* size);
+typedef uint8_t* (*createdof_func_t)(cpu_type_t, unsigned int, const char*[], unsigned int, const char*[], const char*[], uint64_t offsetsInDOF[], size_t* size);
+
+
+void Linker::processDTrace()
+{
+ // only make __dof section in final linked images
+ if ( fOptions.outputKind() == Options::kObjectFile )
+ return;
+
+ // scan all atoms looking for dtrace probes
+ std::vector<DTraceProbeInfo> probeSites;
+ std::vector<DTraceProbeInfo> isEnabledSites;
+ std::map<const ObjectFile::Atom*,CStringSet> atomToDtraceTypes;
+ for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); ++it) {
+ ObjectFile::Atom* atom = *it;
+ std::vector<class ObjectFile::Reference*>& references = atom->getReferences();
+ for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); ++rit) {
+ ObjectFile::Reference* ref = *rit;
+ if ( ref->getTargetBinding() == ObjectFile::Reference::kDontBind ) {
+ const char* probeName = ref->getTargetName();
+ if ( probeName != NULL ) {
+ uint32_t offsetInAtom = ref->getFixUpOffset();
+ if ( strncmp(probeName, "___dtrace_probe$", 16) == 0 )
+ probeSites.push_back(DTraceProbeInfo(atom, offsetInAtom, probeName));
+ else if ( strncmp(probeName, "___dtrace_isenabled$", 20) == 0 )
+ isEnabledSites.push_back(DTraceProbeInfo(atom, offsetInAtom, probeName));
+ else if ( strncmp(probeName, "___dtrace_", 10) == 0 )
+ atomToDtraceTypes[atom].insert(probeName);
+ }
+ }
+ }
+ }
+
+ // if no probes, we're done
+ if ( (probeSites.size() == 0) && (isEnabledSites.size() == 0) )
+ return;
+
+ // partition probes by provider name
+ // The symbol names looks like:
+ // "___dtrace_probe$" provider-name "$" probe-name [ "$"... ]
+ // "___dtrace_isenabled$" provider-name "$" probe-name [ "$"... ]
+ ProviderToProbes providerToProbes;
+ std::vector<DTraceProbeInfo> emptyList;
+ for(std::vector<DTraceProbeInfo>::iterator it = probeSites.begin(); it != probeSites.end(); ++it) {
+ // ignore probes in functions that were coalesed away rdar://problem/5628149
+ if ( fDeadAtoms.count((ObjectFile::Atom*)(it->atom)) == 0 ) {
+ const char* providerStart = &it->probeName[16];
+ const char* providerEnd = strchr(providerStart, '$');
+ if ( providerEnd != NULL ) {
+ char providerName[providerEnd-providerStart+1];
+ strlcpy(providerName, providerStart, providerEnd-providerStart+1);
+ ProviderToProbes::iterator pos = providerToProbes.find(providerName);
+ if ( pos == providerToProbes.end() ) {
+ const char* dup = strdup(providerName);
+ providerToProbes[dup] = emptyList;
+ }
+ providerToProbes[providerName].push_back(*it);
+ }
+ }
+ }
+ for(std::vector<DTraceProbeInfo>::iterator it = isEnabledSites.begin(); it != isEnabledSites.end(); ++it) {
+ // ignore probes in functions that were coalesed away rdar://problem/5628149
+ if ( fDeadAtoms.count((ObjectFile::Atom*)(it->atom)) == 0 ) {
+ const char* providerStart = &it->probeName[20];
+ const char* providerEnd = strchr(providerStart, '$');
+ if ( providerEnd != NULL ) {
+ char providerName[providerEnd-providerStart+1];
+ strlcpy(providerName, providerStart, providerEnd-providerStart+1);
+ ProviderToProbes::iterator pos = providerToProbes.find(providerName);
+ if ( pos == providerToProbes.end() ) {
+ const char* dup = strdup(providerName);
+ providerToProbes[dup] = emptyList;
+ }
+ providerToProbes[providerName].push_back(*it);
+ }
+ }
+ }
+
+ // create a DOF section for each provider
+ int dofIndex=1;
+ CStringSet sectionNamesUsed;
+ for(ProviderToProbes::iterator pit = providerToProbes.begin(); pit != providerToProbes.end(); ++pit, ++dofIndex) {
+ const char* providerName = pit->first;
+ const std::vector<DTraceProbeInfo>& probes = pit->second;
+
+ // open library and find dtrace_create_dof()
+ void* handle = dlopen("/usr/lib/libdtrace.dylib", RTLD_LAZY);
+ if ( handle == NULL )
+ throwf("couldn't dlopen() /usr/lib/libdtrace.dylib: %s", dlerror());
+ createdof_func_t pCreateDOF = (createdof_func_t)dlsym(handle, "dtrace_ld_create_dof");
+ if ( pCreateDOF == NULL )
+ throwf("couldn't find \"dtrace_ld_create_dof\" in /usr/lib/libdtrace.dylib: %s", dlerror());
+ // build list of typedefs/stability infos for this provider
+ CStringSet types;
+ for(std::vector<DTraceProbeInfo>::const_iterator it = probes.begin(); it != probes.end(); ++it) {
+ std::map<const ObjectFile::Atom*,CStringSet>::iterator pos = atomToDtraceTypes.find(it->atom);
+ if ( pos != atomToDtraceTypes.end() ) {
+ for(CStringSet::iterator sit = pos->second.begin(); sit != pos->second.end(); ++sit) {
+ const char* providerStart = strchr(*sit, '$')+1;
+ const char* providerEnd = strchr(providerStart, '$');
+ if ( providerEnd != NULL ) {
+ char aProviderName[providerEnd-providerStart+1];
+ strlcpy(aProviderName, providerStart, providerEnd-providerStart+1);
+ if ( strcmp(aProviderName, providerName) == 0 )
+ types.insert(*sit);
+ }
+ }
+ }
+ }
+ int typeCount = types.size();
+ const char* typeNames[typeCount];
+ //fprintf(stderr, "types for %s:\n", providerName);
+ uint32_t index = 0;
+ for(CStringSet::iterator it = types.begin(); it != types.end(); ++it) {
+ typeNames[index] = *it;
+ //fprintf(stderr, "\t%s\n", *it);
+ ++index;
+ }
+
+ // build list of probe/isenabled sites
+ const uint32_t probeCount = probes.size();
+ const char* probeNames[probeCount];
+ const char* funtionNames[probeCount];
+ uint64_t offsetsInDOF[probeCount];
+ index = 0;
+ for(std::vector<DTraceProbeInfo>::const_iterator it = probes.begin(); it != probes.end(); ++it) {
+ probeNames[index] = it->probeName;
+ funtionNames[index] = it->atom->getName();
+ offsetsInDOF[index] = 0;
+ ++index;
+ }
+ //fprintf(stderr, "calling libtrace to create DOF\n");
+ //for(uint32_t i=0; i < probeCount; ++i)
+ // fprintf(stderr, " [%u]\t %s\t%s\n", i, probeNames[i], funtionNames[i]);
+ // call dtrace library to create DOF section
+ size_t dofSectionSize;
+ uint8_t* p = (*pCreateDOF)(fArchitecture, typeCount, typeNames, probeCount, probeNames, funtionNames, offsetsInDOF, &dofSectionSize);
+ if ( p != NULL ) {
+ char sectionName[18];
+ strcpy(sectionName, "__dof_");
+ strlcpy(§ionName[6], providerName, 10);
+ // create unique section name so each DOF is in its own section
+ if ( sectionNamesUsed.count(sectionName) != 0 ) {
+ sectionName[15] = '0';
+ sectionName[16] = '\0';
+ while ( sectionNamesUsed.count(sectionName) != 0 )
+ ++sectionName[15];
+ }
+ sectionNamesUsed.insert(sectionName);
+ char symbolName[strlen(providerName)+64];
+ sprintf(symbolName, "__dtrace_dof_for_provider_%s", providerName);
+ opaque_section::Reader* reader = new opaque_section::Reader("__TEXT", sectionName,
+ "dtrace", p, dofSectionSize, fNextInputOrdinal, symbolName);
+ fNextInputOrdinal += dofSectionSize;
+ // add references
+ for (uint32_t i=0; i < probeCount; ++i) {
+ uint64_t offset = offsetsInDOF[i];
+ //fprintf(stderr, "%s offset[%d]=0x%08llX\n", providerName, i, offset);
+ if ( offset > dofSectionSize )
+ throwf("offsetsInDOF[i]=%0llX > dofSectionSize=%0lX\n", i, offset, dofSectionSize);
+ reader->addSectionReference(pcRelKind(fArchitecture), offset, probes[i].atom, probes[i].offset, reader->getAtoms()[0], 0);
+ }
+ this->addAtoms(reader->getAtoms());
+ }
+ else {
+ throw "error creating dtrace DOF section";
+ }
+ }
+}
+
+
+static bool matchesObjectFile(ObjectFile::Atom* atom, const char* objectFileLeafName)
+{
+ if ( objectFileLeafName == NULL )
+ return true;
+ const char* atomFullPath = atom->getFile()->getPath();
+ const char* lastSlash = strrchr(atomFullPath, '/');
+ if ( lastSlash != NULL ) {
+ if ( strcmp(&lastSlash[1], objectFileLeafName) == 0 )
+ return true;
+ }
+ else {
+ if ( strcmp(atomFullPath, objectFileLeafName) == 0 )
+ return true;
+ }
+ return false;
+}
+
+
+static bool usesAnonymousNamespace(const char* symbol)
+{
+ return ( (strncmp(symbol, "__Z", 3) == 0) && (strstr(symbol, "_GLOBAL__N_") != NULL) );
+}
+
+
+//
+// convert:
+// __ZN20_GLOBAL__N__Z5main2v3barEv => _ZN-3barEv
+// __ZN37_GLOBAL__N_main.cxx_00000000_493A01A33barEv => _ZN-3barEv
+//
+static void canonicalizeAnonymousName(const char* inSymbol, char outSymbol[])
+{
+ const char* globPtr = strstr(inSymbol, "_GLOBAL__N_");
+ while ( isdigit(*(--globPtr)) )
+ ; // loop
+ char* endptr;
+ unsigned long length = strtoul(globPtr+1, &endptr, 10);
+ const char* globEndPtr = endptr + length;
+ int startLen = globPtr-inSymbol+1;
+ memcpy(outSymbol, inSymbol, startLen);
+ outSymbol[startLen] = '-';
+ strcpy(&outSymbol[startLen+1], globEndPtr);
+}
+
+
+ObjectFile::Atom* Linker::findAtom(const Options::OrderedSymbol& orderedSymbol)
+{
+ ObjectFile::Atom* atom = fGlobalSymbolTable.find(orderedSymbol.symbolName);
+ if ( atom != NULL ) {
+ if ( matchesObjectFile(atom, orderedSymbol.objectFileName) )
+ return atom;
+ }
+ else {
+ // slow case. The requested symbol is not in symbol table, so might be static function
+ static SymbolTable::Mapper hashTableOfTranslationUnitScopedSymbols;
+ static SymbolTable::Mapper hashTableOfSymbolsWithAnonymousNamespace;
+ static bool built = false;
+ // build a hash_map the first time
+ if ( !built ) {
+ for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) {
+ atom = *it;
+ const char* name = atom->getName();
+ if ( name != NULL) {
+ if ( usesAnonymousNamespace(name) ) {
+ // symbol that uses anonymous namespace
+ char canonicalName[strlen(name)+2];
+ canonicalizeAnonymousName(name, canonicalName);
+ const char* hashName = strdup(canonicalName);
+ SymbolTable::Mapper::iterator pos = hashTableOfSymbolsWithAnonymousNamespace.find(hashName);
+ if ( pos == hashTableOfSymbolsWithAnonymousNamespace.end() )
+ hashTableOfSymbolsWithAnonymousNamespace[hashName] = atom;
+ else
+ hashTableOfSymbolsWithAnonymousNamespace[hashName] = NULL; // collision, denote with NULL
+ }
+ else if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) {
+ // static function or data
+ SymbolTable::Mapper::iterator pos = hashTableOfTranslationUnitScopedSymbols.find(name);
+ if ( pos == hashTableOfTranslationUnitScopedSymbols.end() )
+ hashTableOfTranslationUnitScopedSymbols[name] = atom;
+ else
+ hashTableOfTranslationUnitScopedSymbols[name] = NULL; // collision, denote with NULL
+ }
+ }
+ }
+ //fprintf(stderr, "built hash table of %lu static functions\n", hashTableOfTranslationUnitScopedSymbols.size());
+ built = true;
+ }
+
+ // look for name in hashTableOfTranslationUnitScopedSymbols
+ SymbolTable::Mapper::iterator pos = hashTableOfTranslationUnitScopedSymbols.find(orderedSymbol.symbolName);
+ if ( pos != hashTableOfTranslationUnitScopedSymbols.end() ) {
+ if ( (pos->second != NULL) && matchesObjectFile(pos->second, orderedSymbol.objectFileName) ) {
+ //fprintf(stderr, "found %s in hash table\n", orderedSymbol.symbolName);
+ return pos->second;
+ }
+ if ( pos->second == NULL )
+ // name is in hash table, but atom is NULL, so that means there are duplicates, so we use super slow way
+ for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) {
+ atom = *it;
+ if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) {
+ const char* name = atom->getName();
+ if ( (name != NULL) && (strcmp(name, orderedSymbol.symbolName) == 0) ) {
+ if ( matchesObjectFile(atom, orderedSymbol.objectFileName) ) {
+ if ( fOptions.printOrderFileStatistics() )
+ warning("%s specified in order_file but it exists in multiple .o files. "
+ "Prefix symbol with .o filename in order_file to disambiguate", orderedSymbol.symbolName);
+ return atom;
+ }
+ }
+ }
+ }
+ }
+
+ // look for name in hashTableOfSymbolsWithAnonymousNamespace
+ if ( usesAnonymousNamespace(orderedSymbol.symbolName) ) {
+ // symbol that uses anonymous namespace
+ char canonicalName[strlen(orderedSymbol.symbolName)+2];
+ canonicalizeAnonymousName(orderedSymbol.symbolName, canonicalName);
+ SymbolTable::Mapper::iterator pos = hashTableOfSymbolsWithAnonymousNamespace.find(canonicalName);
+ if ( pos != hashTableOfSymbolsWithAnonymousNamespace.end() ) {
+ if ( (pos->second != NULL) && matchesObjectFile(pos->second, orderedSymbol.objectFileName) ) {
+ //fprintf(stderr, "found %s in anonymous namespace hash table\n", canonicalName);
+ return pos->second;
+ }
+ if ( pos->second == NULL )
+ // name is in hash table, but atom is NULL, so that means there are duplicates, so we use super slow way
+ for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) {
+ atom = *it;
+ const char* name = atom->getName();
+ if ( (name != NULL) && usesAnonymousNamespace(name) ) {
+ char canonicalAtomName[strlen(name)+2];
+ canonicalizeAnonymousName(name, canonicalAtomName);
+ if ( strcmp(canonicalAtomName, canonicalName) == 0 ) {
+ if ( matchesObjectFile(atom, orderedSymbol.objectFileName) ) {
+ if ( fOptions.printOrderFileStatistics() )
+ warning("%s specified in order_file but it exists in multiple .o files. "
+ "Prefix symbol with .o filename in order_file to disambiguate", orderedSymbol.symbolName);
+ return atom;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+
+void Linker::sortSections()
+{
+ Section::assignIndexes();
+}
+
+
+//
+// Linker::sortAtoms()
+//
+// The purpose of this method is to take the graph of all Atoms and produce an ordered
+// sequence of atoms. The constraints are that: 1) all Atoms of the same Segment must
+// be contiguous, 2) all Atoms of the same Section must be contigous, 3) Atoms specified
+// in an order_file are seqenced as in the order_file and before Atoms not specified,
+// 4) Atoms in the same section from the same .o file should be contiguous and sequenced
+// in the same order they were in the .o file, 5) Atoms in the same Section but which came
+// from different .o files should be sequenced in the same order that the .o files
+// were passed to the linker (i.e. command line order).
+//
+// The way this is implemented is that the linker passes a "base ordinal" to each Reader
+// as it is constructed. The reader should construct it Atoms so that calling getOrdinal()
+// on its atoms returns a contiguous range of values starting at the base ordinal. Then
+// sorting is just sorting by section, then by ordinal.
+//
+// If an order_file is specified, it gets more complicated. First, an override-ordinal map
+// is created. It causes the sort routine to ignore the value returned by getOrdinal() and
+// use the override value instead. Next some Atoms must be layed out consecutively
+// (e.g. hand written assembly that does not end with return, but rather falls into
+// the next label). This is modeled in Readers via a "kFollowOn" reference. The use of
+// kFollowOn refernces produces "clusters" of atoms that must stay together.
+// If an order_file tries to move one atom, it may need to move a whole cluster. The
+// algorithm to do this models clusters using two maps. The "starts" maps maps any
+// atom in a cluster to the first Atom in the cluster. The "nexts" maps an Atom in a
+// cluster to the next Atom in the cluster. With this in place, while processing an
+// order_file, if any entry is in a cluster (in "starts" map), then the entire cluster is
+// given ordinal overrides.
+//
+void Linker::sortAtoms()
+{
+ fStartSortTime = mach_absolute_time();
+ // if -order_file is used, build map of atom ordinal overrides
+ std::map<const ObjectFile::Atom*, uint32_t>* ordinalOverrideMap = NULL;
+ std::map<const ObjectFile::Atom*, uint32_t> theOrdinalOverrideMap;
+ const bool log = false;
+ if ( fOptions.orderedSymbols().size() != 0 ) {
+ // first make a pass to find all follow-on references and build start/next maps
+ // which are a way to represent clusters of atoms that must layout together
+ std::map<const ObjectFile::Atom*, const ObjectFile::Atom*> followOnStarts;
+ std::map<const ObjectFile::Atom*, const ObjectFile::Atom*> followOnNexts;
+ for (std::vector<ObjectFile::Atom*>::iterator ait=fAllAtoms.begin(); ait != fAllAtoms.end(); ait++) {
+ ObjectFile::Atom* atom = *ait;
+ std::vector<class ObjectFile::Reference*>& references = atom->getReferences();
+ for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) {
+ ObjectFile::Reference* ref = *rit;
+ if ( ref->getKind() == 1 ) { // FIX FIX
+ ObjectFile::Atom* targetAtom = &ref->getTarget();
+ if ( log ) fprintf(stderr, "ref %s -> %s", atom->getDisplayName(), targetAtom->getDisplayName());
+ std::map<const ObjectFile::Atom*, const ObjectFile::Atom*>::iterator startFrom = followOnStarts.find(atom);
+ std::map<const ObjectFile::Atom*, const ObjectFile::Atom*>::iterator startTo = followOnStarts.find(targetAtom);
+ if ( (startFrom == followOnStarts.end()) && (startTo == followOnStarts.end()) ) {
+ // this is first time we've seen either atom, make simple cluster of the two
+ if ( log ) fprintf(stderr, " new cluster\n");
+ followOnStarts[atom] = atom;
+ followOnStarts[targetAtom] = atom;
+ followOnNexts[atom] = targetAtom;
+ followOnNexts[targetAtom] = NULL;
+ }
+ else if ( (startFrom != followOnStarts.end()) && (startTo == followOnStarts.end()) && (followOnNexts[atom] == NULL) ) {
+ // atom is at end of an existing cluster, so append target to end of cluster
+ if ( log ) fprintf(stderr, " end of cluster starting with %s\n", followOnStarts[atom]->getDisplayName());
+ followOnNexts[atom] = targetAtom;
+ followOnNexts[targetAtom] = NULL;
+ followOnStarts[targetAtom] = followOnStarts[atom];
+ }
+ else {
+ // gerneral case of inserting into an existing cluster
+ if ( followOnNexts[atom] != NULL ) {
+ // an atom with two follow-ons is illegal
+ warning("can't order %s because both %s and %s must follow it",
+ atom->getDisplayName(), targetAtom->getDisplayName(), followOnNexts[atom]->getDisplayName());
+ }
+ else {
+ // there already exists an atom that says target must be its follow-on
+ const ObjectFile::Atom* originalStart = startTo->second;
+ const ObjectFile::Atom* originalPrevious = originalStart;
+ while ( followOnNexts[originalPrevious] != targetAtom )
+ originalPrevious = followOnNexts[originalPrevious];
+ bool otherIsAlias = (originalPrevious->getSize() == 0);
+ bool thisIsAlias = (atom->getSize() == 0);
+ if ( !otherIsAlias && !thisIsAlias ) {
+ warning("can't order %s because both %s and %s must preceed it",
+ targetAtom->getDisplayName(), originalPrevious->getDisplayName(), atom->getDisplayName());
+ }
+ else if ( otherIsAlias ) {
+ if ( originalPrevious == originalStart ) {
+ // other is alias at start of cluster, make this the new start of cluster
+ if ( log ) fprintf(stderr, " becomes new start of cluster previous starting with %s\n", originalStart->getDisplayName());
+ followOnNexts[atom] = originalPrevious;
+ for(const ObjectFile::Atom* nextAtom = atom; nextAtom != NULL; nextAtom = followOnNexts[nextAtom])
+ followOnStarts[nextAtom] = atom;
+ }
+ else {
+ // other is alias in middle of cluster, insert new atom before it
+ if ( log ) fprintf(stderr, " insert into cluster starting with %s before alias %s\n", originalStart->getDisplayName(), originalPrevious->getDisplayName());
+ followOnStarts[atom] = originalStart;
+ followOnNexts[atom] = originalPrevious;
+ for(const ObjectFile::Atom* a = originalStart; a != NULL; a = followOnNexts[a]) {
+ if ( followOnNexts[a] == originalPrevious ) {
+ followOnNexts[a] = atom;
+ break;
+ }
+ }
+ }
+ }
+ else {
+ // this is alias, so it can go inbetween originalPrevious and targetAtom
+ if ( log ) fprintf(stderr, " insert into cluster starting with %s after %s\n", originalStart->getDisplayName(), originalPrevious->getDisplayName());
+ followOnStarts[atom] = originalStart;
+ followOnNexts[atom] = followOnNexts[originalPrevious];
+ followOnNexts[originalPrevious] = atom;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ( log ) {
+ for(std::map<const ObjectFile::Atom*, const ObjectFile::Atom*>::iterator it = followOnStarts.begin(); it != followOnStarts.end(); ++it)
+ fprintf(stderr, "start %s -> %s\n", it->first->getDisplayName(), it->second->getDisplayName());
+
+ for(std::map<const ObjectFile::Atom*, const ObjectFile::Atom*>::iterator it = followOnNexts.begin(); it != followOnNexts.end(); ++it)
+ fprintf(stderr, "next %s -> %s\n", it->first->getDisplayName(), (it->second != NULL) ? it->second->getDisplayName() : "null");
+ }
+
+ // with the start/next maps of follow-on atoms we can process the order file and produce override ordinals
+ ordinalOverrideMap = &theOrdinalOverrideMap;
+ uint32_t index = 0;
+ uint32_t matchCount = 0;
+ std::vector<Options::OrderedSymbol>& orderedSymbols = fOptions.orderedSymbols();
+ for(std::vector<Options::OrderedSymbol>::iterator it = orderedSymbols.begin(); it != orderedSymbols.end(); ++it) {
+ ObjectFile::Atom* atom = this->findAtom(*it);
+ if ( atom != NULL ) {
+ std::map<const ObjectFile::Atom*, const ObjectFile::Atom*>::iterator start = followOnStarts.find(atom);
+ if ( start != followOnStarts.end() ) {
+ // this symbol for the order file corresponds to an atom that is in a cluster that must lay out together
+ for(const ObjectFile::Atom* nextAtom = start->second; nextAtom != NULL; nextAtom = followOnNexts[nextAtom]) {
+ std::map<const ObjectFile::Atom*, uint32_t>::iterator pos = theOrdinalOverrideMap.find(nextAtom);
+ if ( pos == theOrdinalOverrideMap.end() ) {
+ theOrdinalOverrideMap[nextAtom] = index++;
+ if (log ) fprintf(stderr, "override ordinal %u assigned to %s in cluster from %s\n", index, nextAtom->getDisplayName(), nextAtom->getFile()->getPath());
+ }
+ else {
+ if (log ) fprintf(stderr, "could not order %s as %u because it was already laid out earlier by %s as %u\n",
+ atom->getDisplayName(), index, followOnStarts[atom]->getDisplayName(), theOrdinalOverrideMap[atom] );
+ }
+ }
+ }
+ else {
+ theOrdinalOverrideMap[atom] = index;
+ if (log ) fprintf(stderr, "override ordinal %u assigned to %s from %s\n", index, atom->getDisplayName(), atom->getFile()->getPath());
+ }
+ }
+ else {
+ ++matchCount;
+ //fprintf(stderr, "can't find match for order_file entry %s/%s\n", it->objectFileName, it->symbolName);
+ }
+ ++index;
+ }
+ if ( fOptions.printOrderFileStatistics() && (fOptions.orderedSymbols().size() != matchCount) ) {
+ warning("only %u out of %lu order_file symbols were applicable", matchCount, fOptions.orderedSymbols().size() );
+ }
+ }
+
+ // sort atoms
+ std::sort(fAllAtoms.begin(), fAllAtoms.end(), Linker::AtomSorter(ordinalOverrideMap, fInitializerAtoms, fTerminatorAtoms));
+
+ //fprintf(stderr, "Sorted atoms:\n");
+ //for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) {
+ // fprintf(stderr, "\t%p, %u %s\t%s\n", (*it)->getSection(), (*it)->getSection()->getIndex(), (*it)->getDisplayName(), (*it)->getFile()->getPath());
+ //}
+}
+
+
+// make sure given addresses are within reach of branches, etc
+void Linker::tweakLayout()
+{
+ // > 2GB images need their large zero fill atoms sorted to the end to keep access with +/- 2GB
+ if ( fTotalSize > 0x7F000000 ) {
+ fBiggerThanTwoGigOutput = true;
+
+ if ( (fTotalSize-fTotalZeroFillSize) > 0x7F000000 )
+ throwf("total output size exceeds 2GB (%lldMB)", (fTotalSize-fTotalZeroFillSize)/(1024*1024));
+
+ // move very large (>1MB) zero fill atoms to a new section at very end of __DATA segment
+ Section* hugeZeroFills = Section::find("__huge", "__DATA", true);
+ for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) {
+ ObjectFile::Atom* atom = *it;
+ if ( atom->isZeroFill() && (atom->getSize() > 1024*1024) && (strcmp(atom->getSegment().getName(), "__DATA") == 0) )
+ atom->setSection(hugeZeroFills);
+ }
+ }
+
+ // move all initializers to start of __text section
+ if ( fOptions.readerOptions().fAutoOrderInitializers ) {
+ // move -init function to front of __text
+ if ( fOptions.initFunctionName() != NULL ) {
+ ObjectFile::Atom* initAtom = fGlobalSymbolTable.find(fOptions.initFunctionName());
+ if ( initAtom == NULL )
+ throwf("could not find -init function: \"%s\"", fOptions.initFunctionName());
+ moveToFrontOfSection(initAtom);
+ }
+
+ // move all functions pointed to by __mod_init_func section to front of __text
+ Section* initSection = Section::find("__mod_init_func", "__DATA", false, false);
+ if ( initSection != NULL ) {
+ for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); ++it) {
+ if ( (*it)->getSection() == initSection ) {
+ std::vector<class ObjectFile::Reference*>& references = (*it)->getReferences();
+ if ( references.size() == 1 )
+ moveToFrontOfSection(&(references[0]->getTarget()));
+ }
+ }
+ }
+ }
+
+ // move atoms with relocations to start of __DATA,__data section
+ // <rdar://problem/6061558> linker should order __DATA segment to reduce dyld dirtied pages
+ if ( fOptions.orderData() ) {
+ bool slideable = false;
+ switch ( fOptions.outputKind() ) {
+ case Options::kDynamicExecutable:
+ case Options::kStaticExecutable:
+ case Options::kDyld:
+ case Options::kPreload:
+ case Options::kObjectFile:
+ case Options::kKextBundle:
+ slideable = false;
+ break;
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ slideable = true;
+ break;
+ }
+ const bool hasPreferredLoadAddress = (fOptions.baseAddress() != 0);
+ Section* dataSection = Section::find("__data", "__DATA", false, false);
+ if ( dataSection != NULL ) {
+ for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); ++it) {
+ ObjectFile::Atom* dataAtom = *it;
+ if ( dataAtom->getSection() == dataSection ) {
+ std::vector<class ObjectFile::Reference*>& references = dataAtom->getReferences();
+ if ( references.size() > 0 ) {
+ if ( slideable && !hasPreferredLoadAddress ) {
+ // in a slidable image dyld will need to rebase and bind so any references will need runtime fixups
+ // if image has preferred base address, assume it will load there and not rebase
+ moveToFrontOfSection(dataAtom);
+ }
+ else {
+ // in a non-slideable image, dyld will only do binding, so only references to
+ // symbols in another dylib will need runtime fixups
+ //fprintf(stderr, "reference from atom %s\n", dataAtom->getDisplayName());
+ for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) {
+ ObjectFile::Reference* reference = *rit;
+ //fprintf(stderr, "\t%d %s\n", reference->getTarget().getDefinitionKind(), reference->getTarget().getDisplayName());
+ if ( (reference->getTarget().getDefinitionKind() == ObjectFile::Atom::kExternalDefinition)
+ || (reference->getTarget().getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) {
+ moveToFrontOfSection(dataAtom);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+}
+
+
+void Linker::writeDotOutput()
+{
+ const char* dotOutFilePath = fOptions.dotOutputFile();
+ if ( dotOutFilePath != NULL ) {
+ FILE* out = fopen(dotOutFilePath, "w");
+ if ( out != NULL ) {
+ // print header
+ fprintf(out, "digraph dg\n{\n");
+ fprintf(out, "\tconcentrate = true;\n");
+ fprintf(out, "\trankdir = LR;\n");
+
+ // print each atom as a node
+ for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) {
+ ObjectFile::Atom* atom = *it;
+ if ( atom->getFile() != fOutputFile ) {
+ const char* name = atom->getDisplayName();
+ if ( (atom->getDefinitionKind() == ObjectFile::Atom::kExternalDefinition)
+ || (atom->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) {
+ fprintf(out, "\taddr%p [ shape = plaintext, label = \"%s\" ];\n", atom, name);
+ }
+ else if ( strcmp(atom->getSectionName(), "__cstring") == 0 ) {
+ char cstring[atom->getSize()+2];
+ atom->copyRawContent((uint8_t*)cstring);
+ fprintf(out, "\taddr%p [ label = \"string: '", atom);
+ for (const char* s=cstring; *s != '\0'; ++s) {
+ if ( *s == '\n' )
+ fprintf(out, "\\\\n");
+ else
+ fputc(*s, out);
+ }
+ fprintf(out, "'\" ];\n");
+ }
+ else {
+ fprintf(out, "\taddr%p [ label = \"%s\" ];\n", atom, name);
+ }
+ }
+ }
+ fprintf(out, "\n");
+
+ // print each reference as an edge
+ for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) {
+ ObjectFile::Atom* fromAtom = *it;
+ if ( fromAtom->getFile() != fOutputFile ) {
+ std::vector<ObjectFile::Reference*>& references = fromAtom->getReferences();
+ std::set<ObjectFile::Atom*> seenTargets;
+ for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) {
+ ObjectFile::Reference* reference = *rit;
+ ObjectFile::Atom* toAtom = &(reference->getTarget());
+ if ( seenTargets.count(toAtom) == 0 ) {
+ seenTargets.insert(toAtom);
+ fprintf(out, "\taddr%p -> addr%p;\n", fromAtom, toAtom);
+ }
+ }
+ }
+ }
+ fprintf(out, "\n");
+
+ // push all imports to bottom of graph
+ fprintf(out, "{ rank = same; ");
+ for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) {
+ ObjectFile::Atom* atom = *it;
+ if ( atom->getFile() != fOutputFile )
+ if ( (atom->getDefinitionKind() == ObjectFile::Atom::kExternalDefinition)
+ || (atom->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) {
+ fprintf(out, "addr%p; ", atom);
+ }
+ }
+ fprintf(out, "};\n ");
+
+ // print footer
+ fprintf(out, "}\n");
+ fclose(out);
+ }
+ else {
+ warning("could not write dot output file: %s", dotOutFilePath);
+ }
+ }
+}
+
+ObjectFile::Atom* Linker::entryPoint(bool orInit)
+{
+ // if main executable, find entry point atom
+ ObjectFile::Atom* entryPoint = NULL;
+ switch ( fOptions.outputKind() ) {
+ case Options::kDynamicExecutable:
+ case Options::kStaticExecutable:
+ case Options::kDyld:
+ case Options::kPreload:
+ entryPoint = fGlobalSymbolTable.find(fOptions.entryName());
+ if ( entryPoint == NULL ) {
+ throwf("could not find entry point \"%s\" (perhaps missing crt1.o)", fOptions.entryName());
+ }
+ break;
+ case Options::kDynamicLibrary:
+ if ( orInit && (fOptions.initFunctionName() != NULL) ) {
+ entryPoint = fGlobalSymbolTable.find(fOptions.initFunctionName());
+ if ( entryPoint == NULL ) {
+ throwf("could not find -init function: \"%s\"", fOptions.initFunctionName());
+ }
+ }
+ break;
+ case Options::kObjectFile:
+ case Options::kDynamicBundle:
+ case Options::kKextBundle:
+ entryPoint = NULL;
+ break;
+ }
+ return entryPoint;
+}
+
+ObjectFile::Atom* Linker::dyldClassicHelper()
+{
+ if ( fOptions.makeClassicDyldInfo() )
+ return fGlobalSymbolTable.find("dyld_stub_binding_helper");
+ else
+ return NULL;
+}
+
+ObjectFile::Atom* Linker::dyldCompressedHelper()
+{
+ if ( fOptions.makeCompressedDyldInfo() ) {
+ // dyld_stub_binder is in libSystem.B.dylib
+ ObjectFile::Atom* atom = fGlobalSymbolTable.find("dyld_stub_binder");
+ if ( atom == NULL ) {
+ this->addJustInTimeAtoms("dyld_stub_binder", true, false, true);
+ }
+ atom = fGlobalSymbolTable.find("dyld_stub_binder");
+ return atom;
+ }
+ else
+ return NULL;
+}
+
+ObjectFile::Atom* Linker::dyldLazyLibraryHelper()
+{
+ return fGlobalSymbolTable.find("dyld_lazy_dylib_stub_binding_helper");
+}
+
+const char* Linker::assureFullPath(const char* path)
+{
+ if ( path[0] == '/' )
+ return path;
+ char cwdbuff[MAXPATHLEN];
+ if ( getcwd(cwdbuff, MAXPATHLEN) != NULL ) {
+ char* result;
+ asprintf(&result, "%s/%s", cwdbuff, path);
+ if ( result != NULL )
+ return result;
+ }
+ return path;
+}
+
+
+//
+// The stab strings are of the form:
+// <name> ':' <type-code> <number-pari>
+// 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) )
+//
+const char* Linker::truncateStabString(const char* str)
+{
+ enum { start, inObjc } state = start;
+ for (const char* s = str; *s != 0; ++s) {
+ char c = *s;
+ switch (state) {
+ case start:
+ if ( c == '[' ) {
+ state = inObjc;
+ }
+ else {
+ if ( c == ':' ) {
+ if ( s[1] == ':' ) {
+ ++s;
+ }
+ else {
+ // found colon
+ // Duplicate strndup behavior here.
+ int trunStrLen = s-str+2;
+ char* temp = new char[trunStrLen+1];
+ memcpy(temp, str, trunStrLen);
+ temp[trunStrLen] = '\0';
+ return temp;
+ }
+ }
+ }
+ break;
+ case inObjc:
+ if ( c == ']' ) {
+ state = start;
+ }
+ break;
+ }
+ }
+ // malformed
+ return str;
+}
+
+
+bool Linker::minimizeStab(ObjectFile::Reader::Stab& stab)
+{
+ switch(stab.type){
+ case N_GSYM:
+ case N_STSYM:
+ case N_LCSYM:
+ case N_FUN:
+ // these all need truncated strings
+ stab.string = truncateStabString(stab.string);
+ return true;
+ case N_SO:
+ case N_OSO:
+ case N_OPT:
+ case N_SOL:
+ // these are included in the minimal stabs, but they keep their full string
+ return true;
+ default:
+ return false;
+ }
+}
+
+
+struct HeaderRange {
+ std::vector<ObjectFile::Reader::Stab>::iterator begin;
+ std::vector<ObjectFile::Reader::Stab>::iterator end;
+ int parentRangeIndex;
+ uint32_t sum;
+ bool sumPrecomputed;
+ bool useEXCL;
+ bool cannotEXCL; // because of SLINE, etc stabs
+};
+
+
+typedef __gnu_cxx::hash_map<const char*, std::vector<uint32_t>, __gnu_cxx::hash<const char*>, CStringEquals> PathToSums;
+
+// hash table that maps header path to a vector of known checksums for that path
+static PathToSums sKnownBINCLs;
+
+
+void Linker::collectStabs(ObjectFile::Reader* reader, std::map<const class ObjectFile::Atom*, uint32_t>& atomOrdinals)
+{
+ const 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());
+ 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 ) {
+ case N_BINCL:
+ {
+ HeaderRange range;
+ range.begin = it;
+ range.end = readerStabs->end();
+ range.parentRangeIndex = curRangeIndex;
+ range.sum = it->value;
+ range.sumPrecomputed = (range.sum != 0);
+ range.useEXCL = false;
+ range.cannotEXCL = false;
+ curRangeIndex = ranges.size();
+ if ( log ) fprintf(stderr, "[%d]BINCL %s\n", curRangeIndex, it->string);
+ ranges.push_back(range);
+ }
+ break;
+ case N_EINCL:
+ if ( curRangeIndex == -1 ) {
+ warning("EINCL missing BINCL in %s", reader->getPath());
+ }
+ else {
+ ranges[curRangeIndex].end = it+1;
+ if ( log ) fprintf(stderr, "[%d->%d]EINCL %s\n", curRangeIndex, ranges[curRangeIndex].parentRangeIndex, it->string);
+ curRangeIndex = ranges[curRangeIndex].parentRangeIndex;
+ }
+ break;
+ case N_FUN:
+ {
+ std::map<const 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:
+ case N_RBRAC:
+ case N_SLINE:
+ case N_STSYM:
+ case N_LCSYM:
+ if ( curRangeIndex != -1 ) {
+ ranges[curRangeIndex].cannotEXCL = true;
+ if ( fOptions.warnStabs() )
+ warning("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 ) {
+ uint32_t sum = 0;
+ const char* s = it->string;
+ char c;
+ while ( (c = *s++) != 0 ) {
+ sum += c;
+ // don't checkusm first number (file index) after open paren in string
+ if ( c == '(' ) {
+ while(isdigit(*s))
+ ++s;
+ }
+ }
+ ranges[curRangeIndex].sum += sum;
+ }
+ }
+
+ }
+ }
+ if ( log ) fprintf(stderr, "processesed %d stabs for %s\n", count, reader->getPath());
+ if ( curRangeIndex != -1 )
+ warning("BINCL (%s) missing EINCL in %s", ranges[curRangeIndex].begin->string, reader->getPath());
+
+ // if no BINCLs
+ if ( ranges.size() == 0 ) {
+ unsigned 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 ( soIndex < soRanges.size() ) {
+ 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);
+ }
+ }
+ 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 ) {
+ const char* header = it->begin->string;
+ uint32_t sum = it->sum;
+ PathToSums::iterator pos = sKnownBINCLs.find(header);
+ if ( pos != sKnownBINCLs.end() ) {
+ std::vector<uint32_t>& sums = pos->second;
+ for(std::vector<uint32_t>::iterator sit=sums.begin(); sit != sums.end(); ++sit) {
+ if (*sit == sum) {
+ //fprintf(stderr, "use EXCL for %s in %s\n", header, reader->getPath());
+ it->useEXCL = true;
+ break;
+ }
+ }
+ if ( ! it->useEXCL ) {
+ // have seen this path, but not this checksum
+ //fprintf(stderr, "registering another checksum %08X for %s\n", sum, header);
+ sums.push_back(sum);
+ }
+ }
+ else {
+ // have not seen this path, so add to known BINCLs
+ std::vector<uint32_t> empty;
+ sKnownBINCLs[header] = empty;
+ sKnownBINCLs[header].push_back(sum);
+ //fprintf(stderr, "registering checksum %08X for %s\n", sum, header);
+ }
+ }
+ }
+
+ // add a new set of stabs with BINCL/EINCL runs that have been seen before, replaced with EXCLs
+ curRangeIndex = -1;
+ const int maxRangeIndex = ranges.size();
+ int soIndex = 0;
+ for(std::vector<ObjectFile::Reader::Stab>::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) {
+ switch ( it->type ) {
+ case N_BINCL:
+ for(int i=curRangeIndex+1; i < maxRangeIndex; ++i) {
+ if ( ranges[i].begin == it ) {
+ curRangeIndex = i;
+ HeaderRange& range = ranges[curRangeIndex];
+ ObjectFile::Reader::Stab stab = *it;
+ stab.value = range.sum; // BINCL and EXCL have n_value set to checksum
+ if ( range.useEXCL )
+ stab.type = N_EXCL; // transform BINCL into EXCL
+ if ( !minimal )
+ fStabs.push_back(stab);
+ break;
+ }
+ }
+ break;
+ case N_EINCL:
+ if ( curRangeIndex != -1 ) {
+ if ( !ranges[curRangeIndex].useEXCL && !minimal )
+ fStabs.push_back(*it);
+ curRangeIndex = ranges[curRangeIndex].parentRangeIndex;
+ }
+ break;
+ default:
+ if ( (curRangeIndex == -1) || !ranges[curRangeIndex].useEXCL ) {
+ 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);
+ }
+ }
+ }
+ }
+
+}
+
+
+// used to prune out atoms that don't need debug notes generated
+class NoDebugNoteAtom
+{
+public:
+ NoDebugNoteAtom(const std::map<class ObjectFile::Reader*, uint32_t>& readersWithDwarfOrdinals)
+ : fReadersWithDwarfOrdinals(readersWithDwarfOrdinals) {}
+
+ bool operator()(const ObjectFile::Atom* atom) const {
+ if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn )
+ return true;
+ if ( atom->getName() == NULL )
+ return true;
+ if ( fReadersWithDwarfOrdinals.find(atom->getFile()) == fReadersWithDwarfOrdinals.end() )
+ return true;
+ return false;
+ }
+
+private:
+ const std::map<class ObjectFile::Reader*, uint32_t>& fReadersWithDwarfOrdinals;
+};
+
+// used to sort atoms with debug notes
+class ReadersWithDwarfSorter
+{
+public:
+ ReadersWithDwarfSorter(const std::map<class ObjectFile::Reader*, uint32_t>& readersWithDwarfOrdinals,
+ const std::map<const class ObjectFile::Atom*, uint32_t>& atomOrdinals)
+ : fReadersWithDwarfOrdinals(readersWithDwarfOrdinals), fAtomOrdinals(atomOrdinals) {}
+
+ bool operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right) const
+ {
+ // first sort by reader
+ unsigned int leftReaderIndex = fReadersWithDwarfOrdinals.find(left->getFile())->second;
+ unsigned int rightReaderIndex = fReadersWithDwarfOrdinals.find(right->getFile())->second;
+ if ( leftReaderIndex != rightReaderIndex )
+ return (leftReaderIndex < rightReaderIndex);
+
+ // then sort by atom ordinal
+ unsigned int leftAtomIndex = fAtomOrdinals.find(left)->second;
+ unsigned int rightAtomIndex = fAtomOrdinals.find(right)->second;
+ return leftAtomIndex < rightAtomIndex;
+ }
+
+private:
+ const std::map<class ObjectFile::Reader*, uint32_t>& fReadersWithDwarfOrdinals;
+ const std::map<const class ObjectFile::Atom*, uint32_t>& fAtomOrdinals;
+};
+
+
+
+
+
+void Linker::synthesizeDebugNotes(std::vector<class ObjectFile::Atom*>& allAtomsByReader)
+{
+ // synthesize "debug notes" and add them to master stabs vector
+ const char* dirPath = NULL;
+ const char* filename = NULL;
+ bool wroteStartSO = false;
+ bool useZeroOSOModTime = (getenv("RC_RELEASE") != NULL);
+ __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> seenFiles;
+ for (std::vector<ObjectFile::Atom*>::iterator it=allAtomsByReader.begin(); it != allAtomsByReader.end(); it++) {
+ ObjectFile::Atom* atom = *it;
+ const char* newDirPath;
+ const char* newFilename;
+ //fprintf(stderr, "debug note for %s\n", atom->getDisplayName());
+ if ( atom->getTranslationUnitSource(&newDirPath, &newFilename) ) {
+ // need SO's whenever the translation unit source file changes
+ if ( newFilename != filename ) {
+ // 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);
+ if ( filename != NULL ) {
+ // translation unit change, emit ending SO
+ ObjectFile::Reader::Stab endFileStab;
+ endFileStab.atom = NULL;
+ endFileStab.type = N_SO;
+ endFileStab.other = 1;
+ endFileStab.desc = 0;
+ endFileStab.value = 0;
+ endFileStab.string = "";
+ fStabs.push_back(endFileStab);
+ }
+ // new translation unit, emit start SO's
+ ObjectFile::Reader::Stab dirPathStab;
+ dirPathStab.atom = NULL;
+ dirPathStab.type = N_SO;
+ dirPathStab.other = 0;
+ dirPathStab.desc = 0;
+ dirPathStab.value = 0;
+ dirPathStab.string = newDirPath;
+ fStabs.push_back(dirPathStab);
+ ObjectFile::Reader::Stab fileStab;
+ fileStab.atom = NULL;
+ fileStab.type = N_SO;
+ fileStab.other = 0;
+ fileStab.desc = 0;
+ fileStab.value = 0;
+ fileStab.string = newFilename;
+ fStabs.push_back(fileStab);
+ // Synthesize OSO for start of file
+ ObjectFile::Reader::Stab objStab;
+ objStab.atom = NULL;
+ objStab.type = N_OSO;
+ // <rdar://problem/6337329> linker should put cpusubtype in n_sect field of nlist entry for N_OSO debug note entries
+ objStab.other = atom->getFile()->updateCpuConstraint(0);
+ objStab.desc = 1;
+ objStab.value = useZeroOSOModTime ? 0 : atom->getFile()->getModificationTime();
+ objStab.string = assureFullPath(atom->getFile()->getPath());
+ fStabs.push_back(objStab);
+ wroteStartSO = true;
+ // add the source file path to seenFiles so it does not show up in SOLs
+ seenFiles.insert(newFilename);
+ }
+ filename = newFilename;
+ dirPath = newDirPath;
+ if ( atom->getSegment().isContentExecutable() && (strncmp(atom->getSectionName(), "__text", 6) == 0) ) {
+ // Synthesize BNSYM and start FUN stabs
+ ObjectFile::Reader::Stab beginSym;
+ beginSym.atom = atom;
+ beginSym.type = N_BNSYM;
+ beginSym.other = 1;
+ beginSym.desc = 0;
+ beginSym.value = 0;
+ beginSym.string = "";
+ fStabs.push_back(beginSym);
+ ObjectFile::Reader::Stab startFun;
+ startFun.atom = atom;
+ startFun.type = N_FUN;
+ startFun.other = 1;
+ startFun.desc = 0;
+ startFun.value = 0;
+ startFun.string = atom->getName();
+ fStabs.push_back(startFun);
+ // Synthesize any SOL stabs needed
+ std::vector<ObjectFile::LineInfo>* lineInfo = atom->getLineInfo();
+ if ( lineInfo != NULL ) {
+ const char* curFile = NULL;
+ for (std::vector<ObjectFile::LineInfo>::iterator it = lineInfo->begin(); it != lineInfo->end(); ++it) {
+ if ( it->fileName != curFile ) {
+ if ( seenFiles.count(it->fileName) == 0 ) {
+ seenFiles.insert(it->fileName);
+ ObjectFile::Reader::Stab sol;
+ sol.atom = 0;
+ sol.type = N_SOL;
+ sol.other = 0;
+ sol.desc = 0;
+ sol.value = 0;
+ sol.string = it->fileName;
+ fStabs.push_back(sol);
+ }
+ curFile = it->fileName;
+ }
+ }
+ }
+ // Synthesize end FUN and ENSYM stabs
+ ObjectFile::Reader::Stab endFun;
+ endFun.atom = atom;
+ endFun.type = N_FUN;
+ endFun.other = 0;
+ endFun.desc = 0;
+ endFun.value = 0;
+ endFun.string = "";
+ fStabs.push_back(endFun);
+ ObjectFile::Reader::Stab endSym;
+ endSym.atom = atom;
+ endSym.type = N_ENSYM;
+ endSym.other = 1;
+ endSym.desc = 0;
+ endSym.value = 0;
+ endSym.string = "";
+ fStabs.push_back(endSym);
+ }
+ else if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn ) {
+ // no stabs for atoms that would not be in the symbol table
+ }
+ else if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAsAbsolute ) {
+ // no stabs for absolute symbols
+ }
+ else if ( (strcmp(atom->getSectionName(), "__eh_frame") == 0) ) {
+ // no stabs for .eh atoms
+ }
+ else if ( (strncmp(atom->getName(), "__dtrace_probe$", 15) == 0) ) {
+ // no stabs for old style dtrace probes
+ }
+ else {
+ ObjectFile::Reader::Stab globalsStab;
+ const char* name = atom->getName();
+ if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) {
+ // Synthesize STSYM stab for statics
+ globalsStab.atom = atom;
+ globalsStab.type = N_STSYM;
+ globalsStab.other = 1;
+ globalsStab.desc = 0;
+ globalsStab.value = 0;
+ globalsStab.string = name;
+ fStabs.push_back(globalsStab);
+ }
+ else {
+ // Synthesize GSYM stab for other globals
+ globalsStab.atom = atom;
+ globalsStab.type = N_GSYM;
+ globalsStab.other = 1;
+ globalsStab.desc = 0;
+ globalsStab.value = 0;
+ globalsStab.string = name;
+ fStabs.push_back(globalsStab);
+ }
+ }
+ }
+ }
+
+ if ( wroteStartSO ) {
+ // emit ending SO
+ ObjectFile::Reader::Stab endFileStab;
+ endFileStab.atom = NULL;
+ endFileStab.type = N_SO;
+ endFileStab.other = 1;
+ endFileStab.desc = 0;
+ endFileStab.value = 0;
+ endFileStab.string = "";
+ fStabs.push_back(endFileStab);
+ }
+}
+
+
+
+
+void Linker::collectDebugInfo()
+{
+ std::map<const class ObjectFile::Atom*, uint32_t> atomOrdinals;
+ fStartDebugTime = mach_absolute_time();
+ if ( fOptions.readerOptions().fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone ) {
+
+ // determine mixture of stabs and dwarf
+ bool someStabs = false;
+ bool someDwarf = false;
+ for (std::vector<class ObjectFile::Reader*>::iterator it=fReadersThatHaveSuppliedAtoms.begin();
+ it != fReadersThatHaveSuppliedAtoms.end();
+ it++) {
+ ObjectFile::Reader* reader = *it;
+ if ( reader != NULL ) {
+ switch ( reader->getDebugInfoKind() ) {
+ case ObjectFile::Reader::kDebugInfoNone:
+ break;
+ case ObjectFile::Reader::kDebugInfoStabs:
+ someStabs = true;
+ break;
+ case ObjectFile::Reader::kDebugInfoDwarf:
+ someDwarf = true;
+ fCreateUUID = true;
+ break;
+ case ObjectFile::Reader::kDebugInfoStabsUUID:
+ someStabs = true;
+ fCreateUUID = true;
+ break;
+ default:
+ throw "Unhandled type of debug information";
+ }
+ }
+ }
+
+ if ( someDwarf || someStabs ) {
+ // try to minimize re-allocations
+ fStabs.reserve(1024);
+
+ // make mapping from atoms to ordinal
+ uint32_t ordinal = 1;
+ for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) {
+ atomOrdinals[*it] = ordinal++;
+ }
+ }
+
+ // process all dwarf .o files as a batch
+ if ( someDwarf ) {
+ // make mapping from readers with dwarf to ordinal
+ std::map<class ObjectFile::Reader*, uint32_t> readersWithDwarfOrdinals;
+ uint32_t readerOrdinal = 1;
+ for (std::vector<class ObjectFile::Reader*>::iterator it=fReadersThatHaveSuppliedAtoms.begin();
+ it != fReadersThatHaveSuppliedAtoms.end();
+ it++) {
+ ObjectFile::Reader* reader = *it;
+ if ( (reader != NULL) && (reader->getDebugInfoKind() == ObjectFile::Reader::kDebugInfoDwarf) ) {
+ readersWithDwarfOrdinals[reader] = readerOrdinal++;
+ }
+ }
+
+ // make a vector of atoms
+ std::vector<class ObjectFile::Atom*> allAtomsByReader(fAllAtoms.begin(), fAllAtoms.end());
+ // remove those not from a reader that has dwarf
+ allAtomsByReader.erase(std::remove_if(allAtomsByReader.begin(), allAtomsByReader.end(),
+ NoDebugNoteAtom(readersWithDwarfOrdinals)), allAtomsByReader.end());
+ // sort by reader then atom ordinal
+ std::sort(allAtomsByReader.begin(), allAtomsByReader.end(), ReadersWithDwarfSorter(readersWithDwarfOrdinals, atomOrdinals));
+ // add debug notes for each atom
+ this->synthesizeDebugNotes(allAtomsByReader);
+ }
+
+ // process all stabs .o files one by one
+ if ( someStabs ) {
+ // get stabs from each reader, in command line order
+ for (std::vector<class ObjectFile::Reader*>::iterator it=fReadersThatHaveSuppliedAtoms.begin();
+ it != fReadersThatHaveSuppliedAtoms.end();
+ it++) {
+ ObjectFile::Reader* reader = *it;
+ if ( reader != NULL ) {
+ switch ( reader->getDebugInfoKind() ) {
+ case ObjectFile::Reader::kDebugInfoDwarf:
+ case ObjectFile::Reader::kDebugInfoNone:
+ // do nothing
+ break;
+ case ObjectFile::Reader::kDebugInfoStabs:
+ case ObjectFile::Reader::kDebugInfoStabsUUID:
+ collectStabs(reader, atomOrdinals);
+ break;
+ default:
+ throw "Unhandled type of debug information";
+ }
+ }
+ }
+ // remove stabs associated with atoms that won't be in output
+ std::set<class ObjectFile::Atom*> allAtomsSet;
+ allAtomsSet.insert(fAllAtoms.begin(), fAllAtoms.end());
+ fStabs.erase(std::remove_if(fStabs.begin(), fStabs.end(), NotInSet(allAtomsSet)), fStabs.end());
+ }
+ }
+}
+
+void Linker::writeOutput()
+{
+ if ( fOptions.forceCpuSubtypeAll() )
+ fCurrentCpuConstraint = ObjectFile::Reader::kCpuAny;
+
+ fStartWriteTime = mach_absolute_time();
+ // tell writer about each segment's atoms
+ fOutputFileSize = fOutputFile->write(fAllAtoms, fStabs, this->entryPoint(true),
+ this->dyldClassicHelper(),this->dyldCompressedHelper(), this->dyldLazyLibraryHelper(),
+ fCreateUUID, fCanScatter,
+ fCurrentCpuConstraint, fBiggerThanTwoGigOutput,
+ fRegularDefAtomsThatOverrideADylibsWeakDef,
+ fGlobalSymbolTable.hasExternalWeakDefinitions());
+}
+
+ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info)
+{
+ // map in whole file
+ uint64_t len = info.fileLen;
+ int fd = ::open(info.path, O_RDONLY, 0);
+ if ( fd == -1 )
+ throwf("can't open file, errno=%d", errno);
+ if ( info.fileLen < 20 )
+ throw "file too small";
+
+ uint8_t* p = (uint8_t*)::mmap(NULL, info.fileLen, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
+ if ( p == (uint8_t*)(-1) )
+ throwf("can't map file, errno=%d", errno);
+
+ // if fat file, skip to architecture we want
+ // Note: fat header is always big-endian
+ const fat_header* fh = (fat_header*)p;
+ if ( fh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
+ const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header));
+ uint32_t sliceToUse;
+ bool sliceFound = false;
+ if ( fOptions.preferSubArchitecture() ) {
+ // first try to find a slice that match cpu-type and cpu-sub-type
+ for (uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) {
+ if ( (OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)fArchitecture)
+ && (OSSwapBigToHostInt32(archs[i].cpusubtype) == (uint32_t)fOptions.subArchitecture()) ) {
+ sliceToUse = i;
+ sliceFound = true;
+ break;
+ }
+ }
+ }
+ if ( !sliceFound ) {
+ // look for any slice that matches just cpu-type
+ for (uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) {
+ if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)fArchitecture ) {
+ sliceToUse = i;
+ sliceFound = true;
+ break;
+ }
+ }
+ }
+ if ( sliceFound ) {
+ uint32_t fileOffset = OSSwapBigToHostInt32(archs[sliceToUse].offset);
+ len = OSSwapBigToHostInt32(archs[sliceToUse].size);
+ // if requested architecture is page aligned within fat file, then remap just that portion of file
+ if ( (fileOffset & 0x00000FFF) == 0 ) {
+ // unmap whole file
+ munmap((caddr_t)p, info.fileLen);
+ // re-map just part we need
+ p = (uint8_t*)::mmap(NULL, len, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, fileOffset);
+ if ( p == (uint8_t*)(-1) )
+ throwf("can't re-map file, errno=%d", errno);
+ }
+ else {
+ p = &p[fileOffset];
+ }
+ }
+ }
+ ::close(fd);
+
+ bool objSubtypeMustMatch = (fOptions.preferSubArchitecture() && !fOptions.allowSubArchitectureMismatches());
+ switch (fArchitecture) {
+ case CPU_TYPE_POWERPC:
+ if ( mach_o::relocatable::Reader<ppc>::validFile(p) )
+ return this->addObject(new mach_o::relocatable::Reader<ppc>::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len);
+ else if ( mach_o::dylib::Reader<ppc>::validFile(p, info.options.fBundleLoader) )
+ return this->addDylib(new mach_o::dylib::Reader<ppc>::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len);
+ else if ( archive::Reader<ppc>::validFile(p, len) )
+ return this->addArchive(new archive::Reader<ppc>::Reader(p, len, info.path, info.modTime, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len);
+ break;
+ case CPU_TYPE_POWERPC64:
+ if ( mach_o::relocatable::Reader<ppc64>::validFile(p) )
+ return this->addObject(new mach_o::relocatable::Reader<ppc64>::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len);
+ else if ( mach_o::dylib::Reader<ppc64>::validFile(p, info.options.fBundleLoader) )
+ return this->addDylib(new mach_o::dylib::Reader<ppc64>::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len);
+ else if ( archive::Reader<ppc64>::validFile(p, len) )
+ return this->addArchive(new archive::Reader<ppc64>::Reader(p, len, info.path, info.modTime, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len);
+ break;
+ case CPU_TYPE_I386:
+ if ( mach_o::relocatable::Reader<x86>::validFile(p) )
+ return this->addObject(new mach_o::relocatable::Reader<x86>::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len);
+ else if ( mach_o::dylib::Reader<x86>::validFile(p, info.options.fBundleLoader) )
+ return this->addDylib(new mach_o::dylib::Reader<x86>::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len);
+ else if ( archive::Reader<x86>::validFile(p, len) )
+ return this->addArchive(new archive::Reader<x86>::Reader(p, len, info.path, info.modTime, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len);
+ break;
+ case CPU_TYPE_X86_64:
+ if ( mach_o::relocatable::Reader<x86_64>::validFile(p) )
+ return this->addObject(new mach_o::relocatable::Reader<x86_64>::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len);
+ else if ( mach_o::dylib::Reader<x86_64>::validFile(p, info.options.fBundleLoader) )
+ return this->addDylib(new mach_o::dylib::Reader<x86_64>::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len);
+ else if ( archive::Reader<x86_64>::validFile(p, len) )
+ return this->addArchive(new archive::Reader<x86_64>::Reader(p, len, info.path, info.modTime, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len);
+ case CPU_TYPE_ARM:
+ if ( mach_o::relocatable::Reader<arm>::validFile(p, objSubtypeMustMatch, fOptions.subArchitecture()) )
+ return this->addObject(new mach_o::relocatable::Reader<arm>::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len);
+ else if ( mach_o::dylib::Reader<arm>::validFile(p, info.options.fBundleLoader) )
+ return this->addDylib(new mach_o::dylib::Reader<arm>::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len);
+ else if ( archive::Reader<arm>::validFile(p, len) )
+ return this->addArchive(new archive::Reader<arm>::Reader(p, len, info.path, info.modTime, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len);
+ break;
+ }
+
+#if LTO_SUPPORT
+ if ( lto::Reader::validFile(p, len, fArchitecture) ) {
+ return this->addObject(new lto::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fArchitecture), info, len);
+ }
+ else if ( !lto::Reader::loaded() && (p[0] == 'B') && (p[1] == 'C') ) {
+ throw "could not process object file. Looks like an llvm bitcode object file, but libLTO.dylib could not be loaded";
+ }
+#endif
+ // error handling
+ if ( ((fat_header*)p)->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
+ throwf("missing required architecture %s in file", fArchitectureName);
+ }
+ else {
+ throw "file is not of required architecture";
+ }
+}
+
+void Linker::logDylib(ObjectFile::Reader* reader, bool indirect)
+{
+ if ( fOptions.readerOptions().fTraceDylibs ) {
+ const char* fullPath = reader->getPath();
+ char realName[MAXPATHLEN];
+ if ( realpath(fullPath, realName) != NULL )
+ fullPath = realName;
+ if ( indirect )
+ logTraceInfo("[Logging for XBS] Used indirect dynamic library: %s\n", fullPath);
+ else
+ logTraceInfo("[Logging for XBS] Used dynamic library: %s\n", fullPath);
+ }
+}
+
+
+
+ObjectFile::Reader* Linker::findDylib(const char* installPath, const char* fromPath)
+{
+ //fprintf(stderr, "findDylib(%s, %s)\n", installPath, fromPath);
+ InstallNameToReader::iterator pos = fDylibMap.find(installPath);
+ if ( pos != fDylibMap.end() ) {
+ return pos->second;
+ }
+ else {
+ // allow -dylib_path option to override indirect library to use
+ for (std::vector<Options::DylibOverride>::iterator dit = fOptions.dylibOverrides().begin(); dit != fOptions.dylibOverrides().end(); ++dit) {
+ if ( strcmp(dit->installName,installPath) == 0 ) {\
+ try {
+ Options::FileInfo info = fOptions.findFile(dit->useInstead);
+ ObjectFile::Reader* reader = this->createReader(info);
+ fDylibMap[strdup(installPath)] = reader;
+ this->logDylib(reader, true);
+ return reader;
+ }
+ catch (const char* msg) {
+ warning("ignoring -dylib_file option, %s", msg);
+ }
+ }
+ }
+ char newPath[MAXPATHLEN];
+ // handle @loader_path
+ if ( strncmp(installPath, "@loader_path/", 13) == 0 ) {
+ strcpy(newPath, fromPath);
+ char* addPoint = strrchr(newPath,'/');
+ if ( addPoint != NULL )
+ strcpy(&addPoint[1], &installPath[13]);
+ else
+ strcpy(newPath, &installPath[13]);
+ installPath = newPath;
+ }
+ // note: @executable_path case is handled inside findFileUsingPaths()
+ // search for dylib using -F and -L paths
+ Options::FileInfo info = fOptions.findFileUsingPaths(installPath);
+ try {
+ ObjectFile::Reader* reader = this->createReader(info);
+ fDylibMap[strdup(installPath)] = reader;
+ this->logDylib(reader, true);
+ return reader;
+ }
+ catch (const char* msg) {
+ throwf("in %s, %s", info.path, msg);
+ }
+ }
+}
+
+
+void Linker::processDylibs()
+{
+ fAllDirectDylibsLoaded = true;
+
+ // mark all dylibs initially specified as required and check if they can be used
+ for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) {
+ it->second->setExplicitlyLinked();
+ this->checkDylibClientRestrictions(it->second);
+ }
+
+ // keep processing dylibs until no more dylibs are added
+ unsigned long lastMapSize = 0;
+ while ( lastMapSize != fDylibMap.size() ) {
+ lastMapSize = fDylibMap.size();
+ // can't iterator fDylibMap while modifying it, so use temp buffer
+ std::vector<ObjectFile::Reader*> currentUnprocessedReaders;
+ for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) {
+ if ( fDylibsProcessed.count(it->second) == 0 )
+ currentUnprocessedReaders.push_back(it->second);
+ }
+ for (std::vector<ObjectFile::Reader*>::iterator it=currentUnprocessedReaders.begin(); it != currentUnprocessedReaders.end(); it++) {
+ fDylibsProcessed.insert(*it);
+ (*it)->processIndirectLibraries(this);
+ }
+ }
+
+ // go back over original dylibs and mark sub frameworks as re-exported
+ if ( fOptions.outputKind() == Options::kDynamicLibrary ) {
+ const char* myLeaf = strrchr(fOptions.installPath(), '/');
+ if ( myLeaf != NULL ) {
+ for (std::vector<class ObjectFile::Reader*>::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) {
+ ObjectFile::Reader* reader = *it;
+ const char* childParent = reader->parentUmbrella();
+ if ( childParent != NULL ) {
+ if ( strcmp(childParent, &myLeaf[1]) == 0 ) {
+ // set re-export bit of info
+ std::map<ObjectFile::Reader*,LibraryOptions>::iterator pos = fDylibOptionsMap.find(reader);
+ if ( pos != fDylibOptionsMap.end() ) {
+ pos->second.fReExport = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+}
+
+
+
+void Linker::createReaders()
+{
+ fStartCreateReadersTime = mach_absolute_time();
+ std::vector<Options::FileInfo>& files = fOptions.getInputFiles();
+ const int count = files.size();
+ if ( count == 0 )
+ throw "no object files specified";
+ // add all direct object, archives, and dylibs
+ for (int i=0; i < count; ++i) {
+ Options::FileInfo& entry = files[i];
+ // ignore /usr/lib/dyld on command line in crt.o build
+ if ( strcmp(entry.path, "/usr/lib/dyld") != 0 ) {
+ try {
+ this->addInputFile(this->createReader(entry), entry);
+ }
+ catch (const char* msg) {
+ if ( (strstr(msg, "architecture") != NULL) && !fOptions.errorOnOtherArchFiles() ) {
+ if ( fOptions.ignoreOtherArchInputFiles() ) {
+ // ignore, because this is about an architecture not in use
+ }
+ else {
+ warning("in %s, %s", entry.path, msg);
+ }
+ }
+ else {
+ throwf("in %s, %s", entry.path, msg);
+ }
+ }
+ }
+ }
+
+ this->processDylibs();
+}
+
+
+
+ObjectFile::Reader* Linker::addArchive(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen)
+{
+ fNextInputOrdinal += mappedLen;
+ // remember which readers are archives because they are logged differently
+ fArchiveReaders.insert(reader);
+
+ // update stats
+ fTotalArchiveSize += mappedLen;
+ ++fTotalArchivesLoaded;
+ return reader;
+}
+
+ObjectFile::Reader* Linker::addObject(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen)
+{
+ fNextInputOrdinal += mappedLen;
+ // any .o files that don't have MH_SUBSECTIONS_VIA_SYMBOLS, that means a generated .o file can't
+ if ( (fOptions.outputKind() == Options::kObjectFile) && !reader->canScatterAtoms() )
+ fCanScatter = false;
+
+ // update stats
+ fTotalObjectSize += mappedLen;
+ ++fTotalObjectLoaded;
+ return reader;
+}
+
+
+void Linker::checkDylibClientRestrictions(ObjectFile::Reader* reader)
+{
+ // Check for any restrictions on who can link with this dylib
+ const char* readerParentName = reader->parentUmbrella() ;
+ std::vector<const char*>* clients = reader->getAllowableClients();
+ if ( (readerParentName != NULL) || (clients != NULL) ) {
+ // only dylibs that are in an umbrella or have a client list need verification
+ const char* installName = fOptions.installPath();
+ const char* installNameLastSlash = strrchr(installName, '/');
+ bool isParent = false;
+ bool isSibling = false;
+ bool isAllowableClient = false;
+ // There are three cases:
+ if ( (readerParentName != NULL) && (installNameLastSlash != NULL) ) {
+ // case 1) The dylib has a parent umbrella, and we are creating the parent umbrella
+ isParent = ( strcmp(&installNameLastSlash[1], readerParentName) == 0 );
+
+ // hack to support umbrella variants that encode the variant name in the install name
+ // e.g. CoreServices_profile
+ if ( !isParent ) {
+ const char* underscore = strchr(&installNameLastSlash[1], '_');
+ if ( underscore != NULL ) {
+ isParent = ( strncmp(&installNameLastSlash[1], readerParentName, underscore-installNameLastSlash-1) == 0 );
+ }
+ }
+
+ // case 2) The dylib has a parent umbrella, and we are creating a sibling with the same parent
+ isSibling = ( (fOptions.umbrellaName() != NULL) && (strcmp(fOptions.umbrellaName(), readerParentName) == 0) );
+ }
+
+ if ( !isParent && !isSibling && (clients != NULL) ) {
+ // case 3) the dylib has a list of allowable clients, and we are creating one of them
+ const char* clientName = fOptions.clientName();
+ int clientNameLen = 0;
+ if ( clientName != NULL ) {
+ // use client name as specified on command line
+ clientNameLen = strlen(clientName);
+ }
+ else {
+ // infer client name from output path (e.g. xxx/libfoo_variant.A.dylib --> foo, Bar.framework/Bar_variant --> Bar)
+ clientName = installName;
+ clientNameLen = strlen(clientName);
+ // starts after last slash
+ if ( installNameLastSlash != NULL )
+ clientName = &installNameLastSlash[1];
+ if ( strncmp(clientName, "lib", 3) == 0 )
+ clientName = &clientName[3];
+ // up to first dot
+ const char* firstDot = strchr(clientName, '.');
+ if ( firstDot != NULL )
+ clientNameLen = firstDot - clientName;
+ // up to first underscore
+ const char* firstUnderscore = strchr(clientName, '_');
+ if ( (firstUnderscore != NULL) && ((firstUnderscore - clientName) < clientNameLen) )
+ clientNameLen = firstUnderscore - 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 )
+ isAllowableClient = true;
+ }
+ }
+
+ if ( !isParent && !isSibling && !isAllowableClient ) {
+ if ( readerParentName != NULL ) {
+ throwf("cannot link directly with %s. Link against the umbrella framework '%s.framework' instead.",
+ reader->getPath(), readerParentName);
+ }
+ else {
+ throwf("cannot link directly with %s", reader->getPath());
+ }
+ }
+ }
+}
+
+
+ObjectFile::Reader* Linker::addDylib(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen)
+{
+ switch ( fOptions.outputKind() ) {
+ case Options::kDynamicExecutable:
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ break;
+ case Options::kStaticExecutable:
+ case Options::kDyld:
+ case Options::kPreload:
+ case Options::kObjectFile:
+ case Options::kKextBundle:
+ warning("unexpected dylib (%s) on link line", reader->getPath());
+ break;
+ }
+
+ fNextInputOrdinal += mappedLen;
+ if ( (reader->getInstallPath() == NULL) && !info.options.fBundleLoader ) {
+ // this is a "blank" stub
+ // silently ignore it
+ return reader;
+ }
+ // add to map of loaded dylibs
+ const char* installPath = reader->getInstallPath();
+ if ( installPath != NULL ) {
+ InstallNameToReader::iterator pos = fDylibMap.find(installPath);
+ if ( pos == fDylibMap.end() ) {
+ fDylibMap[strdup(installPath)] = reader;
+ }
+ else {
+ InstallNameToReader::iterator pos2 = fDylibMap.find(reader->getPath());
+ if ( pos2 == fDylibMap.end() )
+ fDylibMap[strdup(reader->getPath())] = reader;
+ else
+ warning("duplicate dylib %s", reader->getPath());
+ }
+ }
+ else if ( info.options.fBundleLoader )
+ fBundleLoaderReader = reader;
+
+ // log direct readers
+ if ( !fAllDirectDylibsLoaded )
+ this->logDylib(reader, false);
+
+ // update stats
+ ++fTotalDylibsLoaded;
+
+ return reader;
+}
+
+
+void Linker::logTraceInfo (const char* format, ...)
+{
+ static int trace_file = -1;
+ char trace_buffer[MAXPATHLEN * 2];
+ char *buffer_ptr;
+ int length;
+ ssize_t amount_written;
+ const char *trace_file_path = fOptions.readerOptions().fTraceOutputFile;
+
+ if(trace_file == -1) {
+ if(trace_file_path != NULL) {
+ trace_file = open(trace_file_path, O_WRONLY | O_APPEND | O_CREAT, 0666);
+ if(trace_file == -1)
+ throwf("Could not open or create trace file: %s", trace_file_path);
+ }
+ else {
+ trace_file = fileno(stderr);
+ }
+ }
+
+ va_list ap;
+ va_start(ap, format);
+ length = vsnprintf(trace_buffer, sizeof(trace_buffer), format, ap);
+ va_end(ap);
+ buffer_ptr = trace_buffer;
+
+ while(length > 0) {
+ amount_written = write(trace_file, buffer_ptr, length);
+ if(amount_written == -1)
+ /* Failure to write shouldn't fail the build. */
+ return;
+ buffer_ptr += amount_written;
+ length -= amount_written;
+ }
+}
+
+
+
+void Linker::createWriter()
+{
+ fStartCreateWriterTime = mach_absolute_time();
+
+ // make a vector out of all required dylibs in fDylibMap
+ std::vector<ExecutableFile::DyLibUsed> dynamicLibraries;
+ // need to preserve command line order
+ for (std::vector<class ObjectFile::Reader*>::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) {
+ ObjectFile::Reader* reader = *it;
+ for (InstallNameToReader::iterator mit=fDylibMap.begin(); mit != fDylibMap.end(); mit++) {
+ if ( reader == mit->second ) {
+ ExecutableFile::DyLibUsed dylibInfo;
+ dylibInfo.reader = reader;
+ dylibInfo.options = fDylibOptionsMap[reader];
+ dynamicLibraries.push_back(dylibInfo);
+ break;
+ }
+ }
+ }
+ // then add any other dylibs
+ for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) {
+ if ( it->second->implicitlyLinked() ) {
+ // if not already in dynamicLibraries
+ bool alreadyInDynamicLibraries = false;
+ for (std::vector<ExecutableFile::DyLibUsed>::iterator dit=dynamicLibraries.begin(); dit != dynamicLibraries.end(); dit++) {
+ if ( dit->reader == it->second ) {
+ alreadyInDynamicLibraries = true;
+ break;
+ }
+ }
+ if ( ! alreadyInDynamicLibraries ) {
+ ExecutableFile::DyLibUsed dylibInfo;
+ dylibInfo.reader = it->second;
+ std::map<ObjectFile::Reader*,LibraryOptions>::iterator pos = fDylibOptionsMap.find(it->second);
+ if ( pos != fDylibOptionsMap.end() ) {
+ dylibInfo.options = pos->second;
+ }
+ else {
+ dylibInfo.options.fWeakImport = false; // FIX ME
+ dylibInfo.options.fReExport = false;
+ dylibInfo.options.fBundleLoader = false;
+ }
+ dynamicLibraries.push_back(dylibInfo);
+ }
+ }
+ }
+ if ( fBundleLoaderReader != NULL ) {
+ ExecutableFile::DyLibUsed dylibInfo;
+ dylibInfo.reader = fBundleLoaderReader;
+ dylibInfo.options.fWeakImport = false;
+ dylibInfo.options.fReExport = false;
+ dylibInfo.options.fBundleLoader = true;
+ dynamicLibraries.push_back(dylibInfo);
+ }
+
+ const char* path = fOptions.getOutputFilePath();
+ switch ( fArchitecture ) {
+ case CPU_TYPE_POWERPC:
+ this->setOutputFile(new mach_o::executable::Writer<ppc>(path, fOptions, dynamicLibraries));
+ break;
+ case CPU_TYPE_POWERPC64:
+ this->setOutputFile(new mach_o::executable::Writer<ppc64>(path, fOptions, dynamicLibraries));
+ break;
+ case CPU_TYPE_I386:
+ this->setOutputFile(new mach_o::executable::Writer<x86>(path, fOptions, dynamicLibraries));
+ break;
+ case CPU_TYPE_X86_64:
+ this->setOutputFile(new mach_o::executable::Writer<x86_64>(path, fOptions, dynamicLibraries));
+ break;
+ case CPU_TYPE_ARM:
+ this->setOutputFile(new mach_o::executable::Writer<arm>(path, fOptions, dynamicLibraries));
+ break;
+ default:
+ throw "unknown architecture";
+ }
+}
+
+
+Linker::SymbolTable::SymbolTable(Linker& owner)
+ : fOwner(owner), fRequireCount(0), fHasExternalTentativeDefinitions(false), fHasExternalWeakDefinitions(false)
+{
+}
+
+void Linker::SymbolTable::require(const char* name)
+{
+ //fprintf(stderr, "require(%s)\n", name);
+ Mapper::iterator pos = fTable.find(name);
+ if ( pos == fTable.end() ) {
+ fTable[name] = NULL;
+ ++fRequireCount;
+ }
+}
+
+// convenience labels for 2-dimensional switch statement
+enum AllDefinitionCombinations {
+ kRegAndReg = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kRegularDefinition,
+ kRegAndWeak = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kWeakDefinition,
+ kRegAndTent = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kTentativeDefinition,
+ kRegAndExtern = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kExternalDefinition,
+ kRegAndExternWeak = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition,
+ kRegAndAbsolute = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol,
+ kWeakAndReg = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kRegularDefinition,
+ kWeakAndWeak = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kWeakDefinition,
+ kWeakAndTent = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kTentativeDefinition,
+ kWeakAndExtern = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kExternalDefinition,
+ kWeakAndExternWeak = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition,
+ kWeakAndAbsolute = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol,
+ kTentAndReg = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kRegularDefinition,
+ kTentAndWeak = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kWeakDefinition,
+ kTentAndTent = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kTentativeDefinition,
+ kTentAndExtern = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kExternalDefinition,
+ kTentAndExternWeak = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition,
+ kTentAndAbsolute = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol,
+ kExternAndReg = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kRegularDefinition,
+ kExternAndWeak = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kWeakDefinition,
+ kExternAndTent = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kTentativeDefinition,
+ kExternAndExtern = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kExternalDefinition,
+ kExternAndExternWeak = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition,
+ kExternAndAbsolute = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol,
+ kExternWeakAndReg = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kRegularDefinition,
+ kExternWeakAndWeak = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kWeakDefinition,
+ kExternWeakAndTent = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kTentativeDefinition,
+ kExternWeakAndExtern = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kExternalDefinition,
+ kExternWeakAndExternWeak= (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition,
+ kExternWeakAndAbsolute = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol,
+ kAbsoluteAndReg = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kRegularDefinition,
+ kAbsoluteAndWeak = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kWeakDefinition,
+ kAbsoluteAndTent = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kTentativeDefinition,
+ kAbsoluteAndExtern = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kExternalDefinition,
+ kAbsoluteAndExternWeak = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kExternalWeakDefinition,
+ kAbsoluteAndAbsolute = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kAbsoluteSymbol
+};
+
+bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom)
+{
+ bool useNew = true;
+ bool checkVisibilityMismatch = false;
+ const char* name = newAtom.getName();
+ //fprintf(stderr, "map.add(%s => %p from %s)\n", name, &newAtom, newAtom.getFile()->getPath());
+ Mapper::iterator pos = fTable.find(name);
+ ObjectFile::Atom* existingAtom = NULL;
+ if ( pos != fTable.end() )
+ existingAtom = pos->second;
+ if ( existingAtom != NULL ) {
+ // already have atom with same name in symbol table
+ switch ( (AllDefinitionCombinations)((existingAtom->getDefinitionKind() << 3) | newAtom.getDefinitionKind()) ) {
+ case kRegAndReg:
+ throwf("duplicate symbol %s in %s and %s", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath());
+ case kRegAndWeak:
+ // ignore new weak atom, because we already have a non-weak one
+ useNew = false;
+ break;
+ case kRegAndTent:
+ // ignore new tentative atom, because we already have a regular one
+ useNew = false;
+ checkVisibilityMismatch = true;
+ if ( newAtom.getSize() > existingAtom->getSize() ) {
+ warning("for symbol %s tentative definition of size %llu from %s is "
+ "is smaller than the real definition of size %llu from %s",
+ newAtom.getDisplayName(), newAtom.getSize(), newAtom.getFile()->getPath(),
+ existingAtom->getSize(), existingAtom->getFile()->getPath());
+ }
+ break;
+ case kRegAndExtern:
+ // ignore external atom, because we already have a one
+ useNew = false;
+ break;
+ case kRegAndExternWeak:
+ // ignore external atom, because we already have a one
+ useNew = false;
+ break;
+ case kRegAndAbsolute:
+ throwf("duplicate symbol %s in %s and %s", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath());
+ break;
+ case kWeakAndReg:
+ // replace existing weak atom with regular one
+ break;
+ case kWeakAndWeak:
+ // have another weak atom, use whichever has largest alignment requirement
+ // because codegen of some client may require alignment
+ useNew = ( newAtom.getAlignment().trailingZeros() > existingAtom->getAlignment().trailingZeros() );
+ checkVisibilityMismatch = true;
+ break;
+ case kWeakAndTent:
+ // replace existing weak atom with tentative one ???
+ break;
+ case kWeakAndExtern:
+ // keep weak atom, at runtime external one may override
+ useNew = false;
+ break;
+ case kWeakAndExternWeak:
+ // keep weak atom, at runtime external one may override
+ useNew = false;
+ break;
+ case kWeakAndAbsolute:
+ // replace existing weak atom with absolute one
+ break;
+ case kTentAndReg:
+ // replace existing tentative atom with regular one
+ checkVisibilityMismatch = true;
+ if ( newAtom.getSize() < existingAtom->getSize() ) {
+ warning("for symbol %s tentative definition of size %llu from %s is "
+ "being replaced by a real definition of size %llu from %s",
+ newAtom.getDisplayName(), existingAtom->getSize(), existingAtom->getFile()->getPath(),
+ newAtom.getSize(), newAtom.getFile()->getPath());
+ }
+ break;
+ case kTentAndWeak:
+ // replace existing tentative atom with weak one ???
+ break;
+ case kTentAndTent:
+ // use largest
+ checkVisibilityMismatch = true;
+ if ( newAtom.getSize() < existingAtom->getSize() ) {
+ useNew = false;
+ }
+ else {
+ if ( newAtom.getAlignment().trailingZeros() < existingAtom->getAlignment().trailingZeros() )
+ warning("alignment lost in merging tentative definition %s", newAtom.getDisplayName());
+ }
+ break;
+ case kTentAndExtern:
+ case kTentAndExternWeak:
+ // a tentative definition and a dylib definition, so commons-mode decides how to handle
+ switch ( fOwner.fOptions.commonsMode() ) {
+ case Options::kCommonsIgnoreDylibs:
+ if ( fOwner.fOptions.warnCommons() )
+ warning("using common symbol %s from %s and ignoring defintion from dylib %s",
+ existingAtom->getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath());
+ useNew = false;
+ break;
+ case Options::kCommonsOverriddenByDylibs:
+ if ( fOwner.fOptions.warnCommons() )
+ warning("replacing common symbol %s from %s with true definition from dylib %s",
+ existingAtom->getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath());
+ break;
+ case Options::kCommonsConflictsDylibsError:
+ throwf("common symbol %s from %s conflicts with defintion from dylib %s",
+ existingAtom->getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath());
+ }
+ break;
+ case kTentAndAbsolute:
+ // replace tentative with absolute (can't size check because absolutes have no size)
+ break;
+ case kExternAndReg:
+ // replace external atom with regular one
+ break;
+ case kExternAndWeak:
+ // replace external atom with weak one
+ break;
+ case kExternAndTent:
+ // a tentative definition and a dylib definition, so commons-mode decides how to handle
+ switch ( fOwner.fOptions.commonsMode() ) {
+ case Options::kCommonsIgnoreDylibs:
+ if ( fOwner.fOptions.warnCommons() )
+ warning("using common symbol %s from %s and ignoring defintion from dylib %s",
+ newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath());
+ break;
+ case Options::kCommonsOverriddenByDylibs:
+ if ( fOwner.fOptions.warnCommons() )
+ warning("replacing defintion of %s from dylib %s with common symbol from %s",
+ newAtom.getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath());
+ useNew = false;
+ break;
+ case Options::kCommonsConflictsDylibsError:
+ throwf("common symbol %s from %s conflicts with defintion from dylib %s",
+ newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath());
+ }
+ break;
+ case kExternAndExtern:
+ throwf("duplicate symbol %s in %s and %s\n", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath());
+ case kExternAndExternWeak:
+ // keep strong dylib atom, ignore weak one
+ useNew = false;
+ break;
+ case kExternAndAbsolute:
+ // replace external atom with absolute one
+ break;
+ case kExternWeakAndReg:
+ // replace existing weak external with regular
+ break;
+ case kExternWeakAndWeak:
+ // replace existing weak external with weak (let dyld decide at runtime which to use)
+ break;
+ case kExternWeakAndTent:
+ // a tentative definition and a dylib definition, so commons-mode decides how to handle
+ switch ( fOwner.fOptions.commonsMode() ) {
+ case Options::kCommonsIgnoreDylibs:
+ if ( fOwner.fOptions.warnCommons() )
+ warning("using common symbol %s from %s and ignoring defintion from dylib %s",
+ newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath());
+ break;
+ case Options::kCommonsOverriddenByDylibs:
+ if ( fOwner.fOptions.warnCommons() )
+ warning("replacing defintion of %s from dylib %s with common symbol from %s",
+ newAtom.getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath());
+ useNew = false;
+ break;
+ case Options::kCommonsConflictsDylibsError:
+ throwf("common symbol %s from %s conflicts with defintion from dylib %s",
+ newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath());
+ }
+ break;
+ case kExternWeakAndExtern:
+ // replace existing weak external with external
+ break;
+ case kExternWeakAndExternWeak:
+ // keep existing external weak
+ useNew = false;
+ break;
+ case kExternWeakAndAbsolute:
+ // replace existing weak external with absolute
+ break;
+ case kAbsoluteAndReg:
+ throwf("duplicate symbol %s in %s and %s", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath());
+ case kAbsoluteAndWeak:
+ // ignore new weak atom, because we already have a non-weak one
+ useNew = false;
+ break;
+ case kAbsoluteAndTent:
+ // ignore new tentative atom, because we already have a regular one
+ useNew = false;
+ break;
+ case kAbsoluteAndExtern:
+ // ignore external atom, because we already have a one
+ useNew = false;
+ break;
+ case kAbsoluteAndExternWeak:
+ // ignore external atom, because we already have a one
+ useNew = false;
+ break;
+ case kAbsoluteAndAbsolute:
+ throwf("duplicate symbol %s in %s and %s", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath());
+ break;
+ }
+ }
+ if ( (existingAtom != NULL) && checkVisibilityMismatch && (newAtom.getScope() != existingAtom->getScope()) ) {
+ warning("%s has different visibility (%s) in %s and (%s) in %s",
+ newAtom.getDisplayName(), (newAtom.getScope() == 1 ? "hidden" : "default"), newAtom.getFile()->getPath(), (existingAtom->getScope() == 1 ? "hidden" : "default"), existingAtom->getFile()->getPath());
+ }
+ if ( useNew ) {
+ fTable[name] = &newAtom;
+ if ( existingAtom != NULL ) {
+ fOwner.markDead(existingAtom);
+ if ( fOwner.fInitialLoadsDone ) {
+ //fprintf(stderr, "existing %p %s overridden by %p\n", existingAtom, existingAtom->getName(), &newAtom);
+ fOwner.fAtomsOverriddenByLateLoads.insert(existingAtom);
+ }
+ }
+ if ( newAtom.getScope() == ObjectFile::Atom::scopeGlobal ) {
+ switch ( newAtom.getDefinitionKind() ) {
+ case ObjectFile::Atom::kTentativeDefinition:
+ fHasExternalTentativeDefinitions = true;
+ ++fRequireCount; // added a tentative definition means loadUndefines() needs to continue
+ break;
+ case ObjectFile::Atom::kWeakDefinition:
+ fHasExternalWeakDefinitions = true;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ else {
+ fOwner.markDead(&newAtom);
+ }
+ return useNew;
+}
+
+
+
+ObjectFile::Atom* Linker::SymbolTable::find(const char* name)
+{
+ Mapper::iterator pos = fTable.find(name);
+ if ( pos != fTable.end() ) {
+ return pos->second;
+ }
+ return NULL;
+}
+
+
+void Linker::SymbolTable::getUndefinesNames(std::vector<const char*>& undefines)
+{
+ for (Mapper::iterator it=fTable.begin(); it != fTable.end(); it++) {
+ if ( it->second == NULL ) {
+ undefines.push_back(it->first);
+ }
+ }
+}
+
+void Linker::SymbolTable::getTentativesNames(std::vector<const char*>& tents)
+{
+ for (Mapper::iterator it=fTable.begin(); it != fTable.end(); it++) {
+ if ( it->second != NULL ) {
+ if ( (it->second->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition)
+ && (it->second->getScope() == ObjectFile::Atom::scopeGlobal) ) {
+ tents.push_back(it->first);
+ }
+ }
+ }
+}
+
+
+
+bool Linker::AtomSorter::operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right)
+{
+ if ( left == right )
+ return false;
+
+ // first sort by section order (which is already sorted by segment)
+ unsigned int leftSectionIndex = left->getSection()->getIndex();
+ unsigned int rightSectionIndex = right->getSection()->getIndex();
+ if ( leftSectionIndex != rightSectionIndex)
+ return (leftSectionIndex < rightSectionIndex);
+
+ // if a -order_file is specified, then sorting is altered to sort those symbols first
+ if ( fOverriddenOrdinalMap != NULL ) {
+ std::map<const ObjectFile::Atom*, uint32_t>::iterator leftPos = fOverriddenOrdinalMap->find(left);
+ std::map<const ObjectFile::Atom*, uint32_t>::iterator rightPos = fOverriddenOrdinalMap->find(right);
+ std::map<const ObjectFile::Atom*, uint32_t>::iterator end = fOverriddenOrdinalMap->end();
+ if ( leftPos != end ) {
+ if ( rightPos != end ) {
+ // both left and right are overridden, so compare overridden ordinals
+ return leftPos->second < rightPos->second;
+ }
+ else {
+ // left is overridden and right is not, so left < right
+ return true;
+ }
+ }
+ else {
+ if ( rightPos != end ) {
+ // right is overridden and left is not, so right < left
+ return false;
+ }
+ else {
+ // neither are overridden, do default sort
+ // fall into default sorting below
+ }
+ }
+ }
+
+ // the __common section can have real or tentative definitions
+ // we want the real ones to sort before tentative ones
+ bool leftIsTent = (left->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition);
+ bool rightIsTent = (right->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition);
+ if ( leftIsTent != rightIsTent )
+ return rightIsTent;
+
+ // initializers are auto sorted to start of section
+ if ( !fInitializerSet.empty() ) {
+ bool leftFirst = (fInitializerSet.count(left) != 0);
+ bool rightFirst = (fInitializerSet.count(right) != 0);
+ if ( leftFirst != rightFirst )
+ return leftFirst;
+ }
+
+ // terminators are auto sorted to end of section
+ if ( !fTerminatorSet.empty() ) {
+ bool leftLast = (fTerminatorSet.count(left) != 0);
+ bool rightLast = (fTerminatorSet.count(right) != 0);
+ if ( leftLast != rightLast )
+ return rightLast;
+ }
+
+ // lastly sort by atom ordinal. this is already sorted by .o order
+ return left->getOrdinal() < right->getOrdinal();
+}
+
+
+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();
+
+ // open output file
+ ld.createWriter();
+
+ // do linking
+ ld.link();
+ }
+ catch (const char* msg) {
+ if ( archInferred )
+ fprintf(stderr, "ld: %s for inferred architecture %s\n", msg, archName);
+ else if ( showArch )
+ fprintf(stderr, "ld: %s for architecture %s\n", msg, archName);
+ else
+ fprintf(stderr, "ld: %s\n", msg);
+ return 1;
+ }
+
+ return 0;
+}
+++ /dev/null
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
- *
- * Copyright (c) 2006-2007 Apple 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 <stdarg.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include <vector>
-#include <set>
-#include <ext/hash_set>
-
-#include "MachOFileAbstraction.hpp"
-#include "Architectures.hpp"
-
-
- __attribute__((noreturn))
-void throwf(const char* format, ...)
-{
- va_list list;
- char* p;
- va_start(list, format);
- vasprintf(&p, format, list);
- va_end(list);
-
- const char* t = p;
- throw t;
-}
-
-
-template <typename A>
-class MachOChecker
-{
-public:
- static bool validFile(const uint8_t* fileContent);
- static MachOChecker<A>* make(const uint8_t* fileContent, uint32_t fileLength, const char* path)
- { return new MachOChecker<A>(fileContent, fileLength, path); }
- virtual ~MachOChecker() {}
-
-
-private:
- typedef typename A::P P;
- typedef typename A::P::E E;
- typedef typename A::P::uint_t pint_t;
-
- class CStringEquals
- {
- public:
- bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
- };
-
- typedef __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> StringSet;
-
- MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path);
- void checkMachHeader();
- void checkLoadCommands();
- void checkSection(const macho_segment_command<P>* segCmd, const macho_section<P>* sect);
- uint8_t loadCommandSizeMask();
- void checkSymbolTable();
- 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 fLength;
- const char* fStrings;
- const char* fStringsEnd;
- const macho_nlist<P>* fSymbols;
- uint32_t fSymbolCount;
- const macho_dysymtab_command<P>* fDynamicSymbolTable;
- 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;
- bool fWriteableSegmentWithAddrOver4G;
- const macho_segment_command<P>* fFirstSegment;
- const macho_segment_command<P>* fFirstWritableSegment;
-};
-
-
-
-template <>
-bool MachOChecker<ppc>::validFile(const uint8_t* fileContent)
-{
- 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;
- switch (header->filetype()) {
- case MH_EXECUTE:
- case MH_DYLIB:
- case MH_BUNDLE:
- case MH_DYLINKER:
- return true;
- }
- return false;
-}
-
-template <>
-bool MachOChecker<ppc64>::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_POWERPC64 )
- return false;
- switch (header->filetype()) {
- case MH_EXECUTE:
- case MH_DYLIB:
- case MH_BUNDLE:
- case MH_DYLINKER:
- return true;
- }
- return false;
-}
-
-template <>
-bool MachOChecker<x86>::validFile(const uint8_t* fileContent)
-{
- 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;
- switch (header->filetype()) {
- case MH_EXECUTE:
- case MH_DYLIB:
- case MH_BUNDLE:
- case MH_DYLINKER:
- return true;
- }
- 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 <>
-bool MachOChecker<arm>::validFile(const uint8_t* fileContent)
-{
- const macho_header<P>* header = (const macho_header<P>*)fileContent;
- if ( header->magic() != MH_MAGIC )
- return false;
- if ( header->cputype() != CPU_TYPE_ARM )
- 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 <> uint8_t MachOChecker<arm>::loadCommandSizeMask() { return 0x03; }
-
-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), fDynamicSymbolTable(NULL), fIndirectTableCount(0),
- fLocalRelocations(NULL), fLocalRelocationsCount(0), fExternalRelocations(NULL), fExternalRelocationsCount(0),
- fWriteableSegmentWithAddrOver4G(false), fFirstSegment(NULL), fFirstWritableSegment(NULL)
-{
- // sanity check
- if ( ! validFile(fileContent) )
- throw "not a mach-o file that can be checked";
-
- fPath = strdup(path);
- fHeader = (const macho_header<P>*)fileContent;
-
- // sanity check header
- checkMachHeader();
-
- // check load commands
- checkLoadCommands();
-
- checkIndirectSymbolTable();
-
- checkRelocations();
-
- checkSymbolTable();
-}
-
-
-template <typename A>
-void MachOChecker<A>::checkMachHeader()
-{
- if ( (fHeader->sizeofcmds() + sizeof(macho_header<P>)) > fLength )
- throw "sizeofcmds in mach_header is larger than file";
-
- uint32_t flags = fHeader->flags();
- const uint32_t invalidBits = MH_INCRLINK | MH_LAZY_INIT | 0xFFC00000;
- if ( flags & invalidBits )
- throw "invalid bits in mach_header flags";
- if ( (flags & MH_NO_REEXPORTED_DYLIBS) && (fHeader->filetype() != MH_DYLIB) )
- throw "MH_NO_REEXPORTED_DYLIBS bit of mach_header flags only valid for dylibs";
-}
-
-template <typename A>
-void MachOChecker<A>::checkLoadCommands()
-{
- // check that all load commands fit within the load command space file
- const macho_encryption_info_command<P>* encryption_info = NULL;
- const uint8_t* const endOfFile = (uint8_t*)fHeader + fLength;
- const uint8_t* const endOfLoadCommands = (uint8_t*)fHeader + sizeof(macho_header<P>) + fHeader->sizeofcmds();
- const uint32_t cmd_count = fHeader->ncmds();
- const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
- const macho_load_command<P>* cmd = cmds;
- for (uint32_t i = 0; i < cmd_count; ++i) {
- uint32_t size = cmd->cmdsize();
- if ( (size & this->loadCommandSizeMask()) != 0 )
- throwf("load command #%d has a unaligned size", i);
- const uint8_t* endOfCmd = ((uint8_t*)cmd)+cmd->cmdsize();
- if ( endOfCmd > endOfLoadCommands )
- throwf("load command #%d extends beyond the end of the load commands", i);
- if ( endOfCmd > endOfFile )
- throwf("load command #%d extends beyond the end of the file", i);
- switch ( cmd->cmd() ) {
- case macho_segment_command<P>::CMD:
- case LC_SYMTAB:
- case LC_UNIXTHREAD:
- case LC_DYSYMTAB:
- case LC_LOAD_DYLIB:
- case LC_ID_DYLIB:
- case LC_LOAD_DYLINKER:
- case LC_ID_DYLINKER:
- case macho_routines_command<P>::CMD:
- case LC_SUB_FRAMEWORK:
- case LC_SUB_CLIENT:
- case LC_TWOLEVEL_HINTS:
- case LC_PREBIND_CKSUM:
- case LC_LOAD_WEAK_DYLIB:
- case LC_LAZY_LOAD_DYLIB:
- case LC_UUID:
- case LC_REEXPORT_DYLIB:
- case LC_SEGMENT_SPLIT_INFO:
- case LC_CODE_SIGNATURE:
- break;
- case LC_ENCRYPTION_INFO:
- encryption_info = (macho_encryption_info_command<P>*)cmd;
- break;
- case LC_SUB_UMBRELLA:
- case LC_SUB_LIBRARY:
- if ( fHeader->flags() & MH_NO_REEXPORTED_DYLIBS )
- throw "MH_NO_REEXPORTED_DYLIBS bit of mach_header flags should not be set in an image with LC_SUB_LIBRARY or LC_SUB_UMBRELLA";
- break;
- default:
- throwf("load command #%d is an unknown kind 0x%X", i, cmd->cmd());
- }
- cmd = (const macho_load_command<P>*)endOfCmd;
- }
-
- // check segments
- cmd = cmds;
- std::vector<std::pair<pint_t, pint_t> > segmentAddressRanges;
- std::vector<std::pair<pint_t, pint_t> > segmentFileOffsetRanges;
- const macho_segment_command<P>* linkEditSegment = NULL;
- 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->cmdsize() != (sizeof(macho_segment_command<P>) + segCmd->nsects() * sizeof(macho_section_content<P>)) )
- throw "invalid segment load command size";
-
- // see if this overlaps another segment address range
- uint64_t startAddr = segCmd->vmaddr();
- uint64_t endAddr = startAddr + segCmd->vmsize();
- for (typename std::vector<std::pair<pint_t, pint_t> >::iterator it = segmentAddressRanges.begin(); it != segmentAddressRanges.end(); ++it) {
- if ( it->first < startAddr ) {
- if ( it->second > startAddr )
- throw "overlapping segment vm addresses";
- }
- else if ( it->first > startAddr ) {
- if ( it->first < endAddr )
- throw "overlapping segment vm addresses";
- }
- else {
- throw "overlapping segment vm addresses";
- }
- segmentAddressRanges.push_back(std::make_pair<pint_t, pint_t>(startAddr, endAddr));
- }
- // see if this overlaps another segment file offset range
- uint64_t startOffset = segCmd->fileoff();
- uint64_t endOffset = startOffset + segCmd->filesize();
- for (typename std::vector<std::pair<pint_t, pint_t> >::iterator it = segmentFileOffsetRanges.begin(); it != segmentFileOffsetRanges.end(); ++it) {
- if ( it->first < startOffset ) {
- if ( it->second > startOffset )
- throw "overlapping segment file data";
- }
- else if ( it->first > startOffset ) {
- if ( it->first < endOffset )
- throw "overlapping segment file data";
- }
- else {
- throw "overlapping segment file data";
- }
- segmentFileOffsetRanges.push_back(std::make_pair<pint_t, pint_t>(startOffset, endOffset));
- // check is within file bounds
- if ( (startOffset > fLength) || (endOffset > fLength) )
- throw "segment file data is past end of file";
- }
- // verify it fits in file
- if ( startOffset > fLength )
- throw "segment fileoff does not fit in file";
- if ( endOffset > fLength )
- throw "segment fileoff+filesize does not fit in file";
-
- // keep LINKEDIT segment
- if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 )
- linkEditSegment = segCmd;
-
- // cache interesting segments
- if ( fFirstSegment == NULL )
- fFirstSegment = segCmd;
- if ( (segCmd->initprot() & VM_PROT_WRITE) != 0 ) {
- if ( fFirstWritableSegment == NULL )
- fFirstWritableSegment = segCmd;
- if ( segCmd->vmaddr() > 0x100000000ULL )
- fWriteableSegmentWithAddrOver4G = true;
- }
-
- // check section ranges
- const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
- const macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()];
- for(const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
- // check all sections are within segment
- if ( sect->addr() < startAddr )
- throwf("section %s vm address not within segment", sect->sectname());
- if ( (sect->addr()+sect->size()) > endAddr )
- throwf("section %s vm address not within segment", sect->sectname());
- if ( ((sect->flags() & SECTION_TYPE) != S_ZEROFILL) && (segCmd->filesize() != 0) ) {
- if ( sect->offset() < startOffset )
- throwf("section %s file offset not within segment", sect->sectname());
- if ( (sect->offset()+sect->size()) > endOffset )
- throwf("section %s file offset not within segment", sect->sectname());
- }
- checkSection(segCmd, sect);
- }
- }
- cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
- }
-
- // verify there was a LINKEDIT segment
- if ( linkEditSegment == NULL )
- throw "no __LINKEDIT segment";
-
- // checks for executables
- bool isStaticExecutable = false;
- if ( fHeader->filetype() == MH_EXECUTE ) {
- isStaticExecutable = true;
- cmd = cmds;
- for (uint32_t i = 0; i < cmd_count; ++i) {
- switch ( cmd->cmd() ) {
- case LC_LOAD_DYLINKER:
- // the existence of a dyld load command makes a executable dynamic
- isStaticExecutable = false;
- break;
- }
- cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
- }
- if ( isStaticExecutable ) {
- if ( fHeader->flags() != MH_NOUNDEFS )
- throw "invalid bits in mach_header flags for static executable";
- }
- }
-
- // verify encryption info
- if ( encryption_info != NULL ) {
- if ( fHeader->filetype() != MH_EXECUTE )
- throw "LC_ENCRYPTION_INFO load command is only legal in main executables";
- if ( encryption_info->cryptoff() < (sizeof(macho_header<P>) + fHeader->sizeofcmds()) )
- throw "LC_ENCRYPTION_INFO load command has cryptoff covers some load commands";
- if ( (encryption_info->cryptoff() % 4096) != 0 )
- throw "LC_ENCRYPTION_INFO load command has cryptoff which is not page aligned";
- if ( (encryption_info->cryptsize() % 4096) != 0 )
- throw "LC_ENCRYPTION_INFO load command has cryptsize which is not page sized";
- for (typename std::vector<std::pair<pint_t, pint_t> >::iterator it = segmentFileOffsetRanges.begin();
- it != segmentFileOffsetRanges.end(); ++it) {
- if ( (it->first <= encryption_info->cryptoff()) && (encryption_info->cryptoff() < it->second) ) {
- if ( (encryption_info->cryptoff() + encryption_info->cryptsize()) > it->second )
- throw "LC_ENCRYPTION_INFO load command is not contained within one segment";
- }
- }
- }
-
- // check LC_SYMTAB, LC_DYSYMTAB, and LC_SEGMENT_SPLIT_INFO
- cmd = cmds;
- bool foundDynamicSymTab = false;
- 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;
- fSymbolCount = symtab->nsyms();
- fSymbols = (const macho_nlist<P>*)((char*)fHeader + symtab->symoff());
- if ( symtab->symoff() < linkEditSegment->fileoff() )
- throw "symbol table not in __LINKEDIT";
- if ( (symtab->symoff() + fSymbolCount*sizeof(macho_nlist<P>*)) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) )
- throw "symbol table end not in __LINKEDIT";
- if ( (symtab->symoff() % sizeof(pint_t)) != 0 )
- throw "symbol table start not pointer aligned";
- fStrings = (char*)fHeader + symtab->stroff();
- fStringsEnd = fStrings + symtab->strsize();
- if ( symtab->stroff() < linkEditSegment->fileoff() )
- throw "string pool not in __LINKEDIT";
- if ( (symtab->stroff()+symtab->strsize()) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) )
- throw "string pool extends beyond __LINKEDIT";
- if ( (symtab->stroff() % 4) != 0 ) // work around until rdar://problem/4737991 is fixed
- throw "string pool start not pointer aligned";
- if ( (symtab->strsize() % sizeof(pint_t)) != 0 )
- throw "string pool size not a multiple of pointer size";
- }
- break;
- case LC_DYSYMTAB:
- {
- if ( isStaticExecutable )
- throw "LC_DYSYMTAB should not be used in static executable";
- foundDynamicSymTab = true;
- fDynamicSymbolTable = (struct macho_dysymtab_command<P>*)cmd;
- fIndirectTable = (uint32_t*)((char*)fHeader + fDynamicSymbolTable->indirectsymoff());
- fIndirectTableCount = fDynamicSymbolTable->nindirectsyms();
- if ( fIndirectTableCount != 0 ) {
- if ( fDynamicSymbolTable->indirectsymoff() < linkEditSegment->fileoff() )
- throw "indirect symbol table not in __LINKEDIT";
- if ( (fDynamicSymbolTable->indirectsymoff()+fIndirectTableCount*8) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) )
- throw "indirect symbol table not in __LINKEDIT";
- if ( (fDynamicSymbolTable->indirectsymoff() % sizeof(pint_t)) != 0 )
- throw "indirect symbol table not pointer aligned";
- }
- fLocalRelocationsCount = fDynamicSymbolTable->nlocrel();
- if ( fLocalRelocationsCount != 0 ) {
- fLocalRelocations = (const macho_relocation_info<P>*)((char*)fHeader + fDynamicSymbolTable->locreloff());
- if ( fDynamicSymbolTable->locreloff() < linkEditSegment->fileoff() )
- throw "local relocations not in __LINKEDIT";
- if ( (fDynamicSymbolTable->locreloff()+fLocalRelocationsCount*sizeof(macho_relocation_info<P>)) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) )
- throw "local relocations not in __LINKEDIT";
- if ( (fDynamicSymbolTable->locreloff() % sizeof(pint_t)) != 0 )
- throw "local relocations table not pointer aligned";
- }
- fExternalRelocationsCount = fDynamicSymbolTable->nextrel();
- if ( fExternalRelocationsCount != 0 ) {
- fExternalRelocations = (const macho_relocation_info<P>*)((char*)fHeader + fDynamicSymbolTable->extreloff());
- if ( fDynamicSymbolTable->extreloff() < linkEditSegment->fileoff() )
- throw "external relocations not in __LINKEDIT";
- if ( (fDynamicSymbolTable->extreloff()+fExternalRelocationsCount*sizeof(macho_relocation_info<P>)) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) )
- throw "external relocations not in __LINKEDIT";
- if ( (fDynamicSymbolTable->extreloff() % sizeof(pint_t)) != 0 )
- throw "external relocations table not pointer aligned";
- }
- }
- break;
- case LC_SEGMENT_SPLIT_INFO:
- {
- if ( isStaticExecutable )
- throw "LC_SEGMENT_SPLIT_INFO should not be used in static executable";
- const macho_linkedit_data_command<P>* info = (struct macho_linkedit_data_command<P>*)cmd;
- if ( info->dataoff() < linkEditSegment->fileoff() )
- throw "split seg info not in __LINKEDIT";
- if ( (info->dataoff()+info->datasize()) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) )
- throw "split seg info not in __LINKEDIT";
- if ( (info->dataoff() % sizeof(pint_t)) != 0 )
- throw "split seg info table not pointer aligned";
- if ( (info->datasize() % sizeof(pint_t)) != 0 )
- throw "split seg info size not a multiple of pointer size";
- }
- break;
- }
- cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
- }
- if ( !isStaticExecutable && !foundDynamicSymTab )
- throw "missing dynamic symbol table";
- if ( fStrings == NULL )
- throw "missing symbol table";
-
-}
-
-template <typename A>
-void MachOChecker<A>::checkSection(const macho_segment_command<P>* segCmd, const macho_section<P>* sect)
-{
- uint8_t sectionType = (sect->flags() & SECTION_TYPE);
- if ( sectionType == S_ZEROFILL ) {
- if ( sect->offset() != 0 )
- throwf("section offset should be zero for zero-fill section %s", sect->sectname());
- }
-
- // more section tests here
-}
-
-template <typename A>
-void MachOChecker<A>::checkIndirectSymbolTable()
-{
- 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;
- const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
- const macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()];
- for(const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
- // make sure all magic sections that use indirect symbol table fit within it
- uint32_t start = 0;
- uint32_t elementSize = 0;
- switch ( sect->flags() & SECTION_TYPE ) {
- case S_SYMBOL_STUBS:
- elementSize = sect->reserved2();
- start = sect->reserved1();
- break;
- case S_LAZY_SYMBOL_POINTERS:
- case S_NON_LAZY_SYMBOL_POINTERS:
- elementSize = sizeof(pint_t);
- start = sect->reserved1();
- break;
- }
- if ( elementSize != 0 ) {
- uint32_t count = sect->size() / elementSize;
- if ( (count*elementSize) != sect->size() )
- throwf("%s section size is not an even multiple of element size", sect->sectname());
- if ( (start+count) > fIndirectTableCount )
- throwf("%s section references beyond end of indirect symbol table (%d > %d)", sect->sectname(), start+count, fIndirectTableCount );
- }
- }
- }
- cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
- }
-}
-
-
-template <typename A>
-void MachOChecker<A>::checkSymbolTable()
-{
- // verify no duplicate external symbol names
- if ( fDynamicSymbolTable != NULL ) {
- StringSet externalNames;
- const macho_nlist<P>* const exportedStart = &fSymbols[fDynamicSymbolTable->iextdefsym()];
- const macho_nlist<P>* const exportedEnd = &exportedStart[fDynamicSymbolTable->nextdefsym()];
- for(const macho_nlist<P>* p = exportedStart; p < exportedEnd; ++p) {
- const char* symName = &fStrings[p->n_strx()];
- if ( externalNames.find(symName) != externalNames.end() )
- throwf("duplicate external symbol: %s", symName);
- externalNames.insert(symName);
- }
- }
-}
-
-
-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 <>
-arm::P::uint_t MachOChecker<arm>::relocBase()
-{
- if ( fHeader->flags() & MH_SPLIT_SEGS )
- return fFirstWritableSegment->vmaddr();
- else
- return fFirstSegment->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 ( (address >= segCmd->vmaddr()) && (address < segCmd->vmaddr()+segCmd->vmsize()) ) {
- // if segment is writable, we are fine
- if ( (segCmd->initprot() & VM_PROT_WRITE) != 0 )
- return true;
- // could be a text reloc, make sure section bit is set
- const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
- const macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()];
- for(const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
- if ( (sect->addr() <= address) && (address < (sect->addr()+sect->size())) ) {
- // found section for this address, if has relocs we are fine
- return ( (sect->flags() & (S_ATTR_EXT_RELOC|S_ATTR_LOC_RELOC)) != 0 );
- }
- }
- }
- }
- cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
- }
- return false;
-}
-
-
-template <>
-void MachOChecker<ppc>::checkExternalReloation(const macho_relocation_info<P>* reloc)
-{
- if ( reloc->r_length() != 2 )
- 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 "external relocation address not in writable segment";
- // FIX: check r_symbol
-}
-
-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 "external relocation address not in writable segment";
- // FIX: check r_symbol
-}
-
-template <>
-void MachOChecker<x86>::checkExternalReloation(const macho_relocation_info<P>* reloc)
-{
- if ( reloc->r_length() != 2 )
- 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 "external relocation address not in writable segment";
- // FIX: check r_symbol
-}
-
-
-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<arm>::checkExternalReloation(const macho_relocation_info<P>* reloc)
-{
- if ( reloc->r_length() != 2 )
- throw "bad external relocation length";
- if ( reloc->r_type() != ARM_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 "external 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 {
- // ignore pair relocs
- if ( reloc->r_type() == PPC_RELOC_PAIR )
- return;
- // FIX
- if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) )
- throwf("local relocation address 0x%08X not in writable segment", reloc->r_address());
- }
-}
-
-
-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 <>
-void MachOChecker<arm>::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;
- if ( sreloc->r_length() != 2 )
- throw "bad local scattered relocation length";
- if ( sreloc->r_type() != ARM_RELOC_PB_LA_PTR )
- throw "bad local scattered relocation type";
- }
- else {
- if ( reloc->r_length() != 2 )
- throw "bad local relocation length";
- 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()
-{
- // external relocations should be sorted to minimize dyld symbol lookups
- // therefore every reloc with the same r_symbolnum value should be contiguous
- std::set<uint32_t> previouslySeenSymbolIndexes;
- uint32_t lastSymbolIndex = 0xFFFFFFFF;
- const macho_relocation_info<P>* const externRelocsEnd = &fExternalRelocations[fExternalRelocationsCount];
- for (const macho_relocation_info<P>* reloc = fExternalRelocations; reloc < externRelocsEnd; ++reloc) {
- this->checkExternalReloation(reloc);
- if ( reloc->r_symbolnum() != lastSymbolIndex ) {
- if ( previouslySeenSymbolIndexes.count(reloc->r_symbolnum()) != 0 )
- throw "external relocations not sorted";
- previouslySeenSymbolIndexes.insert(lastSymbolIndex);
- lastSymbolIndex = reloc->r_symbolnum();
- }
- }
-
- 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;
-
- try {
- int fd = ::open(path, O_RDONLY, 0);
- if ( fd == -1 )
- throw "cannot open file";
- ::fstat(fd, &stat_buf);
- uint32_t length = stat_buf.st_size;
- uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | 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) ) {
- const struct fat_header* fh = (struct fat_header*)p;
- const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header));
- for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) {
- size_t offset = OSSwapBigToHostInt32(archs[i].offset);
- size_t size = OSSwapBigToHostInt32(archs[i].size);
- unsigned int cputype = OSSwapBigToHostInt32(archs[i].cputype);
-
- switch(cputype) {
- case CPU_TYPE_POWERPC:
- if ( MachOChecker<ppc>::validFile(p + offset) )
- MachOChecker<ppc>::make(p + offset, size, path);
- else
- throw "in universal file, ppc slice does not contain ppc mach-o";
- break;
- case CPU_TYPE_I386:
- if ( MachOChecker<x86>::validFile(p + offset) )
- MachOChecker<x86>::make(p + offset, size, path);
- else
- throw "in universal file, i386 slice does not contain i386 mach-o";
- break;
- case CPU_TYPE_POWERPC64:
- if ( MachOChecker<ppc64>::validFile(p + offset) )
- MachOChecker<ppc64>::make(p + offset, size, path);
- else
- throw "in universal file, ppc64 slice does not contain ppc64 mach-o";
- break;
- case CPU_TYPE_X86_64:
- if ( MachOChecker<x86_64>::validFile(p + offset) )
- MachOChecker<x86_64>::make(p + offset, size, path);
- else
- throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
- break;
- case CPU_TYPE_ARM:
- if ( MachOChecker<arm>::validFile(p + offset) )
- MachOChecker<arm>::make(p + offset, size, path);
- else
- throw "in universal file, arm slice does not contain arm mach-o";
- break;
- default:
- throwf("in universal file, unknown architecture slice 0x%x\n", cputype);
- }
- }
- }
- else if ( MachOChecker<x86>::validFile(p) ) {
- MachOChecker<x86>::make(p, length, path);
- }
- else if ( MachOChecker<ppc>::validFile(p) ) {
- MachOChecker<ppc>::make(p, length, path);
- }
- 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 if ( MachOChecker<arm>::validFile(p) ) {
- MachOChecker<arm>::make(p, length, path);
- }
- else {
- throw "not a known file type";
- }
- }
- catch (const char* msg) {
- throwf("%s in %s", msg, path);
- }
-}
-
-
-int main(int argc, const char* argv[])
-{
- try {
- for(int i=1; i < argc; ++i) {
- const char* arg = argv[i];
- if ( arg[0] == '-' ) {
- if ( strcmp(arg, "-no_content") == 0 ) {
-
- }
- else {
- throwf("unknown option: %s\n", arg);
- }
- }
- else {
- check(arg);
- }
- }
- }
- catch (const char* msg) {
- fprintf(stderr, "machocheck failed: %s\n", msg);
- return 1;
- }
-
- return 0;
-}
-
-
-
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <fcntl.h>
+
+#include "MachOReaderRelocatable.hpp"
+
+#define LTO_SUPPORT 1
+
+#if LTO_SUPPORT
+ #include "LTOReader.hpp"
+#endif
+
+static bool sDumpContent= true;
+static bool sDumpStabs = false;
+static bool sSort = true;
+static bool sNMmode = false;
+static cpu_type_t sPreferredArch = CPU_TYPE_POWERPC64;
+static const char* sMatchName;
+static int sPrintRestrict;
+static int sPrintAlign;
+static int sPrintName;
+
+
+ __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;
+}
+
+void warning(const char* format, ...)
+{
+ va_list list;
+ fprintf(stderr, "warning: ");
+ va_start(list, format);
+ vfprintf(stderr, format, list);
+ va_end(list);
+ fprintf(stderr, "\n");
+}
+
+static void dumpStabs(std::vector<ObjectFile::Reader::Stab>* stabs)
+{
+ // debug info
+ printf("stabs: (%lu)\n", stabs->size());
+ for (std::vector<ObjectFile::Reader::Stab>::iterator it = stabs->begin(); it != stabs->end(); ++it ) {
+ ObjectFile::Reader::Stab& stab = *it;
+ const char* code = "?????";
+ switch (stab.type) {
+ case N_GSYM:
+ code = " GSYM";
+ break;
+ case N_FNAME:
+ code = "FNAME";
+ break;
+ case N_FUN:
+ code = " FUN";
+ break;
+ case N_STSYM:
+ code = "STSYM";
+ break;
+ case N_LCSYM:
+ code = "LCSYM";
+ break;
+ case N_BNSYM:
+ code = "BNSYM";
+ break;
+ case N_OPT:
+ code = " OPT";
+ break;
+ case N_RSYM:
+ code = " RSYM";
+ break;
+ case N_SLINE:
+ code = "SLINE";
+ break;
+ case N_ENSYM:
+ code = "ENSYM";
+ break;
+ case N_SSYM:
+ code = " SSYM";
+ break;
+ case N_SO:
+ code = " SO";
+ break;
+ case N_OSO:
+ code = " OSO";
+ break;
+ case N_LSYM:
+ code = " LSYM";
+ break;
+ case N_BINCL:
+ code = "BINCL";
+ break;
+ case N_SOL:
+ code = " SOL";
+ break;
+ case N_PARAMS:
+ code = "PARMS";
+ break;
+ case N_VERSION:
+ code = " VERS";
+ break;
+ case N_OLEVEL:
+ code = "OLEVL";
+ break;
+ case N_PSYM:
+ code = " PSYM";
+ break;
+ case N_EINCL:
+ code = "EINCL";
+ break;
+ case N_ENTRY:
+ code = "ENTRY";
+ break;
+ case N_LBRAC:
+ code = "LBRAC";
+ break;
+ case N_EXCL:
+ code = " EXCL";
+ break;
+ case N_RBRAC:
+ code = "RBRAC";
+ break;
+ case N_BCOMM:
+ code = "BCOMM";
+ break;
+ case N_ECOMM:
+ code = "ECOMM";
+ break;
+ case N_LENG:
+ code = "LENG";
+ break;
+ }
+ printf(" [atom=%20s] %02X %04X %s %s\n", ((stab.atom != NULL) ? stab.atom->getDisplayName() : ""), stab.other, stab.desc, code, stab.string);
+ }
+}
+
+
+static void dumpAtomLikeNM(ObjectFile::Atom* atom)
+{
+ uint32_t size = atom->getSize();
+
+ const char* visibility;
+ switch ( atom->getScope() ) {
+ case ObjectFile::Atom::scopeTranslationUnit:
+ visibility = "internal";
+ break;
+ case ObjectFile::Atom::scopeLinkageUnit:
+ visibility = "hidden ";
+ break;
+ case ObjectFile::Atom::scopeGlobal:
+ visibility = "global ";
+ break;
+ default:
+ visibility = " ";
+ break;
+ }
+
+ const char* kind;
+ switch ( atom->getDefinitionKind() ) {
+ case ObjectFile::Atom::kRegularDefinition:
+ kind = "regular ";
+ break;
+ case ObjectFile::Atom::kTentativeDefinition:
+ kind = "tentative";
+ break;
+ case ObjectFile::Atom::kWeakDefinition:
+ kind = "weak ";
+ break;
+ case ObjectFile::Atom::kAbsoluteSymbol:
+ kind = "absolute ";
+ break;
+ default:
+ kind = " ";
+ break;
+ }
+
+ printf("0x%08X %s %s %s\n", size, visibility, kind, atom->getDisplayName());
+}
+
+
+static void dumpAtom(ObjectFile::Atom* atom)
+{
+ if(sMatchName && strcmp(sMatchName, atom->getDisplayName()))
+ return;
+
+ //printf("atom: %p\n", atom);
+
+ // name
+ if(!sPrintRestrict || sPrintName)
+ printf("name: %s\n", atom->getDisplayName());
+
+ // scope
+ if(!sPrintRestrict)
+ switch ( atom->getScope() ) {
+ case ObjectFile::Atom::scopeTranslationUnit:
+ printf("scope: translation unit\n");
+ break;
+ case ObjectFile::Atom::scopeLinkageUnit:
+ printf("scope: linkage unit\n");
+ break;
+ case ObjectFile::Atom::scopeGlobal:
+ printf("scope: global\n");
+ break;
+ default:
+ printf("scope: unknown\n");
+ }
+
+ // kind
+ if(!sPrintRestrict)
+ switch ( atom->getDefinitionKind() ) {
+ case ObjectFile::Atom::kRegularDefinition:
+ printf("kind: regular\n");
+ break;
+ case ObjectFile::Atom::kWeakDefinition:
+ printf("kind: weak\n");
+ break;
+ case ObjectFile::Atom::kTentativeDefinition:
+ printf("kind: tentative\n");
+ break;
+ case ObjectFile::Atom::kExternalDefinition:
+ printf("kind: import\n");
+ break;
+ case ObjectFile::Atom::kExternalWeakDefinition:
+ printf("kind: weak import\n");
+ break;
+ case ObjectFile::Atom::kAbsoluteSymbol:
+ printf("kind: absolute symbol\n");
+ break;
+ default:
+ printf("kind: unknown\n");
+ }
+
+ // segment and section
+ if(!sPrintRestrict && (atom->getSectionName() != NULL) )
+ printf("section: %s,%s\n", atom->getSegment().getName(), atom->getSectionName());
+
+ // attributes
+ if(!sPrintRestrict) {
+ printf("attrs: ");
+ if ( atom->dontDeadStrip() )
+ printf("dont-dead-strip ");
+ if ( atom->isZeroFill() )
+ printf("zero-fill ");
+ if ( atom->isThumb() )
+ printf("thumb ");
+ printf("\n");
+ }
+
+ // size
+ if(!sPrintRestrict)
+ printf("size: 0x%012llX\n", atom->getSize());
+
+ // alignment
+ if(!sPrintRestrict || sPrintAlign)
+ printf("align: %u mod %u\n", atom->getAlignment().modulus, (1 << atom->getAlignment().powerOf2) );
+
+ // content
+ if (!sPrintRestrict && sDumpContent ) {
+ uint64_t size = atom->getSize();
+ if ( size < 4096 ) {
+ uint8_t content[size];
+ atom->copyRawContent(content);
+ printf("content: ");
+ if ( atom->getContentType() == ObjectFile::Atom::kCStringType ) {
+ printf("\"");
+ for (unsigned int i=0; i < size; ++i) {
+ if(content[i]<'!' || content[i]>=127)
+ printf("\\%o", content[i]);
+ else
+ printf("%c", content[i]);
+ }
+ printf("\"");
+ }
+ else {
+ for (unsigned int i=0; i < size; ++i)
+ printf("%02X ", content[i]);
+ }
+ }
+ printf("\n");
+ }
+
+ // unwind info
+ if(!sPrintRestrict) {
+ if ( atom->beginUnwind() != atom->endUnwind() ) {
+ printf("unwind encodings:\n");
+ for (ObjectFile::UnwindInfo::iterator it = atom->beginUnwind(); it != atom->endUnwind(); ++it) {
+ printf("\t 0x%04X 0x%08X\n", it->startOffset, it->unwindInfo);
+ }
+ }
+ }
+
+ // references
+ if(!sPrintRestrict) {
+ std::vector<ObjectFile::Reference*>& references = atom->getReferences();
+ const int refCount = references.size();
+ printf("references: (%u)\n", refCount);
+ for (int i=0; i < refCount; ++i) {
+ ObjectFile::Reference* ref = references[i];
+ printf(" %s\n", ref->getDescription());
+ }
+ }
+
+ // line info
+ if(!sPrintRestrict) {
+ std::vector<ObjectFile::LineInfo>* lineInfo = atom->getLineInfo();
+ if ( (lineInfo != NULL) && (lineInfo->size() > 0) ) {
+ printf("line info: (%lu)\n", lineInfo->size());
+ for (std::vector<ObjectFile::LineInfo>::iterator it = lineInfo->begin(); it != lineInfo->end(); ++it) {
+ printf(" offset 0x%04X, line %d, file %s\n", it->atomOffset, it->lineNumber, it->fileName);
+ }
+ }
+ }
+
+ if(!sPrintRestrict)
+ printf("\n");
+}
+
+struct AtomSorter
+{
+ bool operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right)
+ {
+ if ( left == right )
+ return false;
+ int name = strcmp(left->getDisplayName(), right->getDisplayName());
+ if ( name == 0 )
+ return (left->getSize() < right->getSize());
+ else
+ return ( name < 0);
+ }
+};
+
+
+static void dumpFile(ObjectFile::Reader* reader)
+{
+ // stabs debug info
+ if ( sDumpStabs && (reader->getDebugInfoKind() == ObjectFile::Reader::kDebugInfoStabs) ) {
+ std::vector<ObjectFile::Reader::Stab>* stabs = reader->getStabs();
+ if ( stabs != NULL )
+ dumpStabs(stabs);
+ }
+
+ // get all atoms
+ std::vector<ObjectFile::Atom*> atoms = reader->getAtoms();
+
+ // make copy of vector and sort (so output is canonical)
+ std::vector<ObjectFile::Atom*> sortedAtoms(atoms);
+ if ( sSort )
+ std::sort(sortedAtoms.begin(), sortedAtoms.end(), AtomSorter());
+
+ for(std::vector<ObjectFile::Atom*>::iterator it=sortedAtoms.begin(); it != sortedAtoms.end(); ++it) {
+ if ( sNMmode )
+ dumpAtomLikeNM(*it);
+ else
+ dumpAtom(*it);
+ }
+}
+
+
+static ObjectFile::Reader* createReader(const char* path, const ObjectFile::ReaderOptions& options)
+{
+ struct stat stat_buf;
+
+ int fd = ::open(path, O_RDONLY, 0);
+ if ( fd == -1 )
+ throwf("cannot open file: %s", path);
+ ::fstat(fd, &stat_buf);
+ uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | 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 < OSSwapBigToHostInt32(fh->nfat_arch); ++i) {
+ if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)sPreferredArch ) {
+ p = p + OSSwapBigToHostInt32(archs[i].offset);
+ mh = (struct mach_header*)p;
+ }
+ }
+ }
+ if ( mach_o::relocatable::Reader<x86>::validFile(p) )
+ return new mach_o::relocatable::Reader<x86>::Reader(p, path, 0, options, 0);
+ else if ( mach_o::relocatable::Reader<ppc>::validFile(p) )
+ return new mach_o::relocatable::Reader<ppc>::Reader(p, path, 0, options, 0);
+ else if ( mach_o::relocatable::Reader<ppc64>::validFile(p) )
+ return new mach_o::relocatable::Reader<ppc64>::Reader(p, path, 0, options, 0);
+ else if ( mach_o::relocatable::Reader<x86_64>::validFile(p) )
+ return new mach_o::relocatable::Reader<x86_64>::Reader(p, path, 0, options, 0);
+ else if ( mach_o::relocatable::Reader<arm>::validFile(p) )
+ return new mach_o::relocatable::Reader<arm>::Reader(p, path, 0, options, 0);
+#if LTO_SUPPORT
+ if ( lto::Reader::validFile(p, stat_buf.st_size, 0) ) {
+ return new lto::Reader(p, stat_buf.st_size, path, 0, options, 0);
+ }
+#endif
+
+ throwf("not a mach-o object file: %s", path);
+}
+
+static
+void
+usage()
+{
+ fprintf(stderr, "ObjectDump options:\n"
+ "\t-no_content\tdon't dump contents\n"
+ "\t-stabs\t\tdump stabs\n"
+ "\t-arch aaa\tonly dump info about arch aaa\n"
+ "\t-only sym\tonly dump info about sym\n"
+ "\t-align\t\tonly print alignment info\n"
+ "\t-name\t\tonly print symbol names\n"
+ );
+}
+
+int main(int argc, const char* argv[])
+{
+ if(argc<2) {
+ usage();
+ return 0;
+ }
+
+ ObjectFile::ReaderOptions options;
+ options.fAddCompactUnwindEncoding = true;
+ try {
+ for(int i=1; i < argc; ++i) {
+ const char* arg = argv[i];
+ if ( arg[0] == '-' ) {
+ if ( strcmp(arg, "-no_content") == 0 ) {
+ sDumpContent = false;
+ }
+ else if ( strcmp(arg, "-nm") == 0 ) {
+ sNMmode = true;
+ }
+ else if ( strcmp(arg, "-stabs") == 0 ) {
+ sDumpStabs = true;
+ }
+ else if ( strcmp(arg, "-no_sort") == 0 ) {
+ sSort = false;
+ }
+ else if ( strcmp(arg, "-arch") == 0 ) {
+ const char* arch = ++i<argc? argv[i]: "";
+ if ( strcmp(arch, "ppc64") == 0 )
+ sPreferredArch = CPU_TYPE_POWERPC64;
+ else if ( strcmp(arch, "ppc") == 0 )
+ sPreferredArch = CPU_TYPE_POWERPC;
+ else if ( strcmp(arch, "i386") == 0 )
+ sPreferredArch = CPU_TYPE_I386;
+ else if ( strcmp(arch, "x86_64") == 0 )
+ sPreferredArch = CPU_TYPE_X86_64;
+ else if ( strcmp(arch, "arm") == 0 )
+ sPreferredArch = CPU_TYPE_ARM;
+ else if ( strcmp(arch, "armv6") == 0 )
+ sPreferredArch = CPU_TYPE_ARM;
+ else
+ throwf("unknown architecture %s", arch);
+ }
+ else if ( strcmp(arg, "-only") == 0 ) {
+ sMatchName = ++i<argc? argv[i]: NULL;
+ }
+ else if ( strcmp(arg, "-align") == 0 ) {
+ sPrintRestrict = true;
+ sPrintAlign = true;
+ }
+ else if ( strcmp(arg, "-name") == 0 ) {
+ sPrintRestrict = true;
+ sPrintName = true;
+ }
+ else {
+ usage();
+ throwf("unknown option: %s\n", arg);
+ }
+ }
+ else {
+ ObjectFile::Reader* reader = createReader(arg, options);
+ dumpFile(reader);
+ }
+ }
+ }
+ catch (const char* msg) {
+ fprintf(stderr, "ObjDump failed: %s\n", msg);
+ return 1;
+ }
+
+ return 0;
+}
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2008 Apple 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 <vector>
+
+#include "MachOFileAbstraction.hpp"
+#include "MachOTrie.hpp"
+#include "prune_trie.h"
+
+
+
+
+/*
+ * prune_trie() is a C vended function that is used by strip(1) to prune out
+ * defined exported symbols from the export trie. It is passed a pointer to
+ * the start of bytes of the the trie and the size. The prune() funciton
+ * passed is called with each symbol name in the trie to determine if it is
+ * to be pruned (retuning 1) or not (returning 0). It writes the new trie
+ * back into the trie buffer and returns the new size in trie_new_size.
+ * If the pruning succeeds, NULL is returned. If there was an error processing
+ * the trie (e.f. it is malformed), then an error message string is returned.
+ * The error string can be freed.
+ */
+const char*
+prune_trie(
+ uint8_t* trie_start,
+ uint32_t trie_start_size,
+ int (*prune)(const char *name),
+ uint32_t* trie_new_size)
+{
+ // convert trie to vector of entries
+ std::vector<mach_o::trie::Entry> originalExports;
+ try {
+ parseTrie(trie_start, trie_start+trie_start_size, originalExports);
+ }
+ catch (const char* msg) {
+ return strdup(msg);
+ }
+ catch (...) {
+ return strdup("unexpected exception processing trie");
+ }
+
+ // prune entries into new vector of entries
+ std::vector<mach_o::trie::Entry> newExports;
+ newExports.reserve(originalExports.size());
+ for(std::vector<mach_o::trie::Entry>::iterator it = originalExports.begin(); it != originalExports.end(); ++it) {
+ if ( prune(it->name) == 0 )
+ newExports.push_back(*it);
+ }
+
+ // create new export trie
+ std::vector<uint8_t> newExportTrieBytes;
+ newExportTrieBytes.reserve(trie_start_size);
+ mach_o::trie::makeTrie(newExports, newExportTrieBytes);
+ // Need to align trie to 8 or 4 bytes. We don't know the arch, but if the incoming trie
+ // was not 8-byte aligned, then it can't be a 64-bit arch, so use 4-byte alignement.
+ if ( (trie_start_size % 8) != 0 ) {
+ // 4-byte align
+ while ( (newExportTrieBytes.size() % 4 ) != 0)
+ newExportTrieBytes.push_back(0);
+ }
+ else {
+ // 8-byte align
+ while ( (newExportTrieBytes.size() % 8 ) != 0)
+ newExportTrieBytes.push_back(0);
+ }
+
+ // copy into place, zero pad
+ *trie_new_size = newExportTrieBytes.size();
+ if ( *trie_new_size > trie_start_size ) {
+ char* msg;
+ asprintf(&msg, "new trie is larger (%d) than original (%d)", *trie_new_size, trie_start_size);
+ return msg;
+ }
+ memcpy(trie_start, &newExportTrieBytes[0], *trie_new_size);
+ bzero(trie_start+*trie_new_size, trie_start_size - *trie_new_size);
+
+ // success
+ return NULL;
+}
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2008 Apple 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 <stdarg.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <vector>
+#include <set>
+#include <ext/hash_set>
+
+#include "MachOFileAbstraction.hpp"
+#include "Architectures.hpp"
+#include "MachOTrie.hpp"
+
+static bool printRebase = false;
+static bool printBind = false;
+static bool printWeakBind = false;
+static bool printLazyBind = false;
+static bool printOpcodes = false;
+static bool printExport = false;
+static bool printExportGraph = false;
+static cpu_type_t sPreferredArch = CPU_TYPE_I386;
+
+
+ __attribute__((noreturn))
+void throwf(const char* format, ...)
+{
+ va_list list;
+ char* p;
+ va_start(list, format);
+ vasprintf(&p, format, list);
+ va_end(list);
+
+ const char* t = p;
+ throw t;
+}
+
+
+template <typename A>
+class DyldInfoPrinter
+{
+public:
+ static bool validFile(const uint8_t* fileContent);
+ static DyldInfoPrinter<A>* make(const uint8_t* fileContent, uint32_t fileLength, const char* path)
+ { return new DyldInfoPrinter<A>(fileContent, fileLength, path); }
+ virtual ~DyldInfoPrinter() {}
+
+
+private:
+ typedef typename A::P P;
+ typedef typename A::P::E E;
+ typedef typename A::P::uint_t pint_t;
+
+ class CStringEquals
+ {
+ public:
+ bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
+ };
+
+ typedef __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> StringSet;
+
+ DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path);
+ void printRebaseInfo();
+ void printRebaseInfoOpcodes();
+ void printBindingInfo();
+ void printWeakBindingInfo();
+ void printLazyBindingInfo();
+ void printBindingInfoOpcodes(bool weakBinding);
+ void printWeakBindingInfoOpcodes();
+ void printLazyBindingOpcodes();
+ void printExportInfo();
+ void printExportInfoGraph();
+ void processExportNode(const uint8_t* const start, const uint8_t* p, const uint8_t* const end,
+ char* cummulativeString, int curStrOffset);
+ void processExportGraphNode(const uint8_t* const start, const uint8_t* const end,
+ const uint8_t* parent, const uint8_t* p,
+ char* cummulativeString, int curStrOffset);
+ const char* rebaseTypeName(uint8_t type);
+ const char* bindTypeName(uint8_t type);
+ pint_t segStartAddress(uint8_t segIndex);
+ const char* segmentName(uint8_t segIndex);
+ const char* sectionName(uint8_t segIndex, pint_t address);
+ const char* getSegAndSectName(uint8_t segIndex, pint_t address);
+ const char* ordinalName(int libraryOrdinal);
+
+
+ const char* fPath;
+ const macho_header<P>* fHeader;
+ uint64_t fLength;
+ const char* fStrings;
+ const char* fStringsEnd;
+ const macho_nlist<P>* fSymbols;
+ uint32_t fSymbolCount;
+ const macho_dyld_info_command<P>* fInfo;
+ uint64_t fBaseAddress;
+ std::vector<const macho_segment_command<P>*>fSegments;
+ std::vector<const char*> fDylibs;
+};
+
+
+
+template <>
+bool DyldInfoPrinter<ppc>::validFile(const uint8_t* fileContent)
+{
+ 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;
+ switch (header->filetype()) {
+ case MH_EXECUTE:
+ case MH_DYLIB:
+ case MH_BUNDLE:
+ case MH_DYLINKER:
+ return true;
+ }
+ return false;
+}
+
+template <>
+bool DyldInfoPrinter<ppc64>::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_POWERPC64 )
+ return false;
+ switch (header->filetype()) {
+ case MH_EXECUTE:
+ case MH_DYLIB:
+ case MH_BUNDLE:
+ case MH_DYLINKER:
+ return true;
+ }
+ return false;
+}
+
+template <>
+bool DyldInfoPrinter<x86>::validFile(const uint8_t* fileContent)
+{
+ 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;
+ switch (header->filetype()) {
+ case MH_EXECUTE:
+ case MH_DYLIB:
+ case MH_BUNDLE:
+ case MH_DYLINKER:
+ return true;
+ }
+ return false;
+}
+
+template <>
+bool DyldInfoPrinter<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 <>
+bool DyldInfoPrinter<arm>::validFile(const uint8_t* fileContent)
+{
+ const macho_header<P>* header = (const macho_header<P>*)fileContent;
+ if ( header->magic() != MH_MAGIC )
+ return false;
+ if ( header->cputype() != CPU_TYPE_ARM )
+ return false;
+ switch (header->filetype()) {
+ case MH_EXECUTE:
+ case MH_DYLIB:
+ case MH_BUNDLE:
+ case MH_DYLINKER:
+ return true;
+ }
+ return false;
+}
+
+
+template <typename A>
+DyldInfoPrinter<A>::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path)
+ : fHeader(NULL), fLength(fileLength),
+ fStrings(NULL), fStringsEnd(NULL), fSymbols(NULL), fSymbolCount(0), fInfo(NULL), fBaseAddress(0)
+{
+ // sanity check
+ if ( ! validFile(fileContent) )
+ throw "not a mach-o file that can be checked";
+
+ fPath = strdup(path);
+ fHeader = (const macho_header<P>*)fileContent;
+
+ // get LC_DYLD_INFO
+ const uint8_t* const endOfFile = (uint8_t*)fHeader + fLength;
+ const uint8_t* const endOfLoadCommands = (uint8_t*)fHeader + sizeof(macho_header<P>) + fHeader->sizeofcmds();
+ const uint32_t cmd_count = fHeader->ncmds();
+ const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
+ const macho_load_command<P>* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ uint32_t size = cmd->cmdsize();
+ const uint8_t* endOfCmd = ((uint8_t*)cmd)+cmd->cmdsize();
+ if ( endOfCmd > endOfLoadCommands )
+ throwf("load command #%d extends beyond the end of the load commands", i);
+ if ( endOfCmd > endOfFile )
+ throwf("load command #%d extends beyond the end of the file", i);
+ switch ( cmd->cmd() ) {
+ case LC_DYLD_INFO:
+ case LC_DYLD_INFO_ONLY:
+ fInfo = (macho_dyld_info_command<P>*)cmd;
+ break;
+ case macho_segment_command<P>::CMD:
+ {
+ const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd;
+ fSegments.push_back(segCmd);
+ if ( (segCmd->fileoff() == 0) && (segCmd->filesize() != 0) )
+ fBaseAddress = segCmd->vmaddr();
+ }
+ break;
+ case LC_LOAD_DYLIB:
+ case LC_LOAD_WEAK_DYLIB:
+ case LC_REEXPORT_DYLIB:
+ case LC_LAZY_LOAD_DYLIB:
+ {
+ const macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd;
+ const char* lastSlash = strrchr(dylib->name(), '/');
+ const char* leafName = (lastSlash != NULL) ? lastSlash+1 : dylib->name();
+ const char* firstDot = strchr(leafName, '.');
+ if ( firstDot != NULL ) {
+ char* t = strdup(leafName);
+ t[firstDot-leafName] = '\0';
+ fDylibs.push_back(t);
+ }
+ else {
+ fDylibs.push_back(leafName);
+ }
+ }
+ break;
+ }
+ cmd = (const macho_load_command<P>*)endOfCmd;
+ }
+
+ if ( printRebase )
+ printRebaseInfo();
+ if ( printBind )
+ printBindingInfo();
+ if ( printWeakBind )
+ printWeakBindingInfo();
+ if ( printLazyBind )
+ printLazyBindingInfo();
+ if ( printExport )
+ printExportInfo();
+ if ( printOpcodes ) {
+ printRebaseInfoOpcodes();
+ printBindingInfoOpcodes(false);
+ printBindingInfoOpcodes(true);
+ printLazyBindingOpcodes();
+ }
+ if ( printExportGraph )
+ printExportInfoGraph();
+}
+
+static uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end)
+{
+ uint64_t result = 0;
+ int bit = 0;
+ do {
+ if (p == end)
+ throwf("malformed uleb128");
+
+ uint64_t slice = *p & 0x7f;
+
+ if (bit >= 64 || slice << bit >> bit != slice)
+ throwf("uleb128 too big");
+ else {
+ result |= (slice << bit);
+ bit += 7;
+ }
+ }
+ while (*p++ & 0x80);
+ return result;
+}
+
+static int64_t read_sleb128(const uint8_t*& p, const uint8_t* end)
+{
+ int64_t result = 0;
+ int bit = 0;
+ uint8_t byte;
+ do {
+ if (p == end)
+ throwf("malformed sleb128");
+ byte = *p++;
+ result |= ((byte & 0x7f) << bit);
+ bit += 7;
+ } while (byte & 0x80);
+ // sign extend negative numbers
+ if ( (byte & 0x40) != 0 )
+ result |= (-1LL) << bit;
+ return result;
+}
+
+
+template <typename A>
+const char* DyldInfoPrinter<A>::rebaseTypeName(uint8_t type)
+{
+ switch (type ){
+ case REBASE_TYPE_POINTER:
+ return "pointer";
+ case REBASE_TYPE_TEXT_ABSOLUTE32:
+ return "text abs32";
+ case REBASE_TYPE_TEXT_PCREL32:
+ return "text rel32";
+ }
+ return "!!unknown!!";
+}
+
+
+template <typename A>
+const char* DyldInfoPrinter<A>::bindTypeName(uint8_t type)
+{
+ switch (type ){
+ case BIND_TYPE_POINTER:
+ return "pointer";
+ case BIND_TYPE_TEXT_ABSOLUTE32:
+ return "text abs32";
+ case BIND_TYPE_TEXT_PCREL32:
+ return "text rel32";
+ }
+ return "!!unknown!!";
+}
+
+
+template <typename A>
+typename A::P::uint_t DyldInfoPrinter<A>::segStartAddress(uint8_t segIndex)
+{
+ if ( segIndex > fSegments.size() )
+ throw "segment index out of range";
+ return fSegments[segIndex]->vmaddr();
+}
+
+template <typename A>
+const char* DyldInfoPrinter<A>::segmentName(uint8_t segIndex)
+{
+ if ( segIndex > fSegments.size() )
+ throw "segment index out of range";
+ return fSegments[segIndex]->segname();
+}
+
+template <typename A>
+const char* DyldInfoPrinter<A>::sectionName(uint8_t segIndex, pint_t address)
+{
+ if ( segIndex > fSegments.size() )
+ throw "segment index out of range";
+ const macho_segment_command<P>* segCmd = fSegments[segIndex];
+ macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
+ macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()];
+ for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
+ if ( (sect->addr() <= address) && (address < (sect->addr()+sect->size())) ) {
+ if ( strlen(sect->sectname()) > 15 ) {
+ static char temp[18];
+ strlcpy(temp, sect->sectname(), 17);
+ return temp;
+ }
+ else {
+ return sect->sectname();
+ }
+ }
+ }
+ return "??";
+}
+
+template <typename A>
+const char* DyldInfoPrinter<A>::getSegAndSectName(uint8_t segIndex, pint_t address)
+{
+ static char buffer[64];
+ strcpy(buffer, segmentName(segIndex));
+ strcat(buffer, "/");
+ const macho_segment_command<P>* segCmd = fSegments[segIndex];
+ macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
+ macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()];
+ for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
+ if ( (sect->addr() <= address) && (address < (sect->addr()+sect->size())) ) {
+ // section name may not be zero terminated
+ char* end = &buffer[strlen(buffer)];
+ strlcpy(end, sect->sectname(), 16);
+ return buffer;
+ }
+ }
+ return "??";
+}
+
+template <typename A>
+const char* DyldInfoPrinter<A>::ordinalName(int libraryOrdinal)
+{
+ switch ( libraryOrdinal) {
+ case BIND_SPECIAL_DYLIB_SELF:
+ return "this-image";
+ case BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE:
+ return "main-executable";
+ case BIND_SPECIAL_DYLIB_FLAT_LOOKUP:
+ return "flat-namespace";
+ }
+ if ( libraryOrdinal < BIND_SPECIAL_DYLIB_FLAT_LOOKUP )
+ throw "unknown special ordinal";
+ if ( libraryOrdinal > fDylibs.size() )
+ throw "libraryOrdinal out of range";
+ return fDylibs[libraryOrdinal-1];
+}
+
+
+template <typename A>
+void DyldInfoPrinter<A>::printRebaseInfo()
+{
+ if ( (fInfo == NULL) || (fInfo->rebase_off() == 0) ) {
+ printf("no compressed rebase info\n");
+ }
+ else {
+ printf("rebase information:\n");
+ printf("segment section address type\n");
+
+ const uint8_t* p = (uint8_t*)fHeader + fInfo->rebase_off();
+ const uint8_t* end = &p[fInfo->rebase_size()];
+
+ uint8_t type = 0;
+ uint64_t segOffset = 0;
+ uint32_t count;
+ uint32_t skip;
+ int segIndex;
+ pint_t segStartAddr = 0;
+ const char* segName = "??";
+ const char* typeName = "??";
+ bool done = false;
+ while ( !done && (p < end) ) {
+ uint8_t immediate = *p & REBASE_IMMEDIATE_MASK;
+ uint8_t opcode = *p & REBASE_OPCODE_MASK;
+ ++p;
+ switch (opcode) {
+ case REBASE_OPCODE_DONE:
+ done = true;
+ break;
+ case REBASE_OPCODE_SET_TYPE_IMM:
+ type = immediate;
+ typeName = rebaseTypeName(type);
+ break;
+ case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ segIndex = immediate;
+ segStartAddr = segStartAddress(segIndex);
+ segName = segmentName(segIndex);
+ segOffset = read_uleb128(p, end);
+ break;
+ case REBASE_OPCODE_ADD_ADDR_ULEB:
+ segOffset += read_uleb128(p, end);
+ break;
+ case REBASE_OPCODE_ADD_ADDR_IMM_SCALED:
+ segOffset += immediate*sizeof(pint_t);
+ break;
+ case REBASE_OPCODE_DO_REBASE_IMM_TIMES:
+ for (int i=0; i < immediate; ++i) {
+ printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
+ segOffset += sizeof(pint_t);
+ }
+ break;
+ case REBASE_OPCODE_DO_REBASE_ULEB_TIMES:
+ count = read_uleb128(p, end);
+ for (uint32_t i=0; i < count; ++i) {
+ printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
+ segOffset += sizeof(pint_t);
+ }
+ break;
+ case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
+ printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
+ segOffset += read_uleb128(p, end) + sizeof(pint_t);
+ break;
+ case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB:
+ count = read_uleb128(p, end);
+ skip = read_uleb128(p, end);
+ for (uint32_t i=0; i < count; ++i) {
+ printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
+ segOffset += skip + sizeof(pint_t);
+ }
+ break;
+ default:
+ throwf("bad rebase opcode %d", *p);
+ }
+ }
+ }
+
+}
+
+
+
+template <typename A>
+void DyldInfoPrinter<A>::printRebaseInfoOpcodes()
+{
+ if ( (fInfo == NULL) || (fInfo->rebase_off() == 0) ) {
+ printf("no compressed rebase info\n");
+ }
+ else {
+ printf("rebase opcodes:\n");
+ const uint8_t* p = (uint8_t*)fHeader + fInfo->rebase_off();
+ const uint8_t* end = &p[fInfo->rebase_size()];
+
+ uint8_t type = 0;
+ uint64_t address = fBaseAddress;
+ uint32_t count;
+ uint32_t skip;
+ unsigned int segmentIndex;
+ bool done = false;
+ while ( !done && (p < end) ) {
+ uint8_t immediate = *p & REBASE_IMMEDIATE_MASK;
+ uint8_t opcode = *p & REBASE_OPCODE_MASK;
+ ++p;
+ switch (opcode) {
+ case REBASE_OPCODE_DONE:
+ done = true;
+ printf("REBASE_OPCODE_DONE()\n");
+ break;
+ case REBASE_OPCODE_SET_TYPE_IMM:
+ type = immediate;
+ printf("REBASE_OPCODE_SET_TYPE_IMM(%d)\n", type);
+ break;
+ case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ segmentIndex = immediate;
+ address = read_uleb128(p, end);
+ printf("REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(%d, 0x%08llX)\n", segmentIndex, address);
+ break;
+ case REBASE_OPCODE_ADD_ADDR_ULEB:
+ address = read_uleb128(p, end);
+ printf("REBASE_OPCODE_ADD_ADDR_ULEB(0x%0llX)\n", address);
+ break;
+ case REBASE_OPCODE_ADD_ADDR_IMM_SCALED:
+ address = immediate*sizeof(pint_t);
+ printf("REBASE_OPCODE_ADD_ADDR_IMM_SCALED(0x%0llX)\n", address);
+ break;
+ case REBASE_OPCODE_DO_REBASE_IMM_TIMES:
+ printf("REBASE_OPCODE_DO_REBASE_IMM_TIMES(%d)\n", immediate);
+ break;
+ case REBASE_OPCODE_DO_REBASE_ULEB_TIMES:
+ count = read_uleb128(p, end);
+ printf("REBASE_OPCODE_DO_REBASE_ULEB_TIMES(%d)\n", count);
+ break;
+ case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
+ skip = read_uleb128(p, end) + sizeof(pint_t);
+ printf("REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB(%d)\n", skip);
+ break;
+ case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB:
+ count = read_uleb128(p, end);
+ skip = read_uleb128(p, end);
+ printf("REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB(%d, %d)\n", count, skip);
+ break;
+ default:
+ throwf("bad rebase opcode %d", *p);
+ }
+ }
+ }
+
+}
+
+
+
+
+
+
+template <typename A>
+void DyldInfoPrinter<A>::printBindingInfoOpcodes(bool weakbinding)
+{
+ if ( fInfo == NULL ) {
+ printf("no compressed binding info\n");
+ }
+ else if ( !weakbinding && (fInfo->bind_off() == 0) ) {
+ printf("no compressed binding info\n");
+ }
+ else if ( weakbinding && (fInfo->weak_bind_off() == 0) ) {
+ printf("no compressed weak binding info\n");
+ }
+ else {
+ const uint8_t* start;
+ const uint8_t* end;
+ if ( weakbinding ) {
+ printf("weak binding opcodes:\n");
+ start = (uint8_t*)fHeader + fInfo->weak_bind_off();
+ end = &start[fInfo->weak_bind_size()];
+ }
+ else {
+ printf("binding opcodes:\n");
+ start = (uint8_t*)fHeader + fInfo->bind_off();
+ end = &start[fInfo->bind_size()];
+ }
+ const uint8_t* p = start;
+ uint8_t type = 0;
+ uint8_t flags;
+ uint64_t address = fBaseAddress;
+ const char* symbolName = NULL;
+ int libraryOrdinal = 0;
+ int64_t addend = 0;
+ uint32_t segmentIndex = 0;
+ uint32_t count;
+ uint32_t skip;
+ bool done = false;
+ while ( !done && (p < end) ) {
+ uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
+ uint8_t opcode = *p & BIND_OPCODE_MASK;
+ uint32_t opcodeOffset = p-start;
+ ++p;
+ switch (opcode) {
+ case BIND_OPCODE_DONE:
+ done = true;
+ printf("0x%04X BIND_OPCODE_DONE\n", opcodeOffset);
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
+ libraryOrdinal = immediate;
+ printf("0x%04X BIND_OPCODE_SET_DYLIB_ORDINAL_IMM(%d)\n", opcodeOffset, libraryOrdinal);
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
+ libraryOrdinal = read_uleb128(p, end);
+ printf("0x%04X BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB(%d)\n", opcodeOffset, libraryOrdinal);
+ break;
+ case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
+ // the special ordinals are negative numbers
+ if ( immediate == 0 )
+ libraryOrdinal = 0;
+ else {
+ int8_t signExtended = BIND_OPCODE_MASK | immediate;
+ libraryOrdinal = signExtended;
+ }
+ printf("0x%04X BIND_OPCODE_SET_DYLIB_SPECIAL_IMM(%d)\n", opcodeOffset, libraryOrdinal);
+ break;
+ case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
+ flags = immediate;
+ symbolName = (char*)p;
+ while (*p != '\0')
+ ++p;
+ ++p;
+ printf("0x%04X BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM(0x%02X, %s)\n", opcodeOffset, flags, symbolName);
+ break;
+ case BIND_OPCODE_SET_TYPE_IMM:
+ type = immediate;
+ printf("0x%04X BIND_OPCODE_SET_TYPE_IMM(%d)\n", opcodeOffset, type);
+ break;
+ case BIND_OPCODE_SET_ADDEND_SLEB:
+ addend = read_sleb128(p, end);
+ printf("0x%04X BIND_OPCODE_SET_ADDEND_SLEB(%lld)\n", opcodeOffset, addend);
+ break;
+ case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ segmentIndex = immediate;
+ address = read_uleb128(p, end);
+ printf("0x%04X BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(0x%02X, 0x%08llX)\n", opcodeOffset, segmentIndex, address);
+ break;
+ case BIND_OPCODE_ADD_ADDR_ULEB:
+ skip = read_uleb128(p, end);
+ printf("0x%04X BIND_OPCODE_ADD_ADDR_ULEB(0x%08X)\n", opcodeOffset, skip);
+ break;
+ case BIND_OPCODE_DO_BIND:
+ printf("0x%04X BIND_OPCODE_DO_BIND()\n", opcodeOffset);
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
+ skip = read_uleb128(p, end);
+ printf("0x%04X BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB(0x%08X)\n", opcodeOffset, skip);
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
+ skip = immediate*sizeof(pint_t) + sizeof(pint_t);
+ printf("0x%04X BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED(0x%08X)\n", opcodeOffset, skip);
+ break;
+ case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
+ count = read_uleb128(p, end);
+ skip = read_uleb128(p, end);
+ printf("0x%04X BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB(%d, 0x%08X)\n", opcodeOffset, count, skip);
+ break;
+ default:
+ throwf("unknown bind opcode %d", *p);
+ }
+ }
+ }
+
+}
+
+
+
+template <typename A>
+void DyldInfoPrinter<A>::printBindingInfo()
+{
+ if ( (fInfo == NULL) || (fInfo->bind_off() == 0) ) {
+ printf("no compressed binding info\n");
+ }
+ else {
+ printf("bind information:\n");
+ printf("segment section address type weak addend dylib symbol\n");
+ const uint8_t* p = (uint8_t*)fHeader + fInfo->bind_off();
+ const uint8_t* end = &p[fInfo->bind_size()];
+
+ uint8_t type = 0;
+ uint8_t segIndex = 0;
+ uint64_t segOffset = 0;
+ const char* symbolName = NULL;
+ const char* fromDylib = "??";
+ int libraryOrdinal = 0;
+ int64_t addend = 0;
+ uint32_t count;
+ uint32_t skip;
+ pint_t segStartAddr = 0;
+ const char* segName = "??";
+ const char* typeName = "??";
+ const char* weak_import = "";
+ bool done = false;
+ while ( !done && (p < end) ) {
+ uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
+ uint8_t opcode = *p & BIND_OPCODE_MASK;
+ ++p;
+ switch (opcode) {
+ case BIND_OPCODE_DONE:
+ done = true;
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
+ libraryOrdinal = immediate;
+ fromDylib = ordinalName(libraryOrdinal);
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
+ libraryOrdinal = read_uleb128(p, end);
+ fromDylib = ordinalName(libraryOrdinal);
+ break;
+ case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
+ // the special ordinals are negative numbers
+ if ( immediate == 0 )
+ libraryOrdinal = 0;
+ else {
+ int8_t signExtended = BIND_OPCODE_MASK | immediate;
+ libraryOrdinal = signExtended;
+ }
+ fromDylib = ordinalName(libraryOrdinal);
+ break;
+ case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
+ symbolName = (char*)p;
+ while (*p != '\0')
+ ++p;
+ ++p;
+ if ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 )
+ weak_import = "weak";
+ else
+ weak_import = "";
+ break;
+ case BIND_OPCODE_SET_TYPE_IMM:
+ type = immediate;
+ typeName = bindTypeName(type);
+ break;
+ case BIND_OPCODE_SET_ADDEND_SLEB:
+ addend = read_sleb128(p, end);
+ break;
+ case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ segIndex = immediate;
+ segStartAddr = segStartAddress(segIndex);
+ segName = segmentName(segIndex);
+ segOffset = read_uleb128(p, end);
+ break;
+ case BIND_OPCODE_ADD_ADDR_ULEB:
+ segOffset += read_uleb128(p, end);
+ break;
+ case BIND_OPCODE_DO_BIND:
+ printf("%-7s %-16s 0x%08llX %10s %4s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, weak_import, addend, fromDylib, symbolName );
+ segOffset += sizeof(pint_t);
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
+ printf("%-7s %-16s 0x%08llX %10s %4s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, weak_import, addend, fromDylib, symbolName );
+ segOffset += read_uleb128(p, end) + sizeof(pint_t);
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
+ printf("%-7s %-16s 0x%08llX %10s %4s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, weak_import, addend, fromDylib, symbolName );
+ segOffset += immediate*sizeof(pint_t) + sizeof(pint_t);
+ break;
+ case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
+ count = read_uleb128(p, end);
+ skip = read_uleb128(p, end);
+ for (uint32_t i=0; i < count; ++i) {
+ printf("%-7s %-16s 0x%08llX %10s %4s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, weak_import, addend, fromDylib, symbolName );
+ segOffset += skip + sizeof(pint_t);
+ }
+ break;
+ default:
+ throwf("bad bind opcode %d", *p);
+ }
+ }
+ }
+
+}
+
+template <typename A>
+void DyldInfoPrinter<A>::printWeakBindingInfo()
+{
+ if ( (fInfo == NULL) || (fInfo->weak_bind_off() == 0) ) {
+ printf("no weak binding\n");
+ }
+ else {
+ printf("weak binding information:\n");
+ printf("segment section address type addend symbol\n");
+ const uint8_t* p = (uint8_t*)fHeader + fInfo->weak_bind_off();
+ const uint8_t* end = &p[fInfo->weak_bind_size()];
+
+ uint8_t type = 0;
+ uint8_t segIndex = 0;
+ uint64_t segOffset = 0;
+ const char* symbolName = NULL;
+ int64_t addend = 0;
+ uint32_t count;
+ uint32_t skip;
+ pint_t segStartAddr = 0;
+ const char* segName = "??";
+ const char* typeName = "??";
+ bool done = false;
+ while ( !done && (p < end) ) {
+ uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
+ uint8_t opcode = *p & BIND_OPCODE_MASK;
+ ++p;
+ switch (opcode) {
+ case BIND_OPCODE_DONE:
+ done = true;
+ break;
+ case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
+ symbolName = (char*)p;
+ while (*p != '\0')
+ ++p;
+ ++p;
+ if ( (immediate & BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION) != 0 )
+ printf(" strong %s\n", symbolName );
+ break;
+ case BIND_OPCODE_SET_TYPE_IMM:
+ type = immediate;
+ typeName = bindTypeName(type);
+ break;
+ case BIND_OPCODE_SET_ADDEND_SLEB:
+ addend = read_sleb128(p, end);
+ break;
+ case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ segIndex = immediate;
+ segStartAddr = segStartAddress(segIndex);
+ segName = segmentName(segIndex);
+ segOffset = read_uleb128(p, end);
+ break;
+ case BIND_OPCODE_ADD_ADDR_ULEB:
+ segOffset += read_uleb128(p, end);
+ break;
+ case BIND_OPCODE_DO_BIND:
+ printf("%-7s %-16s 0x%08llX %10s %5lld %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, symbolName );
+ segOffset += sizeof(pint_t);
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
+ printf("%-7s %-16s 0x%08llX %10s %5lld %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, symbolName );
+ segOffset += read_uleb128(p, end) + sizeof(pint_t);
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
+ printf("%-7s %-16s 0x%08llX %10s %5lld %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, symbolName );
+ segOffset += immediate*sizeof(pint_t) + sizeof(pint_t);
+ break;
+ case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
+ count = read_uleb128(p, end);
+ skip = read_uleb128(p, end);
+ for (uint32_t i=0; i < count; ++i) {
+ printf("%-7s %-16s 0x%08llX %10s %5lld %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, symbolName );
+ segOffset += skip + sizeof(pint_t);
+ }
+ break;
+ default:
+ throwf("unknown weak bind opcode %d", *p);
+ }
+ }
+ }
+
+}
+
+
+template <typename A>
+void DyldInfoPrinter<A>::printLazyBindingInfo()
+{
+ if ( fInfo == NULL ) {
+ printf("no compressed dyld info\n");
+ }
+ else if ( fInfo->lazy_bind_off() == 0 ) {
+ printf("no compressed lazy binding info\n");
+ }
+ else {
+ printf("lazy binding information:\n");
+ printf("segment section address index dylib symbol\n");
+ const uint8_t* const start = (uint8_t*)fHeader + fInfo->lazy_bind_off();
+ const uint8_t* const end = &start[fInfo->lazy_bind_size()];
+
+ uint8_t type = BIND_TYPE_POINTER;
+ uint8_t segIndex = 0;
+ uint64_t segOffset = 0;
+ const char* symbolName = NULL;
+ const char* fromDylib = "??";
+ int libraryOrdinal = 0;
+ int64_t addend = 0;
+ uint32_t lazy_offset = 0;
+ pint_t segStartAddr = 0;
+ const char* segName = "??";
+ const char* typeName = "??";
+ for (const uint8_t* p=start; p < end; ) {
+ uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
+ uint8_t opcode = *p & BIND_OPCODE_MASK;
+ ++p;
+ switch (opcode) {
+ case BIND_OPCODE_DONE:
+ lazy_offset = p-start;
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
+ libraryOrdinal = immediate;
+ fromDylib = ordinalName(libraryOrdinal);
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
+ libraryOrdinal = read_uleb128(p, end);
+ fromDylib = ordinalName(libraryOrdinal);
+ break;
+ case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
+ // the special ordinals are negative numbers
+ if ( immediate == 0 )
+ libraryOrdinal = 0;
+ else {
+ int8_t signExtended = BIND_OPCODE_MASK | immediate;
+ libraryOrdinal = signExtended;
+ }
+ fromDylib = ordinalName(libraryOrdinal);
+ break;
+ case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
+ symbolName = (char*)p;
+ while (*p != '\0')
+ ++p;
+ ++p;
+ break;
+ case BIND_OPCODE_SET_TYPE_IMM:
+ type = immediate;
+ typeName = bindTypeName(type);
+ break;
+ case BIND_OPCODE_SET_ADDEND_SLEB:
+ addend = read_sleb128(p, end);
+ break;
+ case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ segIndex = immediate;
+ segStartAddr = segStartAddress(segIndex);
+ segName = segmentName(segIndex);
+ segOffset = read_uleb128(p, end);
+ break;
+ case BIND_OPCODE_ADD_ADDR_ULEB:
+ segOffset += read_uleb128(p, end);
+ break;
+ case BIND_OPCODE_DO_BIND:
+ printf("%-7s %-16s 0x%08llX 0x%04X %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, lazy_offset, fromDylib, symbolName );
+ segOffset += sizeof(pint_t);
+ break;
+ default:
+ throwf("bad lazy bind opcode %d", *p);
+ }
+ }
+ }
+
+}
+
+#if 0
+ uint8_t type = BIND_TYPE_POINTER;
+ uint8_t flags;
+ uint64_t address = fBaseAddress;
+ const char* symbolName = NULL;
+ int libraryOrdinal = 0;
+ int64_t addend = 0;
+ uint32_t segmentIndex = 0;
+ uint32_t count;
+ uint32_t skip;
+ for (const uint8_t* p = start; p < end; ) {
+ uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
+ uint8_t opcode = *p & BIND_OPCODE_MASK;
+ uint32_t opcodeOffset = p-start;
+ ++p;
+ switch (opcode) {
+ case BIND_OPCODE_DONE:
+ printf("0x%08X BIND_OPCODE_DONE\n", opcodeOffset);
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
+ libraryOrdinal = immediate;
+ printf("0x%08X BIND_OPCODE_SET_DYLIB_ORDINAL_IMM(%d)\n", opcodeOffset, libraryOrdinal);
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
+ libraryOrdinal = read_uleb128(p, end);
+ printf("0x%08X BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB(%d)\n", opcodeOffset, libraryOrdinal);
+ break;
+ case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
+ // the special ordinals are negative numbers
+ if ( immediate == 0 )
+ libraryOrdinal = 0;
+ else {
+ int8_t signExtended = BIND_OPCODE_MASK | immediate;
+ libraryOrdinal = signExtended;
+ }
+ printf("0x%08X BIND_OPCODE_SET_DYLIB_SPECIAL_IMM(%d)\n", opcodeOffset, libraryOrdinal);
+ break;
+ case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
+ flags = immediate;
+ symbolName = (char*)p;
+ while (*p != '\0')
+ ++p;
+ ++p;
+ printf("0x%08X BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM(0x%02X, %s)\n", opcodeOffset, flags, symbolName);
+ break;
+ case BIND_OPCODE_SET_TYPE_IMM:
+ type = immediate;
+ printf("0x%08X BIND_OPCODE_SET_TYPE_IMM(%d)\n", opcodeOffset, type);
+ break;
+ case BIND_OPCODE_SET_ADDEND_SLEB:
+ addend = read_sleb128(p, end);
+ printf("0x%08X BIND_OPCODE_SET_ADDEND_SLEB(%lld)\n", opcodeOffset, addend);
+ break;
+ case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ segmentIndex = immediate;
+ address = read_uleb128(p, end);
+ printf("0x%08X BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(0x%02X, 0x%08llX)\n", opcodeOffset, segmentIndex, address);
+ break;
+ case BIND_OPCODE_ADD_ADDR_ULEB:
+ skip = read_uleb128(p, end);
+ printf("0x%08X BIND_OPCODE_ADD_ADDR_ULEB(0x%08X)\n", opcodeOffset, skip);
+ break;
+ case BIND_OPCODE_DO_BIND:
+ printf("0x%08X BIND_OPCODE_DO_BIND()\n", opcodeOffset);
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
+ skip = read_uleb128(p, end);
+ printf("0x%08X BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB(0x%08X)\n", opcodeOffset, skip);
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
+ skip = immediate*sizeof(pint_t) + sizeof(pint_t);
+ printf("0x%08X BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED(0x%08X)\n", opcodeOffset, skip);
+ break;
+ case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
+ count = read_uleb128(p, end);
+ skip = read_uleb128(p, end);
+ printf("0x%08X BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB(%d, 0x%08X)\n", opcodeOffset, count, skip);
+ break;
+ default:
+ throwf("unknown bind opcode %d", *p);
+ }
+ }
+#endif
+
+template <typename A>
+void DyldInfoPrinter<A>::printLazyBindingOpcodes()
+{
+ if ( fInfo == NULL ) {
+ printf("no compressed dyld info\n");
+ }
+ else if ( fInfo->lazy_bind_off() == 0 ) {
+ printf("no compressed lazy binding info\n");
+ }
+ else {
+ printf("lazy binding opcodes:\n");
+ const uint8_t* const start = (uint8_t*)fHeader + fInfo->lazy_bind_off();
+ const uint8_t* const end = &start[fInfo->lazy_bind_size()];
+ uint8_t type = BIND_TYPE_POINTER;
+ uint8_t flags;
+ uint64_t address = fBaseAddress;
+ const char* symbolName = NULL;
+ int libraryOrdinal = 0;
+ int64_t addend = 0;
+ uint32_t segmentIndex = 0;
+ uint32_t count;
+ uint32_t skip;
+ for (const uint8_t* p = start; p < end; ) {
+ uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
+ uint8_t opcode = *p & BIND_OPCODE_MASK;
+ uint32_t opcodeOffset = p-start;
+ ++p;
+ switch (opcode) {
+ case BIND_OPCODE_DONE:
+ printf("0x%04X BIND_OPCODE_DONE\n", opcodeOffset);
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
+ libraryOrdinal = immediate;
+ printf("0x%04X BIND_OPCODE_SET_DYLIB_ORDINAL_IMM(%d)\n", opcodeOffset, libraryOrdinal);
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
+ libraryOrdinal = read_uleb128(p, end);
+ printf("0x%04X BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB(%d)\n", opcodeOffset, libraryOrdinal);
+ break;
+ case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
+ // the special ordinals are negative numbers
+ if ( immediate == 0 )
+ libraryOrdinal = 0;
+ else {
+ int8_t signExtended = BIND_OPCODE_MASK | immediate;
+ libraryOrdinal = signExtended;
+ }
+ printf("0x%04X BIND_OPCODE_SET_DYLIB_SPECIAL_IMM(%d)\n", opcodeOffset, libraryOrdinal);
+ break;
+ case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
+ flags = immediate;
+ symbolName = (char*)p;
+ while (*p != '\0')
+ ++p;
+ ++p;
+ printf("0x%04X BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM(0x%02X, %s)\n", opcodeOffset, flags, symbolName);
+ break;
+ case BIND_OPCODE_SET_TYPE_IMM:
+ type = immediate;
+ printf("0x%04X BIND_OPCODE_SET_TYPE_IMM(%d)\n", opcodeOffset, type);
+ break;
+ case BIND_OPCODE_SET_ADDEND_SLEB:
+ addend = read_sleb128(p, end);
+ printf("0x%04X BIND_OPCODE_SET_ADDEND_SLEB(%lld)\n", opcodeOffset, addend);
+ break;
+ case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ segmentIndex = immediate;
+ address = read_uleb128(p, end);
+ printf("0x%04X BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(0x%02X, 0x%08llX)\n", opcodeOffset, segmentIndex, address);
+ break;
+ case BIND_OPCODE_ADD_ADDR_ULEB:
+ skip = read_uleb128(p, end);
+ printf("0x%04X BIND_OPCODE_ADD_ADDR_ULEB(0x%08X)\n", opcodeOffset, skip);
+ break;
+ case BIND_OPCODE_DO_BIND:
+ printf("0x%04X BIND_OPCODE_DO_BIND()\n", opcodeOffset);
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
+ skip = read_uleb128(p, end);
+ printf("0x%04X BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB(0x%08X)\n", opcodeOffset, skip);
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
+ skip = immediate*sizeof(pint_t) + sizeof(pint_t);
+ printf("0x%04X BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED(0x%08X)\n", opcodeOffset, skip);
+ break;
+ case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
+ count = read_uleb128(p, end);
+ skip = read_uleb128(p, end);
+ printf("0x%04X BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB(%d, 0x%08X)\n", opcodeOffset, count, skip);
+ break;
+ default:
+ throwf("unknown bind opcode %d", *p);
+ }
+ }
+ }
+
+}
+
+
+template <typename A>
+void DyldInfoPrinter<A>::processExportNode(const uint8_t* const start, const uint8_t* p, const uint8_t* const end,
+ char* cummulativeString, int curStrOffset)
+{
+ const uint8_t terminalSize = *p++;
+ const uint8_t* children = p + terminalSize;
+ if ( terminalSize != 0 ) {
+ uint32_t flags = read_uleb128(p, end);
+ uint64_t address = read_uleb128(p, end);
+ if ( flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION )
+ fprintf(stdout, "0x%08llX [weak_def] %s\n", address, cummulativeString);
+ else
+ fprintf(stdout, "0x%08llX %s\n", address, cummulativeString);
+ }
+ const uint8_t childrenCount = *children++;
+ const uint8_t* s = children;
+ for (uint8_t i=0; i < childrenCount; ++i) {
+ int edgeStrLen = 0;
+ while (*s != '\0') {
+ cummulativeString[curStrOffset+edgeStrLen] = *s++;
+ ++edgeStrLen;
+ }
+ cummulativeString[curStrOffset+edgeStrLen] = *s++;
+ uint32_t childNodeOffet = read_uleb128(s, end);
+ processExportNode(start, start+childNodeOffet, end, cummulativeString, curStrOffset+edgeStrLen);
+ }
+}
+
+struct SortExportsByAddress
+{
+ bool operator()(const mach_o::trie::Entry& left, const mach_o::trie::Entry& right)
+ {
+ return ( left.address < right.address );
+ }
+};
+
+template <typename A>
+void DyldInfoPrinter<A>::printExportInfo()
+{
+ if ( (fInfo == NULL) || (fInfo->export_off() == 0) ) {
+ printf("no compressed export info\n");
+ }
+ else {
+ const uint8_t* start = (uint8_t*)fHeader + fInfo->export_off();
+ const uint8_t* end = &start[fInfo->export_size()];
+ std::vector<mach_o::trie::Entry> list;
+ parseTrie(start, end, list);
+ //std::sort(list.begin(), list.end(), SortExportsByAddress());
+ for (std::vector<mach_o::trie::Entry>::iterator it=list.begin(); it != list.end(); ++it) {
+ const char* flags = "";
+ if ( it->flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION )
+ flags = "[weak_def] ";
+ fprintf(stdout, "0x%08llX %s%s\n", fBaseAddress+it->address, flags, it->name);
+ }
+ }
+}
+
+
+template <typename A>
+void DyldInfoPrinter<A>::processExportGraphNode(const uint8_t* const start, const uint8_t* const end,
+ const uint8_t* parent, const uint8_t* p,
+ char* cummulativeString, int curStrOffset)
+{
+ const uint8_t* const me = p;
+ const uint8_t terminalSize = *p++;
+ const uint8_t* children = p + terminalSize;
+ if ( terminalSize != 0 ) {
+ uint32_t flags = read_uleb128(p, end);
+ uint64_t address = read_uleb128(p, end);
+ printf("\tnode%03ld [ label=%s,addr0x%08llX ];\n", (long)(me-start), cummulativeString, address);
+ }
+ else {
+ printf("\tnode%03ld;\n", (long)(me-start));
+ }
+ const uint8_t childrenCount = *children++;
+ const uint8_t* s = children;
+ for (uint8_t i=0; i < childrenCount; ++i) {
+ const char* edgeName = (char*)s;
+ int edgeStrLen = 0;
+ while (*s != '\0') {
+ cummulativeString[curStrOffset+edgeStrLen] = *s++;
+ ++edgeStrLen;
+ }
+ cummulativeString[curStrOffset+edgeStrLen] = *s++;
+ uint32_t childNodeOffet = read_uleb128(s, end);
+ printf("\tnode%03ld -> node%03d [ label=%s ] ;\n", (long)(me-start), childNodeOffet, edgeName);
+ processExportGraphNode(start, end, start, start+childNodeOffet, cummulativeString, curStrOffset+edgeStrLen);
+ }
+}
+
+template <typename A>
+void DyldInfoPrinter<A>::printExportInfoGraph()
+{
+ if ( (fInfo == NULL) || (fInfo->export_off() == 0) ) {
+ printf("no compressed export info\n");
+ }
+ else {
+ const uint8_t* p = (uint8_t*)fHeader + fInfo->export_off();
+ const uint8_t* end = &p[fInfo->export_size()];
+ char cummulativeString[2000];
+ printf("digraph {\n");
+ processExportGraphNode(p, end, p, p, cummulativeString, 0);
+ printf("}\n");
+ }
+}
+
+
+
+
+
+static void dump(const char* path)
+{
+ struct stat stat_buf;
+
+ try {
+ int fd = ::open(path, O_RDONLY, 0);
+ if ( fd == -1 )
+ throw "cannot open file";
+ if ( ::fstat(fd, &stat_buf) != 0 )
+ throwf("fstat(%s) failed, errno=%d\n", path, errno);
+ uint32_t length = stat_buf.st_size;
+ 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) ) {
+ const struct fat_header* fh = (struct fat_header*)p;
+ const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header));
+ for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) {
+ size_t offset = OSSwapBigToHostInt32(archs[i].offset);
+ size_t size = OSSwapBigToHostInt32(archs[i].size);
+ cpu_type_t cputype = OSSwapBigToHostInt32(archs[i].cputype);
+ if ( cputype == (uint32_t)sPreferredArch ) {
+ switch(cputype) {
+ case CPU_TYPE_POWERPC:
+ if ( DyldInfoPrinter<ppc>::validFile(p + offset) )
+ DyldInfoPrinter<ppc>::make(p + offset, size, path);
+ else
+ throw "in universal file, ppc slice does not contain ppc mach-o";
+ break;
+ case CPU_TYPE_I386:
+ if ( DyldInfoPrinter<x86>::validFile(p + offset) )
+ DyldInfoPrinter<x86>::make(p + offset, size, path);
+ else
+ throw "in universal file, i386 slice does not contain i386 mach-o";
+ break;
+ case CPU_TYPE_POWERPC64:
+ if ( DyldInfoPrinter<ppc64>::validFile(p + offset) )
+ DyldInfoPrinter<ppc64>::make(p + offset, size, path);
+ else
+ throw "in universal file, ppc64 slice does not contain ppc64 mach-o";
+ break;
+ case CPU_TYPE_X86_64:
+ if ( DyldInfoPrinter<x86_64>::validFile(p + offset) )
+ DyldInfoPrinter<x86_64>::make(p + offset, size, path);
+ else
+ throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
+ break;
+ case CPU_TYPE_ARM:
+ if ( DyldInfoPrinter<arm>::validFile(p + offset) )
+ DyldInfoPrinter<arm>::make(p + offset, size, path);
+ else
+ throw "in universal file, arm slice does not contain arm mach-o";
+ break;
+ default:
+ throwf("in universal file, unknown architecture slice 0x%x\n", cputype);
+ }
+ }
+ }
+ }
+ else if ( DyldInfoPrinter<x86>::validFile(p) ) {
+ DyldInfoPrinter<x86>::make(p, length, path);
+ }
+ else if ( DyldInfoPrinter<ppc>::validFile(p) ) {
+ DyldInfoPrinter<ppc>::make(p, length, path);
+ }
+ else if ( DyldInfoPrinter<ppc64>::validFile(p) ) {
+ DyldInfoPrinter<ppc64>::make(p, length, path);
+ }
+ else if ( DyldInfoPrinter<x86_64>::validFile(p) ) {
+ DyldInfoPrinter<x86_64>::make(p, length, path);
+ }
+ else if ( DyldInfoPrinter<arm>::validFile(p) ) {
+ DyldInfoPrinter<arm>::make(p, length, path);
+ }
+ else {
+ throw "not a known file type";
+ }
+ }
+ catch (const char* msg) {
+ throwf("%s in %s", msg, path);
+ }
+}
+
+static void usage()
+{
+ fprintf(stderr, "Usage: dyldinfo [-arch <arch>] <options> <mach-o file>\n"
+ "\t-rebase print addresses dyld will adjust if file not loaded at preferred address\n"
+ "\t-bind print addresses dyld will set based on symbolic lookups\n"
+ "\t-weak_bind print symbols which dyld must coalesce\n"
+ "\t-lazy_bind print addresses dyld will lazily set on first use\n"
+ "\t-export print addresses of all symbols this file exports\n"
+ "\t-opcodes print opcodes used to generate the rebase and binding information\n"
+ "\t-export_dot print a GraphViz .dot file of the exported symbols trie\n"
+ );
+}
+
+
+int main(int argc, const char* argv[])
+{
+ if ( argc == 1 ) {
+ usage();
+ return 0;
+ }
+
+ try {
+ std::vector<const char*> files;
+ for(int i=1; i < argc; ++i) {
+ const char* arg = argv[i];
+ if ( arg[0] == '-' ) {
+ if ( strcmp(arg, "-arch") == 0 ) {
+ const char* arch = ++i<argc? argv[i]: "";
+ if ( strcmp(arch, "ppc64") == 0 )
+ sPreferredArch = CPU_TYPE_POWERPC64;
+ else if ( strcmp(arch, "ppc") == 0 )
+ sPreferredArch = CPU_TYPE_POWERPC;
+ else if ( strcmp(arch, "i386") == 0 )
+ sPreferredArch = CPU_TYPE_I386;
+ else if ( strcmp(arch, "x86_64") == 0 )
+ sPreferredArch = CPU_TYPE_X86_64;
+ else
+ throwf("unknown architecture %s", arch);
+ }
+ else if ( strcmp(arg, "-rebase") == 0 ) {
+ printRebase = true;
+ }
+ else if ( strcmp(arg, "-bind") == 0 ) {
+ printBind = true;
+ }
+ else if ( strcmp(arg, "-weak_bind") == 0 ) {
+ printWeakBind = true;
+ }
+ else if ( strcmp(arg, "-lazy_bind") == 0 ) {
+ printLazyBind = true;
+ }
+ else if ( strcmp(arg, "-export") == 0 ) {
+ printExport = true;
+ }
+ else if ( strcmp(arg, "-opcodes") == 0 ) {
+ printOpcodes = true;
+ }
+ else if ( strcmp(arg, "-export_dot") == 0 ) {
+ printExportGraph = true;
+ }
+ else {
+ throwf("unknown option: %s\n", arg);
+ }
+ }
+ else {
+ files.push_back(arg);
+ }
+ }
+ if ( files.size() == 0 )
+ usage();
+ if ( files.size() == 1 ) {
+ dump(files[0]);
+ }
+ else {
+ for(std::vector<const char*>::iterator it=files.begin(); it != files.end(); ++it) {
+ printf("\n%s:\n", *it);
+ dump(*it);
+ }
+ }
+ }
+ catch (const char* msg) {
+ fprintf(stderr, "dyldinfo failed: %s\n", msg);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2006-2007 Apple 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 <stdarg.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <vector>
+#include <set>
+#include <ext/hash_set>
+
+#include "MachOFileAbstraction.hpp"
+#include "Architectures.hpp"
+
+
+ __attribute__((noreturn))
+void throwf(const char* format, ...)
+{
+ va_list list;
+ char* p;
+ va_start(list, format);
+ vasprintf(&p, format, list);
+ va_end(list);
+
+ const char* t = p;
+ throw t;
+}
+
+
+template <typename A>
+class MachOChecker
+{
+public:
+ static bool validFile(const uint8_t* fileContent);
+ static MachOChecker<A>* make(const uint8_t* fileContent, uint32_t fileLength, const char* path)
+ { return new MachOChecker<A>(fileContent, fileLength, path); }
+ virtual ~MachOChecker() {}
+
+
+private:
+ typedef typename A::P P;
+ typedef typename A::P::E E;
+ typedef typename A::P::uint_t pint_t;
+
+ class CStringEquals
+ {
+ public:
+ bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
+ };
+
+ typedef __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> StringSet;
+
+ MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path);
+ void checkMachHeader();
+ void checkLoadCommands();
+ void checkSection(const macho_segment_command<P>* segCmd, const macho_section<P>* sect);
+ uint8_t loadCommandSizeMask();
+ void checkSymbolTable();
+ 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 fLength;
+ const char* fStrings;
+ const char* fStringsEnd;
+ const macho_nlist<P>* fSymbols;
+ uint32_t fSymbolCount;
+ const macho_dysymtab_command<P>* fDynamicSymbolTable;
+ 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;
+ bool fWriteableSegmentWithAddrOver4G;
+ const macho_segment_command<P>* fFirstSegment;
+ const macho_segment_command<P>* fFirstWritableSegment;
+};
+
+
+
+template <>
+bool MachOChecker<ppc>::validFile(const uint8_t* fileContent)
+{
+ 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;
+ switch (header->filetype()) {
+ case MH_EXECUTE:
+ case MH_DYLIB:
+ case MH_BUNDLE:
+ case MH_DYLINKER:
+ return true;
+ }
+ return false;
+}
+
+template <>
+bool MachOChecker<ppc64>::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_POWERPC64 )
+ return false;
+ switch (header->filetype()) {
+ case MH_EXECUTE:
+ case MH_DYLIB:
+ case MH_BUNDLE:
+ case MH_DYLINKER:
+ return true;
+ }
+ return false;
+}
+
+template <>
+bool MachOChecker<x86>::validFile(const uint8_t* fileContent)
+{
+ 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;
+ switch (header->filetype()) {
+ case MH_EXECUTE:
+ case MH_DYLIB:
+ case MH_BUNDLE:
+ case MH_DYLINKER:
+ return true;
+ }
+ 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 <>
+bool MachOChecker<arm>::validFile(const uint8_t* fileContent)
+{
+ const macho_header<P>* header = (const macho_header<P>*)fileContent;
+ if ( header->magic() != MH_MAGIC )
+ return false;
+ if ( header->cputype() != CPU_TYPE_ARM )
+ 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 <> uint8_t MachOChecker<arm>::loadCommandSizeMask() { return 0x03; }
+
+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), fDynamicSymbolTable(NULL), fIndirectTableCount(0),
+ fLocalRelocations(NULL), fLocalRelocationsCount(0), fExternalRelocations(NULL), fExternalRelocationsCount(0),
+ fWriteableSegmentWithAddrOver4G(false), fFirstSegment(NULL), fFirstWritableSegment(NULL)
+{
+ // sanity check
+ if ( ! validFile(fileContent) )
+ throw "not a mach-o file that can be checked";
+
+ fPath = strdup(path);
+ fHeader = (const macho_header<P>*)fileContent;
+
+ // sanity check header
+ checkMachHeader();
+
+ // check load commands
+ checkLoadCommands();
+
+ checkIndirectSymbolTable();
+
+ checkRelocations();
+
+ checkSymbolTable();
+}
+
+
+template <typename A>
+void MachOChecker<A>::checkMachHeader()
+{
+ if ( (fHeader->sizeofcmds() + sizeof(macho_header<P>)) > fLength )
+ throw "sizeofcmds in mach_header is larger than file";
+
+ uint32_t flags = fHeader->flags();
+ const uint32_t invalidBits = MH_INCRLINK | MH_LAZY_INIT | 0xFFC00000;
+ if ( flags & invalidBits )
+ throw "invalid bits in mach_header flags";
+ if ( (flags & MH_NO_REEXPORTED_DYLIBS) && (fHeader->filetype() != MH_DYLIB) )
+ throw "MH_NO_REEXPORTED_DYLIBS bit of mach_header flags only valid for dylibs";
+}
+
+template <typename A>
+void MachOChecker<A>::checkLoadCommands()
+{
+ // check that all load commands fit within the load command space file
+ const macho_encryption_info_command<P>* encryption_info = NULL;
+ const uint8_t* const endOfFile = (uint8_t*)fHeader + fLength;
+ const uint8_t* const endOfLoadCommands = (uint8_t*)fHeader + sizeof(macho_header<P>) + fHeader->sizeofcmds();
+ const uint32_t cmd_count = fHeader->ncmds();
+ const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
+ const macho_load_command<P>* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ uint32_t size = cmd->cmdsize();
+ if ( (size & this->loadCommandSizeMask()) != 0 )
+ throwf("load command #%d has a unaligned size", i);
+ const uint8_t* endOfCmd = ((uint8_t*)cmd)+cmd->cmdsize();
+ if ( endOfCmd > endOfLoadCommands )
+ throwf("load command #%d extends beyond the end of the load commands", i);
+ if ( endOfCmd > endOfFile )
+ throwf("load command #%d extends beyond the end of the file", i);
+ switch ( cmd->cmd() ) {
+ case macho_segment_command<P>::CMD:
+ case LC_SYMTAB:
+ case LC_UNIXTHREAD:
+ case LC_DYSYMTAB:
+ case LC_LOAD_DYLIB:
+ case LC_ID_DYLIB:
+ case LC_LOAD_DYLINKER:
+ case LC_ID_DYLINKER:
+ case macho_routines_command<P>::CMD:
+ case LC_SUB_FRAMEWORK:
+ case LC_SUB_CLIENT:
+ case LC_TWOLEVEL_HINTS:
+ case LC_PREBIND_CKSUM:
+ case LC_LOAD_WEAK_DYLIB:
+ case LC_LAZY_LOAD_DYLIB:
+ case LC_UUID:
+ case LC_REEXPORT_DYLIB:
+ case LC_SEGMENT_SPLIT_INFO:
+ case LC_CODE_SIGNATURE:
+ case LC_DYLD_INFO:
+ case LC_DYLD_INFO_ONLY:
+ break;
+ case LC_ENCRYPTION_INFO:
+ encryption_info = (macho_encryption_info_command<P>*)cmd;
+ break;
+ case LC_SUB_UMBRELLA:
+ case LC_SUB_LIBRARY:
+ if ( fHeader->flags() & MH_NO_REEXPORTED_DYLIBS )
+ throw "MH_NO_REEXPORTED_DYLIBS bit of mach_header flags should not be set in an image with LC_SUB_LIBRARY or LC_SUB_UMBRELLA";
+ break;
+ default:
+ throwf("load command #%d is an unknown kind 0x%X", i, cmd->cmd());
+ }
+ cmd = (const macho_load_command<P>*)endOfCmd;
+ }
+
+ // check segments
+ cmd = cmds;
+ std::vector<std::pair<pint_t, pint_t> > segmentAddressRanges;
+ std::vector<std::pair<pint_t, pint_t> > segmentFileOffsetRanges;
+ const macho_segment_command<P>* linkEditSegment = NULL;
+ 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->cmdsize() != (sizeof(macho_segment_command<P>) + segCmd->nsects() * sizeof(macho_section_content<P>)) )
+ throw "invalid segment load command size";
+
+ // see if this overlaps another segment address range
+ uint64_t startAddr = segCmd->vmaddr();
+ uint64_t endAddr = startAddr + segCmd->vmsize();
+ for (typename std::vector<std::pair<pint_t, pint_t> >::iterator it = segmentAddressRanges.begin(); it != segmentAddressRanges.end(); ++it) {
+ if ( it->first < startAddr ) {
+ if ( it->second > startAddr )
+ throw "overlapping segment vm addresses";
+ }
+ else if ( it->first > startAddr ) {
+ if ( it->first < endAddr )
+ throw "overlapping segment vm addresses";
+ }
+ else {
+ throw "overlapping segment vm addresses";
+ }
+ segmentAddressRanges.push_back(std::make_pair<pint_t, pint_t>(startAddr, endAddr));
+ }
+ // see if this overlaps another segment file offset range
+ uint64_t startOffset = segCmd->fileoff();
+ uint64_t endOffset = startOffset + segCmd->filesize();
+ for (typename std::vector<std::pair<pint_t, pint_t> >::iterator it = segmentFileOffsetRanges.begin(); it != segmentFileOffsetRanges.end(); ++it) {
+ if ( it->first < startOffset ) {
+ if ( it->second > startOffset )
+ throw "overlapping segment file data";
+ }
+ else if ( it->first > startOffset ) {
+ if ( it->first < endOffset )
+ throw "overlapping segment file data";
+ }
+ else {
+ throw "overlapping segment file data";
+ }
+ segmentFileOffsetRanges.push_back(std::make_pair<pint_t, pint_t>(startOffset, endOffset));
+ // check is within file bounds
+ if ( (startOffset > fLength) || (endOffset > fLength) )
+ throw "segment file data is past end of file";
+ }
+ // verify it fits in file
+ if ( startOffset > fLength )
+ throw "segment fileoff does not fit in file";
+ if ( endOffset > fLength )
+ throw "segment fileoff+filesize does not fit in file";
+
+ // keep LINKEDIT segment
+ if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 )
+ linkEditSegment = segCmd;
+
+ // cache interesting segments
+ if ( fFirstSegment == NULL )
+ fFirstSegment = segCmd;
+ if ( (segCmd->initprot() & VM_PROT_WRITE) != 0 ) {
+ if ( fFirstWritableSegment == NULL )
+ fFirstWritableSegment = segCmd;
+ if ( segCmd->vmaddr() > 0x100000000ULL )
+ fWriteableSegmentWithAddrOver4G = true;
+ }
+
+ // check section ranges
+ const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
+ const macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()];
+ for(const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
+ // check all sections are within segment
+ if ( sect->addr() < startAddr )
+ throwf("section %s vm address not within segment", sect->sectname());
+ if ( (sect->addr()+sect->size()) > endAddr )
+ throwf("section %s vm address not within segment", sect->sectname());
+ if ( ((sect->flags() & SECTION_TYPE) != S_ZEROFILL) && (segCmd->filesize() != 0) ) {
+ if ( sect->offset() < startOffset )
+ throwf("section %s file offset not within segment", sect->sectname());
+ if ( (sect->offset()+sect->size()) > endOffset )
+ throwf("section %s file offset not within segment", sect->sectname());
+ }
+ checkSection(segCmd, sect);
+ }
+ }
+ cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+
+ // verify there was a LINKEDIT segment
+ if ( linkEditSegment == NULL )
+ throw "no __LINKEDIT segment";
+
+ // checks for executables
+ bool isStaticExecutable = false;
+ if ( fHeader->filetype() == MH_EXECUTE ) {
+ isStaticExecutable = true;
+ cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ switch ( cmd->cmd() ) {
+ case LC_LOAD_DYLINKER:
+ // the existence of a dyld load command makes a executable dynamic
+ isStaticExecutable = false;
+ break;
+ }
+ cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+ if ( isStaticExecutable ) {
+ if ( fHeader->flags() != MH_NOUNDEFS )
+ throw "invalid bits in mach_header flags for static executable";
+ }
+ }
+
+ // verify encryption info
+ if ( encryption_info != NULL ) {
+ if ( fHeader->filetype() != MH_EXECUTE )
+ throw "LC_ENCRYPTION_INFO load command is only legal in main executables";
+ if ( encryption_info->cryptoff() < (sizeof(macho_header<P>) + fHeader->sizeofcmds()) )
+ throw "LC_ENCRYPTION_INFO load command has cryptoff covers some load commands";
+ if ( (encryption_info->cryptoff() % 4096) != 0 )
+ throw "LC_ENCRYPTION_INFO load command has cryptoff which is not page aligned";
+ if ( (encryption_info->cryptsize() % 4096) != 0 )
+ throw "LC_ENCRYPTION_INFO load command has cryptsize which is not page sized";
+ for (typename std::vector<std::pair<pint_t, pint_t> >::iterator it = segmentFileOffsetRanges.begin();
+ it != segmentFileOffsetRanges.end(); ++it) {
+ if ( (it->first <= encryption_info->cryptoff()) && (encryption_info->cryptoff() < it->second) ) {
+ if ( (encryption_info->cryptoff() + encryption_info->cryptsize()) > it->second )
+ throw "LC_ENCRYPTION_INFO load command is not contained within one segment";
+ }
+ }
+ }
+
+ // check LC_SYMTAB, LC_DYSYMTAB, and LC_SEGMENT_SPLIT_INFO
+ cmd = cmds;
+ bool foundDynamicSymTab = false;
+ 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;
+ fSymbolCount = symtab->nsyms();
+ fSymbols = (const macho_nlist<P>*)((char*)fHeader + symtab->symoff());
+ if ( symtab->symoff() < linkEditSegment->fileoff() )
+ throw "symbol table not in __LINKEDIT";
+ if ( (symtab->symoff() + fSymbolCount*sizeof(macho_nlist<P>*)) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) )
+ throw "symbol table end not in __LINKEDIT";
+ if ( (symtab->symoff() % sizeof(pint_t)) != 0 )
+ throw "symbol table start not pointer aligned";
+ fStrings = (char*)fHeader + symtab->stroff();
+ fStringsEnd = fStrings + symtab->strsize();
+ if ( symtab->stroff() < linkEditSegment->fileoff() )
+ throw "string pool not in __LINKEDIT";
+ if ( (symtab->stroff()+symtab->strsize()) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) )
+ throw "string pool extends beyond __LINKEDIT";
+ if ( (symtab->stroff() % 4) != 0 ) // work around until rdar://problem/4737991 is fixed
+ throw "string pool start not pointer aligned";
+ if ( (symtab->strsize() % sizeof(pint_t)) != 0 )
+ throw "string pool size not a multiple of pointer size";
+ }
+ break;
+ case LC_DYSYMTAB:
+ {
+ if ( isStaticExecutable )
+ throw "LC_DYSYMTAB should not be used in static executable";
+ foundDynamicSymTab = true;
+ fDynamicSymbolTable = (struct macho_dysymtab_command<P>*)cmd;
+ fIndirectTable = (uint32_t*)((char*)fHeader + fDynamicSymbolTable->indirectsymoff());
+ fIndirectTableCount = fDynamicSymbolTable->nindirectsyms();
+ if ( fIndirectTableCount != 0 ) {
+ if ( fDynamicSymbolTable->indirectsymoff() < linkEditSegment->fileoff() )
+ throw "indirect symbol table not in __LINKEDIT";
+ if ( (fDynamicSymbolTable->indirectsymoff()+fIndirectTableCount*8) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) )
+ throw "indirect symbol table not in __LINKEDIT";
+ if ( (fDynamicSymbolTable->indirectsymoff() % sizeof(pint_t)) != 0 )
+ throw "indirect symbol table not pointer aligned";
+ }
+ fLocalRelocationsCount = fDynamicSymbolTable->nlocrel();
+ if ( fLocalRelocationsCount != 0 ) {
+ fLocalRelocations = (const macho_relocation_info<P>*)((char*)fHeader + fDynamicSymbolTable->locreloff());
+ if ( fDynamicSymbolTable->locreloff() < linkEditSegment->fileoff() )
+ throw "local relocations not in __LINKEDIT";
+ if ( (fDynamicSymbolTable->locreloff()+fLocalRelocationsCount*sizeof(macho_relocation_info<P>)) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) )
+ throw "local relocations not in __LINKEDIT";
+ if ( (fDynamicSymbolTable->locreloff() % sizeof(pint_t)) != 0 )
+ throw "local relocations table not pointer aligned";
+ }
+ fExternalRelocationsCount = fDynamicSymbolTable->nextrel();
+ if ( fExternalRelocationsCount != 0 ) {
+ fExternalRelocations = (const macho_relocation_info<P>*)((char*)fHeader + fDynamicSymbolTable->extreloff());
+ if ( fDynamicSymbolTable->extreloff() < linkEditSegment->fileoff() )
+ throw "external relocations not in __LINKEDIT";
+ if ( (fDynamicSymbolTable->extreloff()+fExternalRelocationsCount*sizeof(macho_relocation_info<P>)) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) )
+ throw "external relocations not in __LINKEDIT";
+ if ( (fDynamicSymbolTable->extreloff() % sizeof(pint_t)) != 0 )
+ throw "external relocations table not pointer aligned";
+ }
+ }
+ break;
+ case LC_SEGMENT_SPLIT_INFO:
+ {
+ if ( isStaticExecutable )
+ throw "LC_SEGMENT_SPLIT_INFO should not be used in static executable";
+ const macho_linkedit_data_command<P>* info = (struct macho_linkedit_data_command<P>*)cmd;
+ if ( info->dataoff() < linkEditSegment->fileoff() )
+ throw "split seg info not in __LINKEDIT";
+ if ( (info->dataoff()+info->datasize()) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) )
+ throw "split seg info not in __LINKEDIT";
+ if ( (info->dataoff() % sizeof(pint_t)) != 0 )
+ throw "split seg info table not pointer aligned";
+ if ( (info->datasize() % sizeof(pint_t)) != 0 )
+ throw "split seg info size not a multiple of pointer size";
+ }
+ break;
+ }
+ cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+ if ( !isStaticExecutable && !foundDynamicSymTab )
+ throw "missing dynamic symbol table";
+ if ( fStrings == NULL )
+ throw "missing symbol table";
+
+}
+
+template <typename A>
+void MachOChecker<A>::checkSection(const macho_segment_command<P>* segCmd, const macho_section<P>* sect)
+{
+ uint8_t sectionType = (sect->flags() & SECTION_TYPE);
+ if ( sectionType == S_ZEROFILL ) {
+ if ( sect->offset() != 0 )
+ throwf("section offset should be zero for zero-fill section %s", sect->sectname());
+ }
+
+ // more section tests here
+}
+
+template <typename A>
+void MachOChecker<A>::checkIndirectSymbolTable()
+{
+ 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;
+ const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
+ const macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()];
+ for(const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
+ // make sure all magic sections that use indirect symbol table fit within it
+ uint32_t start = 0;
+ uint32_t elementSize = 0;
+ switch ( sect->flags() & SECTION_TYPE ) {
+ case S_SYMBOL_STUBS:
+ elementSize = sect->reserved2();
+ start = sect->reserved1();
+ break;
+ case S_LAZY_SYMBOL_POINTERS:
+ case S_NON_LAZY_SYMBOL_POINTERS:
+ elementSize = sizeof(pint_t);
+ start = sect->reserved1();
+ break;
+ }
+ if ( elementSize != 0 ) {
+ uint32_t count = sect->size() / elementSize;
+ if ( (count*elementSize) != sect->size() )
+ throwf("%s section size is not an even multiple of element size", sect->sectname());
+ if ( (start+count) > fIndirectTableCount )
+ throwf("%s section references beyond end of indirect symbol table (%d > %d)", sect->sectname(), start+count, fIndirectTableCount );
+ }
+ }
+ }
+ cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+}
+
+
+template <typename A>
+void MachOChecker<A>::checkSymbolTable()
+{
+ // verify no duplicate external symbol names
+ if ( fDynamicSymbolTable != NULL ) {
+ StringSet externalNames;
+ const macho_nlist<P>* const exportedStart = &fSymbols[fDynamicSymbolTable->iextdefsym()];
+ const macho_nlist<P>* const exportedEnd = &exportedStart[fDynamicSymbolTable->nextdefsym()];
+ int i = fDynamicSymbolTable->iextdefsym();
+ for(const macho_nlist<P>* p = exportedStart; p < exportedEnd; ++p, ++i) {
+ const char* symName = &fStrings[p->n_strx()];
+ if ( symName > fStringsEnd )
+ throw "string index out of range";
+ //fprintf(stderr, "sym[%d] = %s\n", i, symName);
+ if ( externalNames.find(symName) != externalNames.end() )
+ throwf("duplicate external symbol: %s", symName);
+ externalNames.insert(symName);
+ }
+ // verify no undefines with same name as an external symbol
+ const macho_nlist<P>* const undefinesStart = &fSymbols[fDynamicSymbolTable->iundefsym()];
+ const macho_nlist<P>* const undefinesEnd = &undefinesStart[fDynamicSymbolTable->nundefsym()];
+ for(const macho_nlist<P>* p = undefinesStart; p < undefinesEnd; ++p) {
+ const char* symName = &fStrings[p->n_strx()];
+ if ( symName > fStringsEnd )
+ throw "string index out of range";
+ if ( externalNames.find(symName) != externalNames.end() )
+ throwf("undefine with same name as external symbol: %s", symName);
+ }
+ }
+}
+
+
+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 <>
+arm::P::uint_t MachOChecker<arm>::relocBase()
+{
+ if ( fHeader->flags() & MH_SPLIT_SEGS )
+ return fFirstWritableSegment->vmaddr();
+ else
+ return fFirstSegment->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 ( (address >= segCmd->vmaddr()) && (address < segCmd->vmaddr()+segCmd->vmsize()) ) {
+ // if segment is writable, we are fine
+ if ( (segCmd->initprot() & VM_PROT_WRITE) != 0 )
+ return true;
+ // could be a text reloc, make sure section bit is set
+ const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
+ const macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()];
+ for(const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
+ if ( (sect->addr() <= address) && (address < (sect->addr()+sect->size())) ) {
+ // found section for this address, if has relocs we are fine
+ return ( (sect->flags() & (S_ATTR_EXT_RELOC|S_ATTR_LOC_RELOC)) != 0 );
+ }
+ }
+ }
+ }
+ cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+ return false;
+}
+
+
+template <>
+void MachOChecker<ppc>::checkExternalReloation(const macho_relocation_info<P>* reloc)
+{
+ if ( reloc->r_length() != 2 )
+ 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 "external relocation address not in writable segment";
+ // FIX: check r_symbol
+}
+
+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 "external relocation address not in writable segment";
+ // FIX: check r_symbol
+}
+
+template <>
+void MachOChecker<x86>::checkExternalReloation(const macho_relocation_info<P>* reloc)
+{
+ if ( reloc->r_length() != 2 )
+ 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 "external relocation address not in writable segment";
+ // FIX: check r_symbol
+}
+
+
+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<arm>::checkExternalReloation(const macho_relocation_info<P>* reloc)
+{
+ if ( reloc->r_length() != 2 )
+ throw "bad external relocation length";
+ if ( reloc->r_type() != ARM_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 "external 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 {
+ // ignore pair relocs
+ if ( reloc->r_type() == PPC_RELOC_PAIR )
+ return;
+ // FIX
+ if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) )
+ throwf("local relocation address 0x%08X not in writable segment", reloc->r_address());
+ }
+}
+
+
+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 <>
+void MachOChecker<arm>::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;
+ if ( sreloc->r_length() != 2 )
+ throw "bad local scattered relocation length";
+ if ( sreloc->r_type() != ARM_RELOC_PB_LA_PTR )
+ throw "bad local scattered relocation type";
+ }
+ else {
+ if ( reloc->r_length() != 2 )
+ throw "bad local relocation length";
+ 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()
+{
+ // external relocations should be sorted to minimize dyld symbol lookups
+ // therefore every reloc with the same r_symbolnum value should be contiguous
+ std::set<uint32_t> previouslySeenSymbolIndexes;
+ uint32_t lastSymbolIndex = 0xFFFFFFFF;
+ const macho_relocation_info<P>* const externRelocsEnd = &fExternalRelocations[fExternalRelocationsCount];
+ for (const macho_relocation_info<P>* reloc = fExternalRelocations; reloc < externRelocsEnd; ++reloc) {
+ this->checkExternalReloation(reloc);
+ if ( reloc->r_symbolnum() != lastSymbolIndex ) {
+ if ( previouslySeenSymbolIndexes.count(reloc->r_symbolnum()) != 0 )
+ throw "external relocations not sorted";
+ previouslySeenSymbolIndexes.insert(lastSymbolIndex);
+ lastSymbolIndex = reloc->r_symbolnum();
+ }
+ }
+
+ 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;
+
+ try {
+ int fd = ::open(path, O_RDONLY, 0);
+ if ( fd == -1 )
+ throw "cannot open file";
+ if ( ::fstat(fd, &stat_buf) != 0 )
+ throwf("fstat(%s) failed, errno=%d\n", path, errno);
+ uint32_t length = stat_buf.st_size;
+ 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) ) {
+ const struct fat_header* fh = (struct fat_header*)p;
+ const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header));
+ for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) {
+ size_t offset = OSSwapBigToHostInt32(archs[i].offset);
+ size_t size = OSSwapBigToHostInt32(archs[i].size);
+ unsigned int cputype = OSSwapBigToHostInt32(archs[i].cputype);
+
+ switch(cputype) {
+ case CPU_TYPE_POWERPC:
+ if ( MachOChecker<ppc>::validFile(p + offset) )
+ MachOChecker<ppc>::make(p + offset, size, path);
+ else
+ throw "in universal file, ppc slice does not contain ppc mach-o";
+ break;
+ case CPU_TYPE_I386:
+ if ( MachOChecker<x86>::validFile(p + offset) )
+ MachOChecker<x86>::make(p + offset, size, path);
+ else
+ throw "in universal file, i386 slice does not contain i386 mach-o";
+ break;
+ case CPU_TYPE_POWERPC64:
+ if ( MachOChecker<ppc64>::validFile(p + offset) )
+ MachOChecker<ppc64>::make(p + offset, size, path);
+ else
+ throw "in universal file, ppc64 slice does not contain ppc64 mach-o";
+ break;
+ case CPU_TYPE_X86_64:
+ if ( MachOChecker<x86_64>::validFile(p + offset) )
+ MachOChecker<x86_64>::make(p + offset, size, path);
+ else
+ throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
+ break;
+ case CPU_TYPE_ARM:
+ if ( MachOChecker<arm>::validFile(p + offset) )
+ MachOChecker<arm>::make(p + offset, size, path);
+ else
+ throw "in universal file, arm slice does not contain arm mach-o";
+ break;
+ default:
+ throwf("in universal file, unknown architecture slice 0x%x\n", cputype);
+ }
+ }
+ }
+ else if ( MachOChecker<x86>::validFile(p) ) {
+ MachOChecker<x86>::make(p, length, path);
+ }
+ else if ( MachOChecker<ppc>::validFile(p) ) {
+ MachOChecker<ppc>::make(p, length, path);
+ }
+ 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 if ( MachOChecker<arm>::validFile(p) ) {
+ MachOChecker<arm>::make(p, length, path);
+ }
+ else {
+ throw "not a known file type";
+ }
+ }
+ catch (const char* msg) {
+ throwf("%s in %s", msg, path);
+ }
+}
+
+
+int main(int argc, const char* argv[])
+{
+ try {
+ for(int i=1; i < argc; ++i) {
+ const char* arg = argv[i];
+ if ( arg[0] == '-' ) {
+ if ( strcmp(arg, "-no_content") == 0 ) {
+
+ }
+ else {
+ throwf("unknown option: %s\n", arg);
+ }
+ }
+ else {
+ check(arg);
+ }
+ }
+ }
+ catch (const char* msg) {
+ fprintf(stderr, "machocheck failed: %s\n", msg);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2008 Apple 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 <stdint.h>
+
+
+#if __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+/*
+ * prune_trie() is a C vended function that is used by strip(1) to prune out
+ * defined exported symbols from the export trie. It is passed a pointer to
+ * the start of bytes of the the trie and the size. The prune() funciton
+ * passed is called with each symbol name in the trie to determine if it is
+ * to be pruned (retuning 1) or not (returning 0). It writes the new trie
+ * back into the trie buffer and returns the new size in trie_new_size.
+ * If the pruning succeeds, NULL is returned. If there was an error processing
+ * the trie (e.f. it is malformed), then an error message string is returned.
+ * The error string can be freed.
+ */
+extern const char*
+prune_trie(
+ uint8_t* trie_start,
+ uint32_t trie_start_size,
+ int (*prune)(const char *name),
+ uint32_t* trie_new_size);
+
+
+#if __cplusplus
+}
+#endif /* __cplusplus */
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2006-2008 Apple 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 <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);
+ void rebaseAt(int segIndex, uint64_t offset, uint8_t type);
+
+ const macho_header<P>* fHeader;
+ pint_t fOrignalVMRelocBaseAddress;
+ pint_t fSlide;
+ 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 %s, errno=%d", path, 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;
+ case CPU_TYPE_ARM:
+ fRebasers.push_back(new Rebaser<arm>(&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 if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_ARM)) {
+ fRebasers.push_back(new Rebaser<arm>(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 <> cpu_type_t Rebaser<arm>::getArchitecture() const { return CPU_TYPE_ARM; }
+
+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);
+ }
+
+ // FIXME ¥¥¥ adjust dylib_module if it exists
+}
+
+static uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end)
+{
+ uint64_t result = 0;
+ int bit = 0;
+ do {
+ if (p == end)
+ throwf("malformed uleb128");
+
+ uint64_t slice = *p & 0x7f;
+
+ if (bit >= 64 || slice << bit >> bit != slice)
+ throwf("uleb128 too big");
+ else {
+ result |= (slice << bit);
+ bit += 7;
+ }
+ }
+ while (*p++ & 0x80);
+ return result;
+}
+
+template <typename A>
+void Rebaser<A>::rebaseAt(int segIndex, uint64_t offset, uint8_t type)
+{
+ //fprintf(stderr, "rebaseAt(seg=%d, offset=0x%08llX, type=%d\n", segIndex, offset, type);
+ static int lastSegIndex = -1;
+ static uint8_t* lastSegMappedStart = NULL;
+ if ( segIndex != lastSegIndex ) {
+ 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;
+ int segCount = 0;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
+ if ( segIndex == segCount ) {
+ const macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
+ lastSegMappedStart = (uint8_t*)fHeader + seg->fileoff();
+ lastSegIndex == segCount;
+ break;
+ }
+ ++segCount;
+ }
+ cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+ }
+
+ pint_t* locationToFix = (pint_t*)(lastSegMappedStart+offset);
+ uint32_t* locationToFix32 = (uint32_t*)(lastSegMappedStart+offset);
+ switch (type) {
+ case REBASE_TYPE_POINTER:
+ P::setP(*locationToFix, A::P::getP(*locationToFix) + fSlide);
+ break;
+ case REBASE_TYPE_TEXT_ABSOLUTE32:
+ E::set32(*locationToFix32, E::get32(*locationToFix32) + fSlide);
+ break;
+ default:
+ throwf("bad rebase type %d", type);
+ }
+}
+
+
+template <typename A>
+void Rebaser<A>::adjustDATA()
+{
+ const macho_dysymtab_command<P>* dysymtab = NULL;
+ const macho_dyld_info_command<P>* dyldInfo = 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;
+ case LC_DYLD_INFO:
+ case LC_DYLD_INFO_ONLY:
+ dyldInfo = (macho_dyld_info_command<P>*)cmd;
+ break;
+ }
+ cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+
+ // use new encoding of rebase info if present
+ if ( dyldInfo != NULL ) {
+ if ( dyldInfo->rebase_size() != 0 ) {
+ const uint8_t* p = (uint8_t*)fHeader + dyldInfo->rebase_off();
+ const uint8_t* end = &p[dyldInfo->rebase_size()];
+
+ uint8_t type = 0;
+ uint64_t offset = 0;
+ uint32_t count;
+ uint32_t skip;
+ int segIndex;
+ bool done = false;
+ while ( !done && (p < end) ) {
+ uint8_t immediate = *p & REBASE_IMMEDIATE_MASK;
+ uint8_t opcode = *p & REBASE_OPCODE_MASK;
+ ++p;
+ switch (opcode) {
+ case REBASE_OPCODE_DONE:
+ done = true;
+ break;
+ case REBASE_OPCODE_SET_TYPE_IMM:
+ type = immediate;
+ break;
+ case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ segIndex = immediate;
+ offset = read_uleb128(p, end);
+ break;
+ case REBASE_OPCODE_ADD_ADDR_ULEB:
+ offset += read_uleb128(p, end);
+ break;
+ case REBASE_OPCODE_ADD_ADDR_IMM_SCALED:
+ offset += immediate*sizeof(pint_t);
+ break;
+ case REBASE_OPCODE_DO_REBASE_IMM_TIMES:
+ for (int i=0; i < immediate; ++i) {
+ rebaseAt(segIndex, offset, type);
+ offset += sizeof(pint_t);
+ }
+ break;
+ case REBASE_OPCODE_DO_REBASE_ULEB_TIMES:
+ count = read_uleb128(p, end);
+ for (uint32_t i=0; i < count; ++i) {
+ rebaseAt(segIndex, offset, type);
+ offset += sizeof(pint_t);
+ }
+ break;
+ case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
+ rebaseAt(segIndex, offset, type);
+ offset += read_uleb128(p, end) + sizeof(pint_t);
+ break;
+ case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB:
+ count = read_uleb128(p, end);
+ skip = read_uleb128(p, end);
+ for (uint32_t i=0; i < count; ++i) {
+ rebaseAt(segIndex, offset, type);
+ offset += skip + sizeof(pint_t);
+ }
+ break;
+ default:
+ throwf("bad rebase opcode %d", *p);
+ }
+ }
+
+
+
+ }
+ }
+ else {
+ // 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 <>
+void Rebaser<arm>::doLocalRelocation(const macho_relocation_info<P>* reloc)
+{
+ if ( (reloc->r_address() & R_SCATTERED) == 0 ) {
+ if ( reloc->r_type() == ARM_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() == ARM_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)
+ 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
+ fOrignalVMRelocBaseAddress = segCmd->vmaddr();
+ return;
+ }
+ }
+ }
+ cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+ // just use base address
+ 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 ) {
+ 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";
+ case CPU_TYPE_ARM:
+ return "arm";
+ }
+ 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 if ( arch == CPU_TYPE_ARM ) {
+ // place dylibs below dyld
+ uint64_t topAddr = 0x2FE00000;
+ uint64_t totalSize = totalVMSize(arch, files);
+ if ( totalSize > topAddr )
+ throwf("total size of images (0x%X) does not fit below 0x2FE00000", totalSize);
+ return topAddr - totalSize;
+ }
+ 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 if ( strcmp(arch, "arm") == 0 )
+ onlyArchs.insert(CPU_TYPE_ARM);
+ else if ( strcmp(arch, "armv6") == 0 )
+ onlyArchs.insert(CPU_TYPE_ARM);
+ 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);
+ onlyArchs.insert(CPU_TYPE_ARM);
+ }
+
+ // 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;
+}
+
+
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2008 Apple 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 <stdarg.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <vector>
+#include <set>
+#include <ext/hash_set>
+
+
+#include "MachOFileAbstraction.hpp"
+#include "Architectures.hpp"
+
+
+ __attribute__((noreturn))
+void throwf(const char* format, ...)
+{
+ va_list list;
+ char* p;
+ va_start(list, format);
+ vasprintf(&p, format, list);
+ va_end(list);
+
+ const char* t = p;
+ throw t;
+}
+
+
+template <typename A>
+class UnwindPrinter
+{
+public:
+ static bool validFile(const uint8_t* fileContent);
+ static UnwindPrinter<A>* make(const uint8_t* fileContent, uint32_t fileLength, const char* path)
+ { return new UnwindPrinter<A>(fileContent, fileLength, path); }
+ virtual ~UnwindPrinter() {}
+
+
+private:
+ typedef typename A::P P;
+ typedef typename A::P::E E;
+ typedef typename A::P::uint_t pint_t;
+
+ class CStringEquals
+ {
+ public:
+ bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
+ };
+
+ typedef __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> StringSet;
+
+ UnwindPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path);
+ bool findUnwindSection();
+ void printUnwindSection();
+ void getSymbolTableInfo();
+ const char* functionName(pint_t addr);
+ static const char* archName();
+ static void decode(uint32_t encoding, const uint8_t* funcStart, char* str);
+
+ const char* fPath;
+ const macho_header<P>* fHeader;
+ uint64_t fLength;
+ const macho_section<P>* fUnwindSection;
+ const char* fStrings;
+ const char* fStringsEnd;
+ const macho_nlist<P>* fSymbols;
+ uint32_t fSymbolCount;
+ pint_t fMachHeaderAddress;
+};
+
+
+template <> const char* UnwindPrinter<ppc>::archName() { return "ppc"; }
+template <> const char* UnwindPrinter<ppc64>::archName() { return "ppc64"; }
+template <> const char* UnwindPrinter<x86>::archName() { return "i386"; }
+template <> const char* UnwindPrinter<x86_64>::archName() { return "x86_64"; }
+template <> const char* UnwindPrinter<arm>::archName() { return "arm"; }
+
+template <>
+bool UnwindPrinter<ppc>::validFile(const uint8_t* fileContent)
+{
+ 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;
+ switch (header->filetype()) {
+ case MH_EXECUTE:
+ case MH_DYLIB:
+ case MH_BUNDLE:
+ case MH_DYLINKER:
+ return true;
+ }
+ return false;
+}
+
+template <>
+bool UnwindPrinter<ppc64>::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_POWERPC64 )
+ return false;
+ switch (header->filetype()) {
+ case MH_EXECUTE:
+ case MH_DYLIB:
+ case MH_BUNDLE:
+ case MH_DYLINKER:
+ return true;
+ }
+ return false;
+}
+
+template <>
+bool UnwindPrinter<x86>::validFile(const uint8_t* fileContent)
+{
+ 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;
+ switch (header->filetype()) {
+ case MH_EXECUTE:
+ case MH_DYLIB:
+ case MH_BUNDLE:
+ case MH_DYLINKER:
+ return true;
+ }
+ return false;
+}
+
+template <>
+bool UnwindPrinter<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 <>
+bool UnwindPrinter<arm>::validFile(const uint8_t* fileContent)
+{
+ const macho_header<P>* header = (const macho_header<P>*)fileContent;
+ if ( header->magic() != MH_MAGIC )
+ return false;
+ if ( header->cputype() != CPU_TYPE_ARM )
+ return false;
+ switch (header->filetype()) {
+ case MH_EXECUTE:
+ case MH_DYLIB:
+ case MH_BUNDLE:
+ case MH_DYLINKER:
+ return true;
+ }
+ return false;
+}
+
+
+template <typename A>
+UnwindPrinter<A>::UnwindPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path)
+ : fHeader(NULL), fLength(fileLength), fUnwindSection(NULL),
+ fStrings(NULL), fStringsEnd(NULL), fSymbols(NULL), fSymbolCount(0), fMachHeaderAddress(0)
+{
+ // sanity check
+ if ( ! validFile(fileContent) )
+ throw "not a mach-o file that can be checked";
+
+ fPath = strdup(path);
+ fHeader = (const macho_header<P>*)fileContent;
+
+ getSymbolTableInfo();
+
+ if ( findUnwindSection() )
+ printUnwindSection();
+}
+
+
+template <typename A>
+void UnwindPrinter<A>::getSymbolTableInfo()
+{
+ const uint8_t* const endOfFile = (uint8_t*)fHeader + fLength;
+ const uint8_t* const endOfLoadCommands = (uint8_t*)fHeader + sizeof(macho_header<P>) + fHeader->sizeofcmds();
+ const uint32_t cmd_count = fHeader->ncmds();
+ const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
+ const macho_load_command<P>* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ uint32_t size = cmd->cmdsize();
+ const uint8_t* endOfCmd = ((uint8_t*)cmd)+cmd->cmdsize();
+ if ( endOfCmd > endOfLoadCommands )
+ throwf("load command #%d extends beyond the end of the load commands", i);
+ if ( endOfCmd > endOfFile )
+ throwf("load command #%d extends beyond the end of the file", i);
+ if ( cmd->cmd() == LC_SYMTAB) {
+ const macho_symtab_command<P>* symtab = (macho_symtab_command<P>*)cmd;
+ fSymbolCount = symtab->nsyms();
+ fSymbols = (const macho_nlist<P>*)((char*)fHeader + symtab->symoff());
+ fStrings = (char*)fHeader + symtab->stroff();
+ fStringsEnd = fStrings + symtab->strsize();
+ }
+ cmd = (const macho_load_command<P>*)endOfCmd;
+ }
+}
+
+template <typename A>
+const char* UnwindPrinter<A>::functionName(pint_t addr)
+{
+ for (uint32_t i=0; i < fSymbolCount; ++i) {
+ uint8_t type = fSymbols[i].n_type();
+ if ( ((type & N_STAB) == 0) && ((type & N_TYPE) == N_SECT) ) {
+ if ( fSymbols[i].n_value() == addr ) {
+ const char* r = &fStrings[fSymbols[i].n_strx()];
+ //fprintf(stderr, "addr=0x%08llX, i=%u, n_type=0x%0X, r=%s\n", (long long)(fSymbols[i].n_value()), i, fSymbols[i].n_type(), r);
+ return r;
+ }
+ }
+ }
+ return "--anonymous function--";
+}
+
+
+
+template <typename A>
+bool UnwindPrinter<A>::findUnwindSection()
+{
+ const uint8_t* const endOfFile = (uint8_t*)fHeader + fLength;
+ const uint8_t* const endOfLoadCommands = (uint8_t*)fHeader + sizeof(macho_header<P>) + fHeader->sizeofcmds();
+ const uint32_t cmd_count = fHeader->ncmds();
+ const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
+ const macho_load_command<P>* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ uint32_t size = cmd->cmdsize();
+ const uint8_t* endOfCmd = ((uint8_t*)cmd)+cmd->cmdsize();
+ if ( endOfCmd > endOfLoadCommands )
+ throwf("load command #%d extends beyond the end of the load commands", i);
+ if ( endOfCmd > endOfFile )
+ throwf("load command #%d extends beyond the end of the file", i);
+ if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
+ const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd;
+ const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
+ const macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()];
+ for(const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
+ if ( (strcmp(sect->sectname(), "__unwind_info") == 0) && (strcmp(sect->segname(), "__TEXT") == 0) ) {
+ fUnwindSection = sect;
+ fMachHeaderAddress = segCmd->vmaddr();
+ return fUnwindSection;
+ }
+ }
+ }
+ cmd = (const macho_load_command<P>*)endOfCmd;
+ }
+ return false;
+}
+
+#define EXTRACT_BITS(value, mask) \
+ ( (value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask)))-1) )
+
+
+template <>
+void UnwindPrinter<x86_64>::decode(uint32_t encoding, const uint8_t* funcStart, char* str)
+{
+ *str = '\0';
+ switch ( encoding & UNWIND_X86_64_MODE_MASK ) {
+ case UNWIND_X86_64_MODE_RBP_FRAME:
+ {
+ uint32_t savedRegistersOffset = EXTRACT_BITS(encoding, UNWIND_X86_64_RBP_FRAME_OFFSET);
+ uint32_t savedRegistersLocations = EXTRACT_BITS(encoding, UNWIND_X86_64_RBP_FRAME_REGISTERS);
+ if ( savedRegistersLocations == 0 ) {
+ strcpy(str, "rbp frame, no saved registers");
+ }
+ else {
+ sprintf(str, "rbp frame, at -%d:", savedRegistersOffset*8);
+ bool needComma = false;
+ for (int i=0; i < 5; ++i) {
+ if ( needComma )
+ strcat(str, ",");
+ else
+ needComma = true;
+ switch (savedRegistersLocations & 0x7) {
+ case UNWIND_X86_64_REG_NONE:
+ strcat(str, "-");
+ break;
+ case UNWIND_X86_64_REG_RBX:
+ strcat(str, "rbx");
+ break;
+ case UNWIND_X86_64_REG_R12:
+ strcat(str, "r12");
+ break;
+ case UNWIND_X86_64_REG_R13:
+ strcat(str, "r13");
+ break;
+ case UNWIND_X86_64_REG_R14:
+ strcat(str, "r14");
+ break;
+ case UNWIND_X86_64_REG_R15:
+ strcat(str, "r15");
+ break;
+ default:
+ strcat(str, "r?");
+ }
+ savedRegistersLocations = (savedRegistersLocations >> 3);
+ if ( savedRegistersLocations == 0 )
+ break;
+ }
+ }
+ }
+ break;
+ case UNWIND_X86_64_MODE_STACK_IMMD:
+ case UNWIND_X86_64_MODE_STACK_IND:
+ {
+ uint32_t stackSize = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE);
+ uint32_t stackAdjust = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST);
+ uint32_t regCount = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT);
+ uint32_t permutation = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION);
+ if ( (encoding & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_STACK_IND ) {
+ // stack size is encoded in subl $xxx,%esp instruction
+ uint32_t subl = x86_64::P::E::get32(*((uint32_t*)(funcStart+stackSize)));
+ sprintf(str, "stack size=0x%08X, ", subl + 8*stackAdjust);
+ }
+ else {
+ sprintf(str, "stack size=%d, ", stackSize*8);
+ }
+ if ( regCount == 0 ) {
+ strcat(str, "no registers saved");
+ }
+ else {
+ int permunreg[6];
+ switch ( regCount ) {
+ case 6:
+ permunreg[0] = permutation/120;
+ permutation -= (permunreg[0]*120);
+ permunreg[1] = permutation/24;
+ permutation -= (permunreg[1]*24);
+ permunreg[2] = permutation/6;
+ permutation -= (permunreg[2]*6);
+ permunreg[3] = permutation/2;
+ permutation -= (permunreg[3]*2);
+ permunreg[4] = permutation;
+ permunreg[5] = 0;
+ break;
+ case 5:
+ permunreg[0] = permutation/120;
+ permutation -= (permunreg[0]*120);
+ permunreg[1] = permutation/24;
+ permutation -= (permunreg[1]*24);
+ permunreg[2] = permutation/6;
+ permutation -= (permunreg[2]*6);
+ permunreg[3] = permutation/2;
+ permutation -= (permunreg[3]*2);
+ permunreg[4] = permutation;
+ break;
+ case 4:
+ permunreg[0] = permutation/60;
+ permutation -= (permunreg[0]*60);
+ permunreg[1] = permutation/12;
+ permutation -= (permunreg[1]*12);
+ permunreg[2] = permutation/3;
+ permutation -= (permunreg[2]*3);
+ permunreg[3] = permutation;
+ break;
+ case 3:
+ permunreg[0] = permutation/20;
+ permutation -= (permunreg[0]*20);
+ permunreg[1] = permutation/4;
+ permutation -= (permunreg[1]*4);
+ permunreg[2] = permutation;
+ break;
+ case 2:
+ permunreg[0] = permutation/5;
+ permutation -= (permunreg[0]*5);
+ permunreg[1] = permutation;
+ break;
+ case 1:
+ permunreg[0] = permutation;
+ break;
+ }
+ // renumber registers back to standard numbers
+ int registers[6];
+ bool used[7] = { false, false, false, false, false, false, false };
+ for (int i=0; i < regCount; ++i) {
+ int renum = 0;
+ for (int u=1; u < 7; ++u) {
+ if ( !used[u] ) {
+ if ( renum == permunreg[i] ) {
+ registers[i] = u;
+ used[u] = true;
+ break;
+ }
+ ++renum;
+ }
+ }
+ }
+ bool needComma = false;
+ for (int i=0; i < regCount; ++i) {
+ if ( needComma )
+ strcat(str, ",");
+ else
+ needComma = true;
+ switch ( registers[i] ) {
+ case UNWIND_X86_64_REG_RBX:
+ strcat(str, "rbx");
+ break;
+ case UNWIND_X86_64_REG_R12:
+ strcat(str, "r12");
+ break;
+ case UNWIND_X86_64_REG_R13:
+ strcat(str, "r13");
+ break;
+ case UNWIND_X86_64_REG_R14:
+ strcat(str, "r14");
+ break;
+ case UNWIND_X86_64_REG_R15:
+ strcat(str, "r15");
+ break;
+ case UNWIND_X86_64_REG_RBP:
+ strcat(str, "rbp");
+ break;
+ default:
+ strcat(str, "r??");
+ }
+ }
+ }
+ }
+ break;
+ case UNWIND_X86_64_MODE_DWARF:
+ sprintf(str, "dwarf offset 0x%08X, ", encoding & UNWIND_X86_64_DWARF_SECTION_OFFSET);
+ break;
+ default:
+ if ( encoding == 0 )
+ strcat(str, "no unwind information");
+ else
+ strcat(str, "tbd ");
+ }
+ if ( encoding & UNWIND_HAS_LSDA ) {
+ strcat(str, " LSDA");
+ }
+
+}
+
+template <>
+void UnwindPrinter<x86>::decode(uint32_t encoding, const uint8_t* funcStart, char* str)
+{
+ *str = '\0';
+ switch ( encoding & UNWIND_X86_MODE_MASK ) {
+ case UNWIND_X86_MODE_EBP_FRAME:
+ {
+ uint32_t savedRegistersOffset = EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_OFFSET);
+ uint32_t savedRegistersLocations = EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_REGISTERS);
+ if ( savedRegistersLocations == 0 ) {
+ strcpy(str, "ebp frame, no saved registers");
+ }
+ else {
+ sprintf(str, "ebp frame, at -%d:", savedRegistersOffset*4);
+ bool needComma = false;
+ for (int i=0; i < 5; ++i) {
+ if ( needComma )
+ strcat(str, ",");
+ else
+ needComma = true;
+ switch (savedRegistersLocations & 0x7) {
+ case UNWIND_X86_REG_NONE:
+ strcat(str, "-");
+ break;
+ case UNWIND_X86_REG_EBX:
+ strcat(str, "ebx");
+ break;
+ case UNWIND_X86_REG_ECX:
+ strcat(str, "ecx");
+ break;
+ case UNWIND_X86_REG_EDX:
+ strcat(str, "edx");
+ break;
+ case UNWIND_X86_REG_EDI:
+ strcat(str, "edi");
+ break;
+ case UNWIND_X86_REG_ESI:
+ strcat(str, "esi");
+ break;
+ default:
+ strcat(str, "e??");
+ }
+ savedRegistersLocations = (savedRegistersLocations >> 3);
+ if ( savedRegistersLocations == 0 )
+ break;
+ }
+ }
+ }
+ break;
+ case UNWIND_X86_MODE_STACK_IMMD:
+ case UNWIND_X86_MODE_STACK_IND:
+ {
+ uint32_t stackSize = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE);
+ uint32_t stackAdjust = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST);
+ uint32_t regCount = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT);
+ uint32_t permutation = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION);
+ if ( (encoding & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_STACK_IND ) {
+ // stack size is encoded in subl $xxx,%esp instruction
+ uint32_t subl = x86::P::E::get32(*((uint32_t*)(funcStart+stackSize)));
+ sprintf(str, "stack size=0x%08X, ", subl+4*stackAdjust);
+ }
+ else {
+ sprintf(str, "stack size=%d, ", stackSize*4);
+ }
+ if ( regCount == 0 ) {
+ strcat(str, "no saved regs");
+ }
+ else {
+ int permunreg[6];
+ switch ( regCount ) {
+ case 6:
+ permunreg[0] = permutation/120;
+ permutation -= (permunreg[0]*120);
+ permunreg[1] = permutation/24;
+ permutation -= (permunreg[1]*24);
+ permunreg[2] = permutation/6;
+ permutation -= (permunreg[2]*6);
+ permunreg[3] = permutation/2;
+ permutation -= (permunreg[3]*2);
+ permunreg[4] = permutation;
+ permunreg[5] = 0;
+ break;
+ case 5:
+ permunreg[0] = permutation/120;
+ permutation -= (permunreg[0]*120);
+ permunreg[1] = permutation/24;
+ permutation -= (permunreg[1]*24);
+ permunreg[2] = permutation/6;
+ permutation -= (permunreg[2]*6);
+ permunreg[3] = permutation/2;
+ permutation -= (permunreg[3]*2);
+ permunreg[4] = permutation;
+ break;
+ case 4:
+ permunreg[0] = permutation/60;
+ permutation -= (permunreg[0]*60);
+ permunreg[1] = permutation/12;
+ permutation -= (permunreg[1]*12);
+ permunreg[2] = permutation/3;
+ permutation -= (permunreg[2]*3);
+ permunreg[3] = permutation;
+ break;
+ case 3:
+ permunreg[0] = permutation/20;
+ permutation -= (permunreg[0]*20);
+ permunreg[1] = permutation/4;
+ permutation -= (permunreg[1]*4);
+ permunreg[2] = permutation;
+ break;
+ case 2:
+ permunreg[0] = permutation/5;
+ permutation -= (permunreg[0]*5);
+ permunreg[1] = permutation;
+ break;
+ case 1:
+ permunreg[0] = permutation;
+ break;
+ }
+ // renumber registers back to standard numbers
+ int registers[6];
+ bool used[7] = { false, false, false, false, false, false, false };
+ for (int i=0; i < regCount; ++i) {
+ int renum = 0;
+ for (int u=1; u < 7; ++u) {
+ if ( !used[u] ) {
+ if ( renum == permunreg[i] ) {
+ registers[i] = u;
+ used[u] = true;
+ break;
+ }
+ ++renum;
+ }
+ }
+ }
+ bool needComma = false;
+ for (int i=0; i < regCount; ++i) {
+ if ( needComma )
+ strcat(str, ",");
+ else
+ needComma = true;
+ switch ( registers[i] ) {
+ case UNWIND_X86_REG_EBX:
+ strcat(str, "ebx");
+ break;
+ case UNWIND_X86_REG_ECX:
+ strcat(str, "ecx");
+ break;
+ case UNWIND_X86_REG_EDX:
+ strcat(str, "edx");
+ break;
+ case UNWIND_X86_REG_EDI:
+ strcat(str, "edi");
+ break;
+ case UNWIND_X86_REG_ESI:
+ strcat(str, "esi");
+ break;
+ case UNWIND_X86_REG_EBP:
+ strcat(str, "ebp");
+ break;
+ default:
+ strcat(str, "e??");
+ }
+ }
+ }
+ }
+ break;
+ case UNWIND_X86_MODE_DWARF:
+ sprintf(str, "dwarf offset 0x%08X, ", encoding & UNWIND_X86_DWARF_SECTION_OFFSET);
+ break;
+ default:
+ if ( encoding == 0 )
+ strcat(str, "no unwind information");
+ else
+ strcat(str, "tbd ");
+ }
+ if ( encoding & UNWIND_HAS_LSDA ) {
+ strcat(str, " LSDA");
+ }
+
+}
+
+
+template <typename A>
+void UnwindPrinter<A>::decode(uint32_t encoding, const uint8_t* funcStart, char* str)
+{
+
+
+}
+
+template <typename A>
+void UnwindPrinter<A>::printUnwindSection()
+{
+ const uint8_t* sectionContent = (uint8_t*)fHeader + fUnwindSection->offset();
+ macho_unwind_info_section_header<P>* sectionHeader = (macho_unwind_info_section_header<P>*)(sectionContent);
+
+ printf("Arch: %s, Section: __TEXT,__unwind_info (addr=0x%08llX, size=0x%08llX, fileOffset=0x%08X)\n",
+ archName(), fUnwindSection->addr(), fUnwindSection->size(), fUnwindSection->offset());
+ printf("\tversion=0x%08X\n", sectionHeader->version());
+ printf("\tcommonEncodingsArraySectionOffset=0x%08X\n", sectionHeader->commonEncodingsArraySectionOffset());
+ printf("\tcommonEncodingsArrayCount=0x%08X\n", sectionHeader->commonEncodingsArrayCount());
+ printf("\tpersonalityArraySectionOffset=0x%08X\n", sectionHeader->personalityArraySectionOffset());
+ printf("\tpersonalityArrayCount=0x%08X\n", sectionHeader->personalityArrayCount());
+ printf("\tindexSectionOffset=0x%08X\n", sectionHeader->indexSectionOffset());
+ printf("\tindexCount=0x%08X\n", sectionHeader->indexCount());
+ printf("\tcommon encodings: (count=%u)\n", sectionHeader->commonEncodingsArrayCount());
+ const uint32_t* commonEncodings = (uint32_t*)§ionContent[sectionHeader->commonEncodingsArraySectionOffset()];
+ for (uint32_t i=0; i < sectionHeader->commonEncodingsArrayCount(); ++i) {
+ printf("\t\tencoding[%3u]=0x%08X\n", i, A::P::E::get32(commonEncodings[i]));
+ }
+ printf("\tpersonalities: (count=%u)\n", sectionHeader->personalityArrayCount());
+ const uint32_t* personalityArray = (uint32_t*)§ionContent[sectionHeader->personalityArraySectionOffset()];
+ for (uint32_t i=0; i < sectionHeader->personalityArrayCount(); ++i) {
+ printf("\t\t[%2u]=0x%08X\n", i+1, A::P::E::get32(personalityArray[i]));
+ }
+ printf("\tfirst level index: (count=%u)\n", sectionHeader->indexCount());
+ macho_unwind_info_section_header_index_entry<P>* indexes = (macho_unwind_info_section_header_index_entry<P>*)§ionContent[sectionHeader->indexSectionOffset()];
+ for (uint32_t i=0; i < sectionHeader->indexCount(); ++i) {
+ printf("\t\t[%2u] funcOffset=0x%08X, pageOffset=0x%08X, lsdaOffset=0x%08X\n",
+ i, indexes[i].functionOffset(), indexes[i].secondLevelPagesSectionOffset(), indexes[i].lsdaIndexArraySectionOffset());
+ }
+ uint32_t lsdaIndexArraySectionOffset = indexes[0].lsdaIndexArraySectionOffset();
+ uint32_t lsdaIndexArrayEndSectionOffset = indexes[sectionHeader->indexCount()-1].lsdaIndexArraySectionOffset();
+ uint32_t lsdaIndexArrayCount = (lsdaIndexArrayEndSectionOffset-lsdaIndexArraySectionOffset)/sizeof(macho_unwind_info_section_header_lsda_index_entry<P>);
+ printf("\tLSDA table: (section offset 0x%08X, count=%u)\n", lsdaIndexArraySectionOffset, lsdaIndexArrayCount);
+ macho_unwind_info_section_header_lsda_index_entry<P>* lindex = (macho_unwind_info_section_header_lsda_index_entry<P>*)§ionContent[lsdaIndexArraySectionOffset];
+ for (uint32_t i=0; i < lsdaIndexArrayCount; ++i) {
+ printf("\t\t[%3u] funcOffset=0x%08X, lsdaOffset=0x%08X, %s\n",
+ i, lindex[i].functionOffset(), lindex[i].lsdaOffset(), functionName(lindex[i].functionOffset()+fMachHeaderAddress));
+ if ( *(((uint8_t*)fHeader) + lindex[i].lsdaOffset()) != 0xFF )
+ fprintf(stderr, "BAD LSDA entry (does not start with 0xFF) for %s\n", functionName(lindex[i].functionOffset()+fMachHeaderAddress));
+ }
+ for (uint32_t i=0; i < sectionHeader->indexCount()-1; ++i) {
+ printf("\tsecond level index[%u] sectionOffset=0x%08X, count=%u, fileOffset=0x%08X\n", i, indexes[i].secondLevelPagesSectionOffset(),
+ sectionHeader->indexCount(), fUnwindSection->offset()+indexes[i].secondLevelPagesSectionOffset());
+ macho_unwind_info_regular_second_level_page_header<P>* page = (macho_unwind_info_regular_second_level_page_header<P>*)§ionContent[indexes[i].secondLevelPagesSectionOffset()];
+ if ( page->kind() == UNWIND_SECOND_LEVEL_REGULAR ) {
+ printf("\t\tkind=UNWIND_SECOND_LEVEL_REGULAR\n");
+ printf("\t\tentryPageOffset=0x%08X\n", page->entryPageOffset());
+ printf("\t\tentryCount=0x%08X\n", page->entryCount());
+ const macho_unwind_info_regular_second_level_entry<P>* entry = (macho_unwind_info_regular_second_level_entry<P>*)((char*)page+page->entryPageOffset());
+ for (uint32_t j=0; j < page->entryCount(); ++j) {
+ uint32_t funcOffset = entry[j].functionOffset();
+ if ( entry[j].encoding() & UNWIND_HAS_LSDA ) {
+ // verify there is a corresponding entry in lsda table
+ bool found = false;
+ for (uint32_t k=0; k < lsdaIndexArrayCount; ++k) {
+ if ( lindex[k].functionOffset() == funcOffset ) {
+ found = true;
+ break;
+ }
+ }
+ if ( !found ) {
+ fprintf(stderr, "MISSING LSDA entry for %s\n", functionName(funcOffset+fMachHeaderAddress));
+ }
+ }
+ char encodingString[100];
+ decode(entry[j].encoding(), ((const uint8_t*)fHeader)+funcOffset, encodingString);
+ printf("\t\t\t[%3u] funcOffset=0x%08X, encoding=0x%08X (%-40s) %s\n",
+ j, funcOffset, entry[j].encoding(), encodingString, functionName(funcOffset+fMachHeaderAddress));
+ }
+ }
+ else if ( page->kind() == UNWIND_SECOND_LEVEL_COMPRESSED ) {
+ macho_unwind_info_compressed_second_level_page_header<P>* cp = (macho_unwind_info_compressed_second_level_page_header<P>*)page;
+ printf("\t\tkind=UNWIND_SECOND_LEVEL_COMPRESSED\n");
+ printf("\t\tentryPageOffset=0x%08X\n", cp->entryPageOffset());
+ printf("\t\tentryCount=0x%08X\n", cp->entryCount());
+ printf("\t\tencodingsPageOffset=0x%08X\n", cp->encodingsPageOffset());
+ printf("\t\tencodingsCount=0x%08X\n", cp->encodingsCount());
+ const uint32_t* entries = (uint32_t*)(((uint8_t*)page)+cp->entryPageOffset());
+ const uint32_t* encodings = (uint32_t*)(((uint8_t*)page)+cp->encodingsPageOffset());
+ const uint32_t baseFunctionOffset = indexes[i].functionOffset();
+ for (uint32_t j=0; j < cp->entryCount(); ++j) {
+ uint8_t encodingIndex = UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entries[j]);
+ uint32_t encoding;
+ if ( encodingIndex < sectionHeader->commonEncodingsArrayCount() )
+ encoding = A::P::E::get32(commonEncodings[encodingIndex]);
+ else
+ encoding = A::P::E::get32(encodings[encodingIndex-sectionHeader->commonEncodingsArrayCount()]);
+ char encodingString[100];
+ uint32_t funcOff = UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entries[j])+baseFunctionOffset;
+ decode(encoding, ((const uint8_t*)fHeader)+funcOff, encodingString);
+ const char* name = functionName(funcOff+fMachHeaderAddress);
+ if ( encoding & UNWIND_HAS_LSDA ) {
+ // verify there is a corresponding entry in lsda table
+ bool found = false;
+ for (uint32_t k=0; k < lsdaIndexArrayCount; ++k) {
+ if ( lindex[k].functionOffset() == funcOff ) {
+ found = true;
+ break;
+ }
+ }
+ if ( !found ) {
+ fprintf(stderr, "MISSING LSDA entry for %s\n", name);
+ }
+ }
+ printf("\t\t\t[%3u] funcOffset=0x%08X, encoding[%3u]=0x%08X (%-40s) %s\n",
+ j, funcOff, encodingIndex, encoding, encodingString, name);
+ }
+ }
+ else {
+ fprintf(stderr, "\t\tbad page header\n");
+ }
+ }
+
+}
+
+static void dump(const char* path, const std::set<cpu_type_t>& onlyArchs)
+{
+ struct stat stat_buf;
+
+ try {
+ int fd = ::open(path, O_RDONLY, 0);
+ if ( fd == -1 )
+ throw "cannot open file";
+ if ( ::fstat(fd, &stat_buf) != 0 )
+ throwf("fstat(%s) failed, errno=%d\n", path, errno);
+ uint32_t length = stat_buf.st_size;
+ 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) ) {
+ const struct fat_header* fh = (struct fat_header*)p;
+ const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header));
+ for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) {
+ size_t offset = OSSwapBigToHostInt32(archs[i].offset);
+ size_t size = OSSwapBigToHostInt32(archs[i].size);
+ unsigned int cputype = OSSwapBigToHostInt32(archs[i].cputype);
+ if ( onlyArchs.count(cputype) ) {
+ switch(cputype) {
+ case CPU_TYPE_POWERPC:
+ if ( UnwindPrinter<ppc>::validFile(p + offset) )
+ UnwindPrinter<ppc>::make(p + offset, size, path);
+ else
+ throw "in universal file, ppc slice does not contain ppc mach-o";
+ break;
+ case CPU_TYPE_I386:
+ if ( UnwindPrinter<x86>::validFile(p + offset) )
+ UnwindPrinter<x86>::make(p + offset, size, path);
+ else
+ throw "in universal file, i386 slice does not contain i386 mach-o";
+ break;
+ case CPU_TYPE_POWERPC64:
+ if ( UnwindPrinter<ppc64>::validFile(p + offset) )
+ UnwindPrinter<ppc64>::make(p + offset, size, path);
+ else
+ throw "in universal file, ppc64 slice does not contain ppc64 mach-o";
+ break;
+ case CPU_TYPE_X86_64:
+ if ( UnwindPrinter<x86_64>::validFile(p + offset) )
+ UnwindPrinter<x86_64>::make(p + offset, size, path);
+ else
+ throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
+ break;
+ case CPU_TYPE_ARM:
+ if ( UnwindPrinter<arm>::validFile(p + offset) )
+ UnwindPrinter<arm>::make(p + offset, size, path);
+ else
+ throw "in universal file, arm slice does not contain arm mach-o";
+ break;
+ default:
+ throwf("in universal file, unknown architecture slice 0x%x\n", cputype);
+ }
+ }
+ }
+ }
+ else if ( UnwindPrinter<x86>::validFile(p) && onlyArchs.count(CPU_TYPE_I386) ) {
+ UnwindPrinter<x86>::make(p, length, path);
+ }
+ else if ( UnwindPrinter<ppc>::validFile(p) && onlyArchs.count(CPU_TYPE_POWERPC) ) {
+ UnwindPrinter<ppc>::make(p, length, path);
+ }
+ else if ( UnwindPrinter<ppc64>::validFile(p) && onlyArchs.count(CPU_TYPE_POWERPC64) ) {
+ UnwindPrinter<ppc64>::make(p, length, path);
+ }
+ else if ( UnwindPrinter<x86_64>::validFile(p) && onlyArchs.count(CPU_TYPE_X86_64) ) {
+ UnwindPrinter<x86_64>::make(p, length, path);
+ }
+ else if ( UnwindPrinter<arm>::validFile(p) && onlyArchs.count(CPU_TYPE_ARM) ) {
+ UnwindPrinter<arm>::make(p, length, path);
+ }
+ else {
+ throw "not a known file type";
+ }
+ }
+ catch (const char* msg) {
+ throwf("%s in %s", msg, path);
+ }
+}
+
+
+int main(int argc, const char* argv[])
+{
+ std::set<cpu_type_t> onlyArchs;
+ std::vector<const char*> files;
+
+ try {
+ for(int i=1; i < argc; ++i) {
+ const char* arg = argv[i];
+ if ( arg[0] == '-' ) {
+ 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 if ( strcmp(arch, "arm") == 0 )
+ onlyArchs.insert(CPU_TYPE_ARM);
+ else
+ throwf("unknown architecture %s", arch);
+ }
+ else {
+ throwf("unknown option: %s\n", arg);
+ }
+ }
+ else {
+ files.push_back(arg);
+ }
+ }
+
+ // 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);
+ onlyArchs.insert(CPU_TYPE_ARM);
+ }
+
+ // process each file
+ for(std::vector<const char*>::iterator it=files.begin(); it != files.end(); ++it) {
+ dump(*it, onlyArchs);
+ }
+
+ }
+ catch (const char* msg) {
+ fprintf(stderr, "UnwindDump failed: %s\n", msg);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+
+++ /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 <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;
- case CPU_TYPE_ARM:
- fRebasers.push_back(new Rebaser<arm>(&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 if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_ARM)) {
- fRebasers.push_back(new Rebaser<arm>(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 <> cpu_type_t Rebaser<arm>::getArchitecture() const { return CPU_TYPE_ARM; }
-
-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);
- }
-
- // FIXME ¥¥¥ adjust dylib_module if it exists
-}
-
-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 <>
-void Rebaser<arm>::doLocalRelocation(const macho_relocation_info<P>* reloc)
-{
- if ( (reloc->r_address() & R_SCATTERED) == 0 ) {
- if ( reloc->r_type() == ARM_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() == ARM_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";
- case CPU_TYPE_ARM:
- return "arm";
- }
- 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 if ( arch == CPU_TYPE_ARM ) {
- // place dylibs below dyld
- uint64_t topAddr = 0x2FE00000;
- uint64_t totalSize = totalVMSize(arch, files);
- if ( totalSize > topAddr )
- throwf("total size of images (0x%X) does not fit below 0x2FE00000", totalSize);
- return topAddr - totalSize;
- }
- 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 if ( strcmp(arch, "arm") == 0 )
- onlyArchs.insert(CPU_TYPE_ARM);
- else if ( strcmp(arch, "armv6") == 0 )
- onlyArchs.insert(CPU_TYPE_ARM);
- 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);
- onlyArchs.insert(CPU_TYPE_ARM);
- }
-
- // 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;
-}
-
-
-
VALID_ARCHS ?= "ppc ppc64 i386 x86_64 armv6"
MYDIR=$(shell cd ../../bin;pwd)
+LD = ld
+OBJECTDUMP = ObjectDump
+MACHOCHECK = machocheck
+OTOOL = otool
+REBASE = rebase
-# if run within Xcode, add the just built tools to the command path
ifdef BUILT_PRODUCTS_DIR
+ # if run within Xcode, add the just built tools to the command path
PATH := ${BUILT_PRODUCTS_DIR}:${MYDIR}:${PATH}
COMPILER_PATH := ${BUILT_PRODUCTS_DIR}:${MYDIR}:${COMPILER_PATH}
+ LD = ${BUILT_PRODUCTS_DIR}/ld
+ OBJECTDUMP = ${BUILT_PRODUCTS_DIR}/ObjectDump
+ MACHOCHECK = ${BUILT_PRODUCTS_DIR}/machocheck
+ REBASE = ${BUILT_PRODUCTS_DIR}/rebase
else
ifneq "$(findstring /unit-tests/test-cases/, $(shell pwd))" ""
+ # if run from Terminal inside unit-test directory
RELEASEDIR=$(shell cd ../../../build/Release;pwd)
- PATH := ${RELEASEDIR}:${MYDIR}:${PATH}
- COMPILER_PATH := ${RELEASEDIR}:${MYDIR}:${COMPILER_PATH}
+ DEBUGDIR=$(shell cd ../../../build/Debug;pwd)
+ PATH := ${RELEASEDIR}:${DEBUGDIR}:${MYDIR}:${PATH}
+ COMPILER_PATH := ${RELEASEDIR}:${DEBUGDIR}:${MYDIR}:${COMPILER_PATH}
+ LD = ${RELEASEDIR}/ld
+ OBJECTDUMP = ${RELEASEDIR}/ObjectDump
+ MACHOCHECK = ${RELEASEDIR}/machocheck
+ REBASE = ${RELEASEDIR}/rebase
else
PATH := ${MYDIR}:${PATH}:
COMPILER_PATH := ${MYDIR}:${COMPILER_PATH}:
export PATH
export COMPILER_PATH
-LD = ld
-OBJECTDUMP = ObjectDump
-MACHOCHECK = machocheck
-OTOOL = otool
-CC = gcc-4.0 -arch ${ARCH}
+CC = gcc-4.2 -arch ${ARCH} ${SDKExtra}
CCFLAGS = -Wall -std=c99
ASMFLAGS =
-CXX = g++-4.0 -arch ${ARCH}
+CXX = g++-4.2 -arch ${ARCH} ${SDKExtra}
CXXFLAGS = -Wall
ifeq ($(ARCH),armv6)
- LDFLAGS := -syslibroot /Developer/SDKs/Purple
+ SDKExtra = -isysroot /Developer/SDKs/Extra
+ LDFLAGS := -syslibroot /Developer/SDKs/Extra
+ override FILEARCH = arm
+else
+ FILEARCH = $(ARCH)
+endif
+
+ifeq ($(ARCH),armv7)
+ SDKExtra = -isysroot /Developer/SDKs/Extra
+ LDFLAGS := -syslibroot /Developer/SDKs/Extra
override FILEARCH = arm
else
FILEARCH = $(ARCH)
endif
ifeq ($(ARCH),thumb)
- LDFLAGS := -syslibroot /Developer/SDKs/Purple
+ SDKExtra = -isysroot /Developer/SDKs/Extra
+ LDFLAGS := -syslibroot /Developer/SDKs/Extra
CCFLAGS += -mthumb
CXXFLAGS += -mthumb
override ARCH = armv6
FILEARCH = $(ARCH)
endif
+ifeq ($(ARCH),thumb2)
+ SDKExtra = -isysroot /Developer/SDKs/Extra
+ LDFLAGS := -syslibroot /Developer/SDKs/Extra
+ CCFLAGS += -mthumb
+ CXXFLAGS += -mthumb
+ override ARCH = armv7
+ override FILEARCH = arm
+ CC = /Volumes/Leopard/Developer/Platforms/iPhoneOS.platform/usr/bin/gcc-4.2 -arch ${ARCH}
+else
+ FILEARCH = $(ARCH)
+endif
+
RM = rm
RMFLAGS = -rf
# utilites for Makefiles
-PASS_IFF = pass-iff-exit-zero.pl
+PASS_IFF = ${MYDIR}/pass-iff-exit-zero.pl
PASS_IFF_SUCCESS = ${PASS_IFF}
-PASS_IFF_EMPTY = pass-iff-no-stdin.pl
-PASS_IFF_STDIN = pass-iff-stdin.pl
-FAIL_IFF = fail-iff-exit-zero.pl
+PASS_IFF_EMPTY = ${MYDIR}/pass-iff-no-stdin.pl
+PASS_IFF_STDIN = ${MYDIR}/pass-iff-stdin.pl
+FAIL_IFF = ${MYDIR}/fail-iff-exit-zero.pl
FAIL_IFF_SUCCESS = ${FAIL_IFF}
-PASS_IFF_ERROR = pass-iff-exit-non-zero.pl
-FAIL_IF_ERROR = fail-if-exit-non-zero.pl
-FAIL_IF_SUCCESS = fail-if-exit-zero.pl
-FAIL_IF_EMPTY = fail-if-no-stdin.pl
-FAIL_IF_STDIN = fail-if-stdin.pl
+PASS_IFF_ERROR = ${MYDIR}/pass-iff-exit-non-zero.pl
+FAIL_IF_ERROR = ${MYDIR}/fail-if-exit-non-zero.pl
+FAIL_IF_SUCCESS = ${MYDIR}/fail-if-exit-zero.pl
+FAIL_IF_EMPTY = ${MYDIR}/fail-if-no-stdin.pl
+FAIL_IF_STDIN = ${MYDIR}/fail-if-stdin.pl
PASS_IFF_GOOD_MACHO = ${PASS_IFF} ${MACHOCHECK}
FAIL_IF_BAD_MACHO = ${FAIL_IF_ERROR} ${MACHOCHECK}
FAIL_IF_BAD_OBJ = ${FAIL_IF_ERROR} ${OBJECTDUMP} >/dev/null
[ "$PROCTORRUN" ] && exec ../proctor-run
-all_archs="x86_64 armv6 thumb ppc ppc64 i386 "
-valid_archs="x86_64 armv6 ppc ppc64 i386 "
+all_archs="x86_64 ppc i386 "
+#armv6 thumb
+valid_archs="x86_64 armv6 ppc i386 "
# clean first
../bin/make-recursive.pl clean > /dev/null
mkdir /tmp/$$
+mkdir $BUILD_ROOT/lib
+ln -s /Developer/usr/lib/libLTO.dylib ${BUILD_ROOT}/lib/libLTO.dylib
for arch in $all_archs
do
echo ""
--- /dev/null
+##
+# Copyright (c) 2008 Apple 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
+
+#
+# Check the -force_load option.
+# <rdar://problem/3738966> Need a linker option to load all objects from one library
+#
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} foo.c -c -o foo.o
+ ${CC} ${CCFLAGS} bar.c -c -o bar.o
+ ${CC} ${CCFLAGS} baz.c -c -o baz.o
+ ${CC} ${CCFLAGS} bat.c -c -o bat.o
+ libtool -static foo.o bar.o -o libfoobar.a
+ libtool -static baz.o bat.o -o libbazbat.a
+ ${CC} ${CCFLAGS} main.c -Wl,-force_load,libfoobar.a libbazbat.a -o main -Wl,-why_load
+ nm main | grep "_bar" | ${FAIL_IF_EMPTY}
+ nm main | grep "_foo" | ${FAIL_IF_EMPTY}
+ nm main | grep "_baz" | ${FAIL_IF_STDIN}
+ nm main | grep "_bat" | ${FAIL_IF_STDIN}
+ ${PASS_IFF_GOOD_MACHO} main
+
+clean:
+ rm -rf main *.o *.a
--- /dev/null
+void bar() {}
--- /dev/null
+void bat() {}
--- /dev/null
+void baz() {}
+
--- /dev/null
+void foo() {}
--- /dev/null
+
+int main()
+{
+ return 0;
+}
--- /dev/null
+##
+# Copyright (c) 2008 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+
+
+#
+# test thumb2 branch ranges
+#
+
+run: all
+
+
+all:
+ ${CC} ${CCFLAGS} foo.s -c
+ ${CC} ${CCFLAGS} bar.s -c
+ ${CC} ${CCFLAGS} foo.o bar.o -e _foo -o foobar ${ARCH_FLAGS} -nostdlib
+ ${LD} -arch ${ARCH} -r foo.o bar.o -o foobar.o
+ ${CC} ${CCFLAGS} foobar.o -e _foo -o foobar2 ${ARCH_FLAGS} -nostdlib
+ ${PASS_IFF_GOOD_MACHO} foobar
+
+clean:
+ rm *.o foobar*
--- /dev/null
+
+ .text
+#if __thumb__
+ .thumb_func _bar
+ .code 16
+#endif
+
+ .globl _bar
+_bar:
+ nop
+#if __arm__
+ bl _foo
+ blx _foo
+// b _foo
+#endif
+
+
+
+
+ .subsections_via_symbols
+
\ No newline at end of file
--- /dev/null
+
+
+ .text
+
+#if __thumb__
+ .thumb_func _foo
+ .code 16
+#endif
+ .globl _foo
+_foo:
+ nop
+#if __arm__
+ bl _bar
+ blx _bar
+// b _bar
+
+
+_space1:
+
+#if __thumb2__
+ .space 16*1024*1024 -100
+#elif __thumb__
+ .space 4*1024*1024 -100
+#else
+ .space 16*1024*1024 -100
+#endif
+
+#endif // __arm__
+
+
+
+ .subsections_via_symbols
+
\ No newline at end of file
##
-# Copyright (c) 2007 Apple Inc. All rights reserved.
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
#
TESTROOT = ../..
include ${TESTROOT}/include/common.makefile
+SHELL = bash # use bash shell so we can redirect just stderr
+
#
# Test that cfstring literals are coalesced and dead stripped
# There is 3 CFSTR in foo.c and 1 CFSTR in bar.c
ifeq (,${findstring 64,$(ARCH)})
CFSTRING_SIZE = 16
- CFSTRING_SIZE_WRITABLE = 32
else
CFSTRING_SIZE = 32
- CFSTRING_SIZE_WRITABLE = 64
endif
${CC} ${CCFLAGS} foo.c bar.c -o foo -framework CoreFoundation -dead_strip
size -l foo | grep "__cfstring: ${CFSTRING_SIZE}" | ${FAIL_IF_EMPTY}
# now try with -fwritable-strings
- ${CC} ${CCFLAGS} foo.c bar.c -o foo_writable -framework CoreFoundation -dead_strip -fwritable-strings
- size -l foo_writable | grep "__cfstring: ${CFSTRING_SIZE_WRITABLE}" | ${PASS_IFF_STDIN}
+ ${CC} ${CCFLAGS} foo.c bar.c -o foo_writable -framework CoreFoundation -dead_strip -fwritable-strings 2>/dev/null
+ size -l foo_writable | grep "__cfstring: ${CFSTRING_SIZE}" | ${PASS_IFF_STDIN}
clean:
rm -rf foo foo_writable
#include <CoreFoundation/CFString.h>
+CFStringRef OtherCFString = CFSTR("other");
void bar()
{
extern void bar();
+extern CFStringRef OtherCFString;
+
void foo()
{
CFStringGetLength(CFSTR("hello"));
CFStringGetLength(CFSTR("world"));
+ CFStringGetLength(OtherCFString);
}
--- /dev/null
+##
+# Copyright (c) 2009 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# Test the if all symbols from a dylib are weak_import, that the whole dylib is weakly loaded
+#
+
+run: all-${ARCH}
+
+
+all-ppc:
+ ${PASS_IFF} true
+
+all-arm:
+ ${PASS_IFF} true
+
+all-i386: all-real
+
+all-x86_64: all-real
+
+
+all-real:
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib
+ ${FAIL_IF_BAD_MACHO} libfoo.dylib
+
+ ${CC} ${CCFLAGS} main.c -o main libfoo.dylib
+ dyldinfo -bind main | grep wfoo | ${FAIL_IF_EMPTY}
+
+ ${PASS_IFF_GOOD_MACHO} main
+
+
+clean:
+ rm -rf libfoo.dylib main
--- /dev/null
+
+
+__attribute__((weak)) void wfoo() {}
+void foo() {}
--- /dev/null
+
+extern void foo();
+extern void wfoo();
+
+void* pfoo = &foo;
+void* pwfoo = &wfoo;
+
+int main (void)
+{
+ if (pfoo != &foo)
+ return 1;
+ if (pwfoo != &wfoo)
+ return 1;
+
+ return 0;
+}
+
--- /dev/null
+
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# Test that an object file can be code signed
+#
+# <rdar://problem/5881324> when linking a kext the static linker should leave a pad in the headers to allow code signing
+#
+
+CODE_SIGN_ARCH = ${ARCH}
+ifeq (${ARCH},ppc)
+ CODE_SIGN_ARCH = ppc7400
+endif
+
+
+run: all
+
+all:
+ ${CC} foo.c -c -o foo.o
+ ${LD} -r foo.o -o foo2.o
+ codesign_allocate -i foo2.o -a ${CODE_SIGN_ARCH} 256 -o foo3.o
+ ${PASS_IFF} true
+
+clean:
+ rm foo.o foo2.o foo3.o
--- /dev/null
+void foo() {}
--- /dev/null
+##
+# Copyright (c) 2009 Apple 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
+
+#
+# Check that __cstring sections in other segments are not coalesced
+#<rdar://problem/6535736> ld coalesces C strings in different segments
+#
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} main.c custom.s -o main
+ size -l main | grep __cstring | wc -l | grep 2 | ${FAIL_IF_EMPTY}
+ ${PASS_IFF_GOOD_MACHO} main
+
+clean:
+ rm main
--- /dev/null
+
+
+
+ .section __MYSEG, __cstring, cstring_literals
+LC: .ascii "hello"
+
+
+
--- /dev/null
+#include <stdio.h>
+
+int main()
+{
+ printf("hello");
+ return 0;
+}
--- /dev/null
+##
+# Copyright (c) 2008 Apple 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
+
+#
+# Check that cstrings in custom sections are not uniqued with
+# cstrings in the standard section.
+#
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} -c foo.s -o foo.o
+ ${CC} ${CCFLAGS} -c bar.s -o bar.o
+ ${LD} -arch ${ARCH} -r foo.o bar.o -o foobar.o
+ size -l foobar.o | grep "(__TEXT, __cstring): 13" | ${FAIL_IF_EMPTY}
+ size -l foobar.o | grep "(__TEXT, __mystring): 15" | ${FAIL_IF_EMPTY}
+ otool -lv foobar.o | grep -A10 __mystring | grep S_CSTRING_LITERALS | ${FAIL_IF_EMPTY}
+ ${CC} ${CCFLAGS} foo.o bar.o -dynamiclib -o libfoobar.dylib
+ size -l libfoobar.dylib | grep "__cstring: 13" | ${FAIL_IF_EMPTY}
+ size -l libfoobar.dylib | grep "__mystring: 15" | ${FAIL_IF_EMPTY}
+ otool -lv libfoobar.dylib | grep -A10 __mystring | grep S_CSTRING_LITERALS | ${FAIL_IF_EMPTY}
+ ${PASS_IFF_GOOD_MACHO} libfoobar.dylib
+
+clean:
+ rm foo.o bar.o libfoobar.dylib foobar.o
--- /dev/null
+ .cstring
+LC0:
+ .ascii "bar\0"
+LC1:
+ .ascii "coal\0"
+ .text
+
+
+ .section __TEXT, __mystring, cstring_literals
+LC4:
+ .ascii "bar\0"
+LC5:
+ .ascii "mycoal\0"
+
+
+ .data
+#if __LP64__
+ .quad LC0
+ .quad LC1
+ .quad LC4
+ .quad LC5
+#else
+ .long LC0
+ .long LC1
+ .long LC4
+ .long LC5
+#endif
+
+ .subsections_via_symbols
+
+
--- /dev/null
+ .cstring
+LC0:
+ .ascii "foo\0"
+LC1:
+ .ascii "coal\0"
+ .text
+
+
+ .section __TEXT, __mystring, cstring_literals
+LC4:
+ .ascii "foo\0"
+LC5:
+ .ascii "mycoal\0"
+
+
+ .data
+#if __LP64__
+ .quad LC0
+ .quad LC1
+ .quad LC4
+ .quad LC5
+#else
+ .long LC0
+ .long LC1
+ .long LC4
+ .long LC5
+#endif
+
+ .subsections_via_symbols
+
+
--- /dev/null
+##
+# Copyright (c) 2008 Apple 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
+
+#
+# <rdar://problem/6069861> ld -r for x86_64 is changing visibility of cstring constants
+#
+# In order to coalesce strings from different .o files, the linker internally
+# makes them weak/hidden. Test to make sure that weak/hidden does not leak out.
+#
+
+ifeq (${ARCH},x86_64)
+ STRING_LABEL_COUNT = 3
+else
+ STRING_LABEL_COUNT = 1
+endif
+
+
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} -c foo.c -o foo.o
+ ${CC} ${CCFLAGS} -c bar.c -o bar.o
+ ${LD} -arch ${ARCH} -r foo.o bar.o -o foobar.o
+ nm -nm foobar.o | grep __cstring | grep "weak private external" | ${FAIL_IF_STDIN}
+ ${LD} -arch ${ARCH} -r foo.o bar.o -o foobar.o -keep_private_externs
+ nm -nm foobar.o | grep __cstring | grep "weak private external" | ${FAIL_IF_STDIN}
+ nm -m foobar.o | grep __cstring | wc -l | grep ${STRING_LABEL_COUNT} | ${FAIL_IF_EMPTY}
+ ${CC} ${CCFLAGS} foo.o bar.o -dynamiclib -o libfoobar.dylib
+ nm -m libfoobar.dylib | grep __cstring | wc -l | grep 1 | ${FAIL_IF_EMPTY}
+ ${PASS_IFF} /usr/bin/true
+
+clean:
+ rm foo.o bar.o foobar.o libfoobar.dylib
--- /dev/null
+const char* kBar = "hello";
+const char* kBar2 = "there";
--- /dev/null
+
+void func() {}
+
+const char kFoo[] = "foo";
+
+const char* kFoo2 = "hello";
--- /dev/null
+##
+# Copyright (c) 2008 Apple 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
+
+#
+# Tests that a non-weak symbol in an archive cleanly overrides
+# a weak symbol in a .o file with dead code stripping
+#
+# <rdar://problem/6294378> duplicate typeinfo in executable
+#
+
+run: all
+
+all:
+ ${CXX} ${CXXFLAGS} foo.cxx -c -o foo.o
+ ${CXX} ${CXXFLAGS} bar.cxx -c -o bar.o
+ libtool -static foo.o bar.o -o libfoobar.a
+ ${CXX} ${CXXFLAGS} main.cxx libfoobar.a -dead_strip -o main
+ dwarfdump --eh-frame --verify main >/dev/null
+ ${PASS_IFF_GOOD_MACHO} main
+
+
+clean:
+ rm -rf main foo.o bar.o libfoobar.a
--- /dev/null
+
+#include <stdio.h>
+
+void doit()
+{
+ printf("hello there %s\n", "world");
+
+}
+
+void bar()
+{
+ doit();
+}
+
--- /dev/null
+#include <stdio.h>
+
+__attribute__((weak)) void doit()
+{
+ printf("hello %s\n", "world");
+}
+
+
+void foo()
+{
+ doit();
+}
+
--- /dev/null
+
+extern void foo();
+extern void bar();
+
+int main()
+{
+ foo();
+ bar();
+ return 0;
+}
+
+
--- /dev/null
+##
+# Copyright (c) 2008 Apple 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
+
+#
+# Tests that a non-weak symbol in an archive cleanly overrides
+# a weak symbol in a .o file with dead code stripping
+#
+# <rdar://problem/6294378> duplicate typeinfo in executable
+#
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} foo.c -c -o foo.o
+ libtool -static foo.o -o libfoo.a
+ ${CC} ${CCFLAGS} main.c libfoo.a -dead_strip -o main
+ nm main | grep _foo | ${FAIL_IF_EMPTY}
+ ${PASS_IFF_GOOD_MACHO} main
+
+
+clean:
+ rm -rf main foo.o libfoo.a
--- /dev/null
+
+extern void good();
+
+void foo()
+{
+ good();
+}
+
+void loadme()
+{
+// foo();
+}
+
--- /dev/null
+
+extern void loadme();
+
+void good()
+{
+}
+
+void bad()
+{
+}
+
+// foo is first found be dead stripping here
+// then the use of loadme causes libfoo.a(foo.o)
+// to be loaded which overrides foo
+__attribute__((weak)) void foo()
+{
+ bad();
+}
+
+int main()
+{
+ foo();
+ loadme();
+ return 0;
+}
+
+
--- /dev/null
+##
+# Copyright (c) 2009 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# Test that N_NO_DEAD_STRIP bit survives ld -r
+#
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} main.c -c -o main.o
+ nm -m main.o | grep _bar | grep "no dead strip" | ${FAIL_IF_EMPTY}
+ nm -m main.o | grep _foo | grep "no dead strip" | ${FAIL_IF_EMPTY}
+ ${LD} -r main.o -o main2.o
+ nm -m main2.o | grep _bar | grep "no dead strip" | ${FAIL_IF_EMPTY}
+ nm -m main2.o | grep _foo | grep "no dead strip" | ${FAIL_IF_EMPTY}
+ ${CC} main2.o -o main
+ nm -m main | grep _bar | grep "no dead strip" | ${PASS_IFF_EMPTY}
+
+clean:
+ rm -rf main.o main2.o main
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2009 Apple 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@
+ */
+
+
+__attribute__((used)) static void foo()
+{
+}
+
+
+__attribute__((used)) void bar()
+{
+}
+
+
+int main()
+{
+ foo();
+ bar();
+ return 0;
+}
--- /dev/null
+##
+# Copyright (c) 2009 Apple 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
+
+#
+# Check that symbols in no-dead-strip sections can be coalesced.
+# <rdar://problem/6578360> -dead_strip inhibits weak coalescing in no_dead_strip section
+#
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} baz.c -c -o baz.o
+ libtool -static baz.o -o libbaz.a
+ ${CC} ${CCFLAGS} main.c foo.c libbaz.a -dead_strip -o main
+ nm -j main | grep _hidden | wc -l | grep 1 | ${FAIL_IF_EMPTY}
+ ${PASS_IFF_GOOD_MACHO} main
+
+clean:
+ rm -rf main baz.o libbaz.a
--- /dev/null
+void baz()
+{
+}
+
+
+#include "foo.c"
+
--- /dev/null
+
+// function can be coalesced and should not be dead stripped
+void __attribute__ ((weak, section ("__TEXT,__text_no_strip,regular,no_dead_strip"))) foo()
+{
+
+}
+
+
+// function should not be exported, can be coalesced, and should not be dead stripped
+void __attribute__ ((weak, visibility("hidden"), section ("__TEXT,__text_no_strip,regular,no_dead_strip"))) hidden()
+{
+
+}
+
+// bar should be dead stripped
+void __attribute__ ((weak, section ("__DATA,__text2"))) bar()
+{
+
+}
+
+__attribute__((constructor)) static void init()
+{
+ foo();
+ hidden();
+}
--- /dev/null
+
+// baz is in a lazily loaded archive
+extern void baz();
+
+int main()
+{
+ baz();
+ return 0;
+}
+
+
+#include "foo.c"
+
--- /dev/null
+##
+# Copyright (c) 2008 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# Test -mark_dead_strippable_dylib
+#
+# foo is used.
+# bar is not used by should still be linked
+# baz is not used and should be automatically not linked
+#
+
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib
+ ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib
+ ${CC} ${CCFLAGS} -dynamiclib baz.c -o libbaz.dylib -Wl,-mark_dead_strippable_dylib
+ ${CC} ${CCFLAGS} main.c libfoo.dylib libbar.dylib libbaz.dylib -o main
+ otool -L main | grep libfoo.dylib | ${FAIL_IF_EMPTY}
+ otool -L main | grep libbar.dylib | ${FAIL_IF_EMPTY}
+ otool -L main | grep libbaz.dylib | ${FAIL_IF_STDIN}
+ ${PASS_IFF_GOOD_MACHO} main
+
+
+clean:
+
+ rm -rf libbar.dylib libfoo.dylib libbaz.dylib main
--- /dev/null
+
+int bar (void)
+{
+ return 1;
+}
--- /dev/null
+
+int baz (void)
+{
+ return 1;
+}
--- /dev/null
+int foo (void)
+{
+ return 1;
+}
--- /dev/null
+
+extern void foo();
+
+int main()
+{
+ foo();
+ return 0;
+}
int deadwood()
{
- BAR_COUNT1(2);
+ if ( BAR_COUNT1_ENABLED() )
+ BAR_COUNT1(2);
return 0;
}
int a = 1;
while(a) {
- FOO_COUNT1(1);
+ if ( FOO_COUNT1_ENABLED() )
+ FOO_COUNT1(1);
printf("test\n");
- BAR_COUNT1(2);
+ if ( BAR_COUNT1_ENABLED() )
+ BAR_COUNT1(2);
sleep(1);
}
##
-# Copyright (c) 2007 Apple Inc. All rights reserved.
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
#
${CC} ${CCFLAGS} -gdwarf-2 baz.c -c -o baz.o
${FAIL_IF_BAD_OBJ} baz.o
libtool -static bar.o baz.o foo.o -o liball.a
- ${CC} ${CCFLAGS} liball.a -all_load -dynamiclib -o liball.dylib -nodefaultlibs
+ ${CC} ${CCFLAGS} liball.a -all_load -dynamiclib -o liball.dylib -nodefaultlibs -lSystem
${FAIL_IF_BAD_MACHO} liball.dylib
nm -fap liball.dylib | ./stabs-filter.pl > liball.dylib.stabs
${PASS_IFF} diff liball.dylib.stabs expected-stabs
0000 FUN
0000 ENSYM
0000 GSYM _init
-0000 STSYM .my_non_standard_name
0000 STSYM .my_non_standard_name_static
+0000 STSYM .my_non_standard_name
0000 STSYM __ZZ3bariE8bar_init
0000 GSYM _uninit
-0000 STSYM _sinit
-0000 STSYM _suninit
+0000 STSYM __ZL7suninit
+0000 STSYM __ZL5sinit
0000 STSYM __ZZ3bariE10bar_uninit
0000 SO
--- /dev/null
+##
+# Copyright (c) 2008 Apple 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
+
+
+#
+# Verify label in __eh_frame can be removed with no impact
+#
+run: all
+
+all:
+ ${CC} ${CCXXFLAGS} foo.c -c -o foo.o -fexceptions
+ ${CC} ${CCXXFLAGS} bar.c -c -o bar.o -fexceptions
+ ${CC} ${CCXXFLAGS} baz.c -c -o baz.o -fexceptions
+ ${LD} -r foo.o -no_eh_labels -o foo.no.o
+ ${LD} -r bar.o -no_eh_labels -o bar.no.o
+ ${LD} -r baz.o -no_eh_labels -o baz.no.o
+ ${LD} -r foo.o bar.o baz.o -o foobarbaz.o
+ ${LD} -r foo.no.o bar.no.o baz.no.o -o foobarbaz.no.o
+ ${OBJECTDUMP} -no_content -no_sort foobarbaz.o > foobarbaz.dump
+ ${OBJECTDUMP} -no_content -no_sort foobarbaz.no.o > foobarbaz.no.dump
+ ${FAIL_IF_ERROR} dwarfdump --eh-frame --verify foobarbaz.no.o >/dev/null
+ ${PASS_IFF_SUCCESS} diff foobarbaz.dump foobarbaz.no.dump
+
+
+clean:
+ rm -f foo.o bar.o baz.o foo.no.o bar.no.o baz.no.o foobarbaz.*
--- /dev/null
+
+extern void other();
+
+void bar() {
+ other();
+ other();
+}
+
+void __attribute__((weak)) my_weak() {
+ other();
+ other();
+}
+
+void bar2() {
+ other();
+ other();
+}
+
--- /dev/null
+
+extern void other();
+
+void baz() {
+ other();
+ other();
+}
+
+void __attribute__((weak)) my_weak() {
+ other();
+ other();
+}
+
+void baz2() {
+ other();
+ other();
+}
+
--- /dev/null
+
+extern void other();
+
+void foo() {
+ other();
+ other();
+}
+
+void __attribute__((weak)) my_weak() {
+ other();
+ other();
+}
+
+void foo2() {
+ other();
+ other();
+}
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2008 Apple 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>
+#include "func.h"
+
+void baz()
+{
+ func();
+}
+
+
--- /dev/null
+
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# verify that if symbols are stripped out of .o files
+# the linker can still process unwind info from .o files
+# correctly
+
+run: all
+
+all:
+ ${CXX} ${CCXXFLAGS} main.cxx -c -o main1.o -Os
+ #strip main1.o -u -s keep.exp -o main2.o
+ ${LD} main1.o -r -x -exported_symbols_list keep.exp -o main2.o
+ ${CXX} ${CCXXFLAGS} main1.o -Wl,-x -o main1 -exported_symbols_list keep.exp
+ ${CXX} ${CCXXFLAGS} main2.o -o main2
+ unwinddump -arch ${ARCH} main1 > main1.unwind
+ unwinddump -arch ${ARCH} main2 > main2.unwind
+ ${PASS_IFF} diff main1.unwind main2.unwind
+
+clean:
+ rm -f main1* main2*
--- /dev/null
+_main
+__Z3barv
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2008 Apple 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>
+#include <stdlib.h>
+
+void bar()
+{
+}
+
+
+void foo2()
+{
+ int a = arc4random();
+ int b = arc4random();
+ fprintf(stderr, "hello %d %d\n", a, b);
+}
+
+void foo1()
+{
+ foo2();
+ fprintf(stderr, "world\n");
+}
+
+
+
+int main()
+{
+ foo1();
+ bar();
+ return 0;
+}
\ No newline at end of file
${FAIL_IF_BAD_OBJ} foo2.o
${CXX} ${CCXXFLAGS} foo2.o bar.cxx -dynamiclib -o libfoobar.dylib
${FAIL_IF_BAD_MACHO} libfoobar.dylib
+ dwarfdump --eh-frame --verify libfoobar.dylib > /dev/null
nm libfoobar.dylib | grep '.eh' | ${PASS_IFF_EMPTY}
clean:
.globl _start
.globl _end
+ .globl _endAlias
_start:
.long 0
.long 0
_end:
+_endAlias:
##
-# Copyright (c) 2007 Apple Inc. All rights reserved.
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
#
all:
${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_foo*bar'
- nm -j -g -f libfoo.dylib | diff - expect1 | ${FAIL_IF_STDIN}
+ nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect1 | ${FAIL_IF_STDIN}
${FAIL_IF_BAD_MACHO} libfoo.dylib
${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f?o'
- nm -j -g -f libfoo.dylib | diff - expect2 | ${FAIL_IF_STDIN}
+ nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect2 | ${FAIL_IF_STDIN}
${FAIL_IF_BAD_MACHO} libfoo.dylib
${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_foo*'
- nm -j -g -f libfoo.dylib | diff - expect3 | ${FAIL_IF_STDIN}
+ nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect3 | ${FAIL_IF_STDIN}
${FAIL_IF_BAD_MACHO} libfoo.dylib
${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f*o*'
- nm -j -g -f libfoo.dylib | diff - expect4 | ${FAIL_IF_STDIN}
+ nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect4 | ${FAIL_IF_STDIN}
${FAIL_IF_BAD_MACHO} libfoo.dylib
${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,_foo -Wl,-exported_symbol -Wl,'_*bar'
- nm -j -g -f libfoo.dylib | diff - expect5 | ${FAIL_IF_STDIN}
+ nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect5 | ${FAIL_IF_STDIN}
${FAIL_IF_BAD_MACHO} libfoo.dylib
${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -exported_symbols_list list5
- nm -j -g -f libfoo.dylib | diff - expect5 | ${FAIL_IF_STDIN}
+ nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect5 | ${FAIL_IF_STDIN}
${FAIL_IF_BAD_MACHO} libfoo.dylib
${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-unexported_symbol -Wl,'_*2*'
- nm -j -g -f libfoo.dylib | diff - expect6 | ${FAIL_IF_STDIN}
+ nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect6 | ${FAIL_IF_STDIN}
${FAIL_IF_BAD_MACHO} libfoo.dylib
${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f[abcdef]o'
- nm -j -g -f libfoo.dylib | diff - expect7 | ${FAIL_IF_STDIN}
+ nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect7 | ${FAIL_IF_STDIN}
${FAIL_IF_BAD_MACHO} libfoo.dylib
${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f[a-f]o'
- nm -j -g -f libfoo.dylib | diff - expect7 | ${FAIL_IF_STDIN}
+ nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect7 | ${FAIL_IF_STDIN}
${FAIL_IF_BAD_MACHO} libfoo.dylib
${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f[a-z]o'
- nm -j -g -f libfoo.dylib | diff - expect8 | ${FAIL_IF_STDIN}
+ nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect8 | ${FAIL_IF_STDIN}
${FAIL_IF_BAD_MACHO} libfoo.dylib
${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f[a-fnop]o'
- nm -j -g -f libfoo.dylib | diff - expect8 | ${FAIL_IF_STDIN}
+ nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect8 | ${FAIL_IF_STDIN}
${PASS_IFF_GOOD_MACHO} libfoo.dylib
clean:
##
-# Copyright (c) 2007 Apple Inc. All rights reserved.
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
#
all:
${CC} ${CCFLAGS} test.c -dynamiclib -exported_symbols_list test.exp -o libtest.dylib
- nm -jg libtest.dylib | grep _ > test.nm
+ nm -jg libtest.dylib | grep '^_' > test.nm
diff test.nm expected.nm
${PASS_IFF_GOOD_MACHO} libtest.dylib
PWD = $(shell pwd)
#
-# The point of this test is to check the two forms of the
-# -filelist option
+# Check the two forms of the -filelist option
#
run: all
all:
- ${CC} ${CCFLAGS} -c hello.c -o hello-${ARCH}.o
- ${FAIL_IF_BAD_OBJ} hello-${ARCH}.o
- echo "${PWD}/hello-${ARCH}.o" > "${PWD}/filelist1"
- cd /tmp && ${CC} ${CCFLAGS} -filelist "${PWD}/filelist1" -o "${PWD}/hello-${ARCH}"
- ${FAIL_IF_BAD_MACHO} hello-${ARCH}
- echo "hello-${ARCH}.o" > "${PWD}/filelist2"
- cd /tmp && ${CC} ${CCFLAGS} -filelist "${PWD}/filelist2,${PWD}" -o "${PWD}/hello-${ARCH}"
- ${PASS_IFF_GOOD_MACHO} hello-${ARCH}
+ ${CC} ${CCFLAGS} -c hello.c -o hello.o
+ ${FAIL_IF_BAD_OBJ} hello.o
+ echo "${PWD}/hello.o" > "${PWD}/filelist1"
+ cd /tmp && ${CC} ${CCFLAGS} -filelist "${PWD}/filelist1" -o "${PWD}/hello"
+ ${FAIL_IF_BAD_MACHO} hello
+ echo "hello.o" > "${PWD}/filelist2"
+ cd /tmp && ${CC} ${CCFLAGS} -filelist "${PWD}/filelist2,${PWD}" -o "${PWD}/hello"
+ ${FAIL_IF_BAD_MACHO} hello
+ echo "${PWD}/hello.o" > "${PWD}/filelist,withComma"
+ cd /tmp && ${CC} ${CCFLAGS} -filelist "${PWD}/filelist,withComma" -o "${PWD}/hello"
+ ${PASS_IFF_GOOD_MACHO} hello
clean:
- rm hello-* *.o filelist1 filelist2
+ rm -f hello.o hello filelist1 filelist2 filelist,withComma
##
-# Copyright (c) 2007 Apple Inc. All rights reserved.
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
#
all-x86_64:
${CC} ${CCFLAGS} foo.c bar.c -dynamiclib -o libfoobar.dylib
- otool -Iv libfoobar.dylib | grep 0x | ${FAIL_IF_STDIN}
+ otool -Iv libfoobar.dylib | grep 0x | grep _ | ${FAIL_IF_STDIN}
${CC} ${CCFLAGS} foo.c bar.c -dynamiclib -o libfoobar.dylib -flat_namespace
otool -Iv libfoobar.dylib | grep 0x | ${PASS_IFF_STDIN}
+++ /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
-# can link a program with a large zero-fill section
-#
-
-run: all
-
-all:
- ${CC} ${CCFLAGS} -c test.c -o test-${ARCH}.o
- ${FAIL_IF_BAD_OBJ} test-${ARCH}.o
- ${CC} ${CCFLAGS} -c a.c -o a-${ARCH}.o
- ${FAIL_IF_BAD_OBJ} a-${ARCH}.o
- ${FAIL_IF_ERROR} ar -r libtest-${ARCH}.a test-${ARCH}.o 2>/dev/null
- ${FAIL_IF_ERROR} ranlib libtest-${ARCH}.a
- ${CC} ${CCFLAGS} a-${ARCH}.o -L. -ltest-${ARCH} -o a-${ARCH}
- ${PASS_IFF_GOOD_MACHO} a-${ARCH}
-
-clean:
- rm -rf *.o *.a a-*
+++ /dev/null
-extern int common_var;
-int *fn();
-
-int
-main(int argc, char **argv)
-{
- return 0!=&common_var;
-}
+++ /dev/null
-The point of this test is a sanity check that ld can link a program with a large zero-fill section
+++ /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@
- */
-
-struct abc {
- int a;
- int b;
- int c;
-} struct_var;
-
-int common_var;
-extern const int defined_var;
-
-int *fn()
-{
- return &common_var;
-}
grep ordinal fail.log | ${PASS_IFF_EMPTY}
clean:
- rm *.dylib main fail.log
+ rm -f *.dylib main fail.log
--- /dev/null
+##
+# Copyright (c) 2008 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# Test that initializers are automatically sorted to start of __text
+# and terminators are sorted to end of __text
+#
+# <rdar://problem/6061904> automatically order initializers to start of __TEXT
+#
+
+run: all
+
+all:
+ ${CXX} ${CXXFLAGS} main.cxx foo.cxx bar.cxx -o main -dead_strip
+ nm -s __TEXT __text -nj main | grep -v dyld_stub_binding_helper | c++filt > actual-order.txt
+ ${PASS_IFF} diff actual-order.txt expected-order.txt
+
+
+clean:
+ rm -rf main actual-order.txt
--- /dev/null
+
+
+
+class Bar {
+public:
+ Bar() : a(20) {}
+ ~Bar() {}
+private:
+ int a;
+};
+
+
+Bar b1;
+Bar b2;
+
+
--- /dev/null
+M::M()
+__static_initialization_and_destruction_0(int, int)
+global constructors keyed to m1
+Foo::Foo()
+__static_initialization_and_destruction_0(int, int)
+global constructors keyed to f1
+Bar::Bar()
+__static_initialization_and_destruction_0(int, int)
+global constructors keyed to b1
+start
+_main
+M::~M()
+Foo::~Foo()
+Bar::~Bar()
+___tcf_0
+___tcf_1
+___tcf_0
+___tcf_1
+___tcf_0
+___tcf_1
--- /dev/null
+
+
+
+class Foo {
+public:
+ Foo() : a(20) {}
+ ~Foo() {}
+private:
+ int a;
+};
+
+
+Foo f1;
+Foo f2;
--- /dev/null
+#include <stdio.h>
+
+class M {
+public:
+ M() : a(20) {}
+ ~M() {}
+private:
+ int a;
+};
+
+
+M m1;
+M m2;
+
+int main()
+{
+ return 0;
+}
--- /dev/null
+
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# Sanity check that ld can link a kext
+#
+
+ifeq (${ARCH},x86_64)
+ FILE_TYPE = KEXTBUNDLE
+else
+ FILE_TYPE = OBJECT
+endif
+
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} -static -fno-common -mkernel -c mykext.c -o mykext.o
+ ${CC} ${CCFLAGS} -static -fno-common -mkernel -c mykextinfo.c -o mykextinfo.o
+ ${CC} ${CCFLAGS} -Wl,-kext mykext.o mykextinfo.o -nostdlib -lkmodc++ -lkmod -lcc_kext -o mykext
+ otool -hv mykext | grep ${FILE_TYPE} | ${FAIL_IF_EMPTY}
+ nm -nm mykext | grep '(undefined) external _extern_global' | ${FAIL_IF_EMPTY}
+ nm -nm mykext | grep '(__DATA,__data) external _my_global' | ${FAIL_IF_EMPTY}
+ otool -rv mykext | grep '_extern_global' | ${FAIL_IF_EMPTY}
+ ${PASS_IFF} true
+
+clean:
+ rm -f mykext.o mykextinfo.o mykext
--- /dev/null
+#include <mach/mach_types.h>
+
+
+int my_global = 3;
+extern int extern_global;
+
+kern_return_t mykext_start (kmod_info_t * ki, void * d) {
+ ++my_global;
+ ++extern_global;
+ return KERN_SUCCESS;
+}
+
+
+kern_return_t mykext_stop (kmod_info_t * ki, void * d) {
+ --my_global;
+ --extern_global;
+ return KERN_SUCCESS;
+}
--- /dev/null
+#include <mach/mach_types.h>
+
+extern kern_return_t _start(kmod_info_t *ki, void *data);
+extern kern_return_t _stop(kmod_info_t *ki, void *data);
+__private_extern__ kern_return_t mykext_start(kmod_info_t *ki, void *data);
+__private_extern__ kern_return_t mykext_stop(kmod_info_t *ki, void *data);
+
+KMOD_EXPLICIT_DECL(com.yourcompany.kext.mykext, "1.0.0d1", _start, _stop)
+__private_extern__ kmod_start_func_t *_realmain = mykext_start;
+__private_extern__ kmod_stop_func_t *_antimain = mykext_stop;
+__private_extern__ int _kext_apple_cc = __APPLE_CC__ ;
+
all: zero one two three four five six seven eight nine ten \
eleven twelve thirteen fourteen fifteen sixteen seventeen \
- eighteen nineteen twenty
-
+ eighteen nineteen
zero:
#
# llvm : b5.c : Dfoo2
# MachO : main5.c : Dfoo3, Ufoo1
#
- #echo "Five..."
+ #echo "verify that if call to mach-o is optimized away, mach-o func is dead stripped"
${LLVMGCC} ${CCFLAGS} --emit-llvm a5.c -c -o a5.o
${LLVMGCC} ${CCFLAGS} --emit-llvm b5.c -c -o b5.o
${LLVMGCC} ${CCFLAGS} main5.c -c -o main5.o
# llvm : a12.c
# MachO : main12.c
#
- #echo "Tweleve..."
+ #echo "verify tentative def in llvm .o referenced from mach-o"
${LLVMGCC} ${CCFLAGS} --emit-llvm a12.c -c -o a12.o
${LLVMGCC} ${CCFLAGS} main12.c -c -o main12.o
${LLVMGCC} ${CCFLAGS} a12.o main12.o -o main12.exe
twenty:
#echo verify bitcode files in archives works
- ${LLVMGCC} ${CCFLAGS} --emit-llvm a20.c -c -o a20.o
- ${LLVMGCC} ${CCFLAGS} --emit-llvm b20.c -c -o b20.o
- ar cru lib20.a a20.o b20.o
- ${LLVMGCC} ${CCFLAGS} main20.c lib20.a -all_load -o main20.exe
- nm main20.exe | grep _foo | ${PASS_IFF_STDIN}
-
+ #${LLVMGCC} ${CCFLAGS} --emit-llvm a20.c -c -o a20.o
+ #${LLVMGCC} ${CCFLAGS} --emit-llvm b20.c -c -o b20.o
+ #libtool -static a20.o b20.o -o lib20.a
+ #${LLVMGCC} ${CCFLAGS} main20.c lib20.a -all_load -o main20.exe
+ #nm main20.exe | grep _foo | ${PASS_IFF_STDIN}
+
+
clean:
- rm -rf *.o main*.exe big.* *.dylib main16.exe.lto.bc fail.log lib20.a
+ rm -rf *.o main*.exe big.* *.dylib main16.exe.lto.bc fail.log lib20.a main21.preload lib21.a
--- /dev/null
+##
+# Copyright (c) 2008 Apple 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
+
+#
+# <rdar://problem/5935600> LTO : 176.gcc and 177.mesa build failure at -O4
+#
+# Check that LTO can bring in an archive member and that member needs a
+# symbol from a dylib.
+#
+
+LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH}
+LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH}
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} foo.c -c -o foo.o
+ libtool -static foo.o -o libfoo.a
+ ${LLVMGCC} ${CCFLAGS} --emit-llvm main.c -c -o main.o
+ ${LLVMGCC} ${CCFLAGS} main.o -o main libfoo.a
+ ${PASS_IFF_GOOD_MACHO} main
+
+clean:
+ rm -rf main foo.o libfoo.a main.o
--- /dev/null
+#include <stdio.h>
+void foo()
+{
+ fprintf(stderr, "hello\n");
+}
--- /dev/null
+extern void foo();
+
+int main()
+{
+ foo();
+ return 0;
+}
+
--- /dev/null
+##
+# Copyright (c) 2008 Apple 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
+
+#
+# Check that -mllvm optiions work. Verify --disable-inlining
+# results in foo() not being inlined
+#
+
+LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH}
+LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH}
+
+run: all
+
+all:
+ ${LLVMGCC} ${CCFLAGS} --emit-llvm main.c -c -o main.o
+ ${LLVMGCC} ${CCFLAGS} main.o -o main
+ nm main | grep _foo | ${FAIL_IF_STDIN}
+ ${LLVMGCC} ${CCFLAGS} main.o -o main2 -Wl,-mllvm -Wl,--disable-inlining
+ nm main2 | grep _foo | ${FAIL_IF_EMPTY}
+ ${PASS_IFF_GOOD_MACHO} main
+
+clean:
+ rm main main.o main2
--- /dev/null
+
+#include <stdio.h>
+
+
+void foo(int x)
+{
+ printf("hello, world %d\n", x);
+}
+
+int main()
+{
+ foo(10);
+ return 0;
+}
+
--- /dev/null
+##
+# Copyright (c) 2008 Apple 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
+
+#
+# verify -preload -pie produces relocations
+#
+
+LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH}
+LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH}
+
+run: all
+
+all:
+ ${LLVMGCC} ${CCFLAGS} --emit-llvm a.c -c -o a.o
+ ${LLVMGCC} ${CCFLAGS} --emit-llvm b.c -c -o b.o
+ ${LLVMGCC} ${CCFLAGS} --emit-llvm main.c -c -o main.o
+ ${LLVMGCC} ${CCFLAGS} main.o a.o b.o -Wl,-preload -Wl,-pie -o main.preload \
+ -e _entry -nostdlib -Wl,-segalign,0x20 -Wl,-seg1addr,0x200
+ otool -rv main.preload | grep "Local relocation information" | ${PASS_IFF_STDIN}
+
+
+
+
+clean:
+ rm a.o b.o main.o main.preload
--- /dev/null
+
+extern const char* mystring;
+
+const char** myp = &mystring;
--- /dev/null
+ const char* mystring = "hello";
--- /dev/null
+
+extern const char** myp;
+
+
+const char** entry(int i) {
+ if ( i ) {
+ *myp = "help";
+ }
+ return myp;
+}
+
--- /dev/null
+##
+# Copyright (c) 2008 Apple 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
+
+#
+# Check that LTO works when a llvm bitcode has a weak symbol _foo
+# and mach-o has a strong _foo.
+#
+
+LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH}
+LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH}
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} foo.c -c -o foo.o
+ ${LLVMGCC} ${CCFLAGS} --emit-llvm main.c -c -o main.o
+ ${LLVMGCC} ${CCFLAGS} main.o foo.o -o main
+ otool -Iv main | grep _abort | ${FAIL_IF_STDIN}
+ ${PASS_IFF_GOOD_MACHO} main
+
+clean:
+ rm main foo.o main.o
+
\ No newline at end of file
--- /dev/null
+
+
+void foo()
+{
+ // do nothing
+}
--- /dev/null
+
+#include <stdlib.h>
+
+static void die() { abort(); }
+
+
+__attribute__((visibility("hidden"),weak)) void foo()
+{
+ die();
+}
+
+int main()
+{
+ foo();
+
+}
+
--- /dev/null
+##
+# Copyright (c) 2009 Apple 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
+
+#
+# Check that a bundle built with no data links
+# <rdar://problem/6532377> gcc DejaGnu failure: building longcall/dylib library
+#
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} foo.c -bundle -o foo.bundle
+ ${PASS_IFF_GOOD_MACHO} foo.bundle
+
+clean:
+ rm foo.bundle
--- /dev/null
+#include <stdlib.h>
+
+void foo()
+{
+ rand();
+}
\ No newline at end of file
--- /dev/null
+##
+# Copyright (c) 2008 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# Test that when ld creates an object file with no symbols
+# and no section content, that the segment size is zero
+# and the LC_SYMTAB is empty.
+#
+# <rdar://problem/6048484> LC_SEGMENT_64 filesize incorrect for MH_OBJECT filetype
+#
+
+run: all
+
+all:
+ as -arch ${ARCH} -n empty.s -o empty.o
+ ${LD} -r empty.o -x -o empty2.o
+ otool -lv empty2.o | egrep 'vmsize 0x0[0]+$$' | ${FAIL_IF_EMPTY}
+ otool -lv empty2.o | grep 'filesize 0' | ${FAIL_IF_EMPTY}
+ otool -lv empty2.o | grep 'nsyms 0' | ${FAIL_IF_EMPTY}
+ otool -lv empty2.o | grep 'symoff 0' | ${FAIL_IF_EMPTY}
+ ${PASS_IFF} true
+
+clean:
+ rm empty.o empty2.o
${CC} ${CCFLAGS} -c other.c -o other.o
${LD} -r -arch ${ARCH} foo.o other.o -o fooall.o -exported_symbol _foo
# make sure there are two indirect symbols: _foo and LOCAL
- otool -Iv fooall.o | grep "2 entries" | ${FAIL_IF_EMPTY}
- otool -Iv fooall.o | grep _foo | ${FAIL_IF_EMPTY}
+ otool -Iv fooall.o | grep "3 entries" | ${FAIL_IF_EMPTY}
+ otool -Iv fooall.o | grep _foo | ${FAIL_IF_EMPTY}
+ otool -Iv fooall.o | grep _tent | ${FAIL_IF_EMPTY}
otool -Iv fooall.o | grep _other | ${FAIL_IF_STDIN}
# make sure re-parsed correctly
${OBJECTDUMP} fooall.o | grep name: | grep '_foo$$non_lazy_ptr' | ${FAIL_IF_EMPTY}
${OBJECTDUMP} fooall.o | grep name: | grep '_other$$non_lazy_ptr' | ${FAIL_IF_EMPTY}
+ ${OBJECTDUMP} fooall.o | grep name: | grep '_tent$$non_lazy_ptr' | ${FAIL_IF_EMPTY}
${PASS_IFF} true
clean:
int getother() { return other; }
+
+extern int tent;
+
+int gettent() { return tent; }
+
int foo = 2;
int other = 3;
+int tent;
--- /dev/null
+##
+# Copyright (c) 2009 Apple 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
+
+
+#
+# Verify an Objective-C object file when run through
+# ld -r -x
+# <rdar://problem/6605499> x86_64 obj-c runtime confused when static lib is stripped
+#
+#
+
+SELECTOR_REFS = "__OBJC,__message_refs"
+
+ifeq ($(ARCH),x86_64)
+ SELECTOR_REFS = "__DATA,__objc_selrefs"
+endif
+ifeq ($(ARCH),armv6)
+ SELECTOR_REFS = "__DATA,__objc_selrefs"
+endif
+
+
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} test.m -c -o test.o
+ ${OBJECTDUMP} -no_content test.o | grep -B3 -A6 ${SELECTOR_REFS} > test.dump
+
+ ${LD} -arch ${ARCH} -r test.o -x -o test-r.o
+ ${OBJECTDUMP} -no_content test-r.o | grep -B3 -A6 ${SELECTOR_REFS} > test-r.dump
+
+ diff test.dump test-r.dump | ${PASS_IFF_EMPTY}
+
+clean:
+ rm -rf test.o test.dump test-r.o test-r.dump
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2009 Apple 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>
+#include <stdlib.h>
+
+@interface Foo @end
+@implementation Foo
++(void)initialize { }
++(void)foo {
+ fprintf(stderr, "GOOD\n");
+ exit(0);
+}
++(void)bar {
+ fprintf(stderr, "BAD\n");
+ abort();
+}
+@end
+
+void PublicFunction(void)
+{
+ [Foo foo];
+ [Foo bar];
+}
all:
${CC} ${CCFLAGS} test.m -c -o test.o
- ObjectDump -no_content test.o | grep -v zero-fill-at> test.dump
+ ${OBJECTDUMP} -no_content test.o | grep -v zero-fill-at> test.dump
- ${LD} -arch ${ARCH} -r test.o -o test-r.o
- ObjectDump -no_content test-r.o | grep -v zero-fill-at > test-r.dump
+ ${LD} -arch ${ARCH} -r test.o -keep_private_externs -o test-r.o
+ ${OBJECTDUMP} -no_content test-r.o | grep -v zero-fill-at > test-r.dump
diff test.dump test-r.dump | ${PASS_IFF_EMPTY}
##
-# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+# Copyright (c) 2006-2009 Apple Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
#
# verify if operator new is overridden that WEAK_DEFINES is set
${CXX} ${CXXFLAGS} -DOP_NEW -I${TESTROOT}/include -o main main.cxx
otool -hv main | grep WEAK_DEFINES | ${FAIL_IF_EMPTY}
+ # verify if operator new is overridden but not exported, WEAK_DEFINES is not set
+ ${CXX} ${CXXFLAGS} -DOP_NEW -I${TESTROOT}/include -o main main.cxx -Wl,-exported_symbol,_main
+ otool -hv main | grep WEAK_DEFINES | ${FAIL_IF_STDIN}
# verify if operator new is not overridden that WEAK_DEFINES is not set
${CXX} ${CXXFLAGS} -I${TESTROOT}/include -o main main.cxx
otool -hv main | grep WEAK_DEFINES | ${PASS_IFF_EMPTY}
#if OP_NEW
void* operator new(size_t s) throw (std::bad_alloc)
{
- return malloc(s);;
+ return malloc(s);
}
#endif
##
-# Copyright (c) 2007 Apple Inc. All rights reserved.
+# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
#
run: all
all:
- ${CXX} ${CXXFLAGS} main.cxx -DANCHOR=1 -o main -Wl,-order_file -Wl,main.order
+ ${CXX} ${CXXFLAGS} main.cxx -o main -Wl,-order_file -Wl,main.order
${FAIL_IF_BAD_MACHO} main
- nm -n -g -j main | grep "_GLOBAL__N" > main.actual
+ nm -n -j main | grep "_GLOBAL__N" > main.actual
${PASS_IFF} diff main.actual main.expected
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2007 Apple Inc. All rights reserved.
+ * Copyright (c) 2007-2008 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <stdio.h>
-#if ANCHOR
- int anchor = 4;
-#endif
namespace {
struct myanonstruct { int a; };
-__Z3barPN17_GLOBAL__N_anchor12myanonstructE
-__ZN3wow17_GLOBAL__N_anchor5innerEv
-__ZN17_GLOBAL__N_anchor3bazEPNS_12myanonstructE
-__ZN17_GLOBAL__N_anchor3fooEv
+__Z3barPN12_GLOBAL__N_112myanonstructE
+__ZN3wow12_GLOBAL__N_15innerEv
+__ZN12_GLOBAL__N_13bazEPNS_12myanonstructE
+__ZN12_GLOBAL__N_13fooEv
-__Z3barPN95_GLOBAL__N__Volumes_my_src_ld64_unit_tests_test_cases_order_file_ans_main.cxx_00000000_38BA812C12myanonstructE
-__ZN3wow95_GLOBAL__N__Volumes_my_src_ld64_unit_tests_test_cases_order_file_ans_main.cxx_00000000_38BA812C5innerEv
-__ZN95_GLOBAL__N__Volumes_my_src_ld64_unit_tests_test_cases_order_file_ans_main.cxx_00000000_38BA812C3bazEPNS_12myanonstructE
-__ZN95_GLOBAL__N__Volumes_my_src_ld64_unit_tests_test_cases_order_file_ans_main.cxx_00000000_38BA812C3fooEv
+__Z3barPN12_GLOBAL__N_112myanonstructE
+__ZN3wow12_GLOBAL__N_15innerEv
+__ZN12_GLOBAL__N_13bazEPNS_12myanonstructE
+__ZN12_GLOBAL__N_13fooEv
-##
-# 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
run: all
all:
- ${CC} ${CCFLAGS} main.c -o main -prebind -mmacosx-version-min=10.3
+ # SnowLeopard is missing libmx.dylib which gcc thinks it needs
+ ${CC} ${CCFLAGS} main.c -o main -prebind -mmacosx-version-min=10.3 -nostdlib -lcrt1.o -lSystem
otool -hv main | grep ${KEYWORD} | ${FAIL_IF_EMPTY}
${PASS_IFF_GOOD_MACHO} main
--- /dev/null
+##
+# Copyright (c) 2009 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# Test that a public re-exported library is automatically added as a dependent
+# unless nothing is used from it.
+#
+
+
+run: all
+
+all:
+
+# -sub_library for 10.4
+ ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name /usr/lib/libbar.dylib -mmacosx-version-min=10.4
+ ${FAIL_IF_BAD_MACHO} libbar.dylib
+ ${CC} ${CCFLAGS} -dynamiclib middle.c -o libmiddle.dylib -lbar -L. -sub_library libbar -install_name /mid/libmiddle.dylib -mmacosx-version-min=10.4
+ ${FAIL_IF_BAD_MACHO} libmiddle.dylib
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lmiddle -L. -sub_library libmiddle -install_name /usr/lib/libfoo.dylib -mmacosx-version-min=10.4
+ ${FAIL_IF_BAD_MACHO} libfoo.dylib
+ ${CC} ${CCFLAGS} -dynamiclib other.c -o libother.dylib -lbar -L. -sub_library libbar -install_name /usr/lib/libother.dylib -mmacosx-version-min=10.4
+ ${FAIL_IF_BAD_MACHO} libother.dylib
+ ${CC} ${CCFLAGS} main.c -DCALL_BAR libfoo.dylib libother.dylib -o main -L. -mmacosx-version-min=10.4
+ nm -m main | grep _bar | grep libbar | ${FAIL_IF_EMPTY}
+ ${PASS_IFF_GOOD_MACHO} main
+
+# -sub_library for 10.5
+ ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name /usr/lib/libbar.dylib -mmacosx-version-min=10.5
+ ${FAIL_IF_BAD_MACHO} libbar.dylib
+ ${CC} ${CCFLAGS} -dynamiclib middle.c -o libmiddle.dylib -lbar -L. -sub_library libbar -install_name /mid/libmiddle.dylib -mmacosx-version-min=10.5
+ ${FAIL_IF_BAD_MACHO} libmiddle.dylib
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lmiddle -L. -sub_library libmiddle -install_name /usr/lib/libfoo.dylib -mmacosx-version-min=10.5
+ ${FAIL_IF_BAD_MACHO} libfoo.dylib
+ ${CC} ${CCFLAGS} -dynamiclib other.c -o libother.dylib -lbar -L. -sub_library libbar -install_name /usr/lib/libother.dylib -mmacosx-version-min=10.5
+ ${FAIL_IF_BAD_MACHO} libother.dylib
+ ${CC} ${CCFLAGS} main.c -DCALL_BAR libfoo.dylib libother.dylib -o main -L. -mmacosx-version-min=10.5
+ nm -m main | grep _bar | grep libbar | ${FAIL_IF_EMPTY}
+ ${PASS_IFF_GOOD_MACHO} main
+
+
+clean:
+ rm -rf libbar.dylib libfoo.dylib libmiddle.dylib libother.dylib main
--- /dev/null
+
+int bar (void)
+{
+ return 1;
+}
--- /dev/null
+int foo (void)
+{
+ return 1;
+}
--- /dev/null
+
+extern void bar();
+
+int main()
+{
+ bar();
+ return 0;
+}
--- /dev/null
+
+void middle() {}
+
--- /dev/null
+void other() {}
##
-# Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
+# Copyright (c) 2006-2008 Apple Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
#
${CC} ${CCFLAGS} -c bar.m -o bar.${ARCH}.o
${FAIL_IF_BAD_OBJ} bar.${ARCH}.o
- ${CC} ${CCFLAGS} foo.${ARCH}.o bar.${ARCH}.o -dynamiclib -Wl,-no_uuid -o libfoo.${ARCH}.dylib -framework Foundation -framework CoreFoundation
+ ${CC} ${CCFLAGS} foo.${ARCH}.o bar.${ARCH}.o -dynamiclib -Wl,-no_uuid -Wl,-no_order_data -o libfoo.${ARCH}.dylib -framework Foundation -framework CoreFoundation
${FAIL_IF_BAD_MACHO} libfoo.${ARCH}.dylib
- ${CC} ${CCFLAGS} foo.${ARCH}.o bar.${ARCH}.o -dynamiclib -Wl,-no_uuid -o libfoo-alt.${ARCH}.dylib -framework Foundation -framework CoreFoundation -seg1addr 0x12340000 -install_name libfoo.${ARCH}.dylib
+ ${CC} ${CCFLAGS} foo.${ARCH}.o bar.${ARCH}.o -dynamiclib -Wl,-no_uuid -Wl,-no_order_data -o libfoo-alt.${ARCH}.dylib -framework Foundation -framework CoreFoundation -seg1addr 0x12340000 -install_name libfoo.${ARCH}.dylib
${FAIL_IF_BAD_MACHO} libfoo-alt.${ARCH}.dylib
-
- rebase -arch ${ARCH} -low_address 0x12340000 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 *.o *.dylib
+ rm -f *.o *.dylib
* @APPLE_LICENSE_HEADER_END@
*/
+
+
#if __arm__
.text
.align 2
@ call external + addend
bne _external+16
+
+_pointer_diffs:
+ nop
+ bl 1f
+1: nop
+ .long _foo-1b
+ .long _foo+10-1b
+ .long _test_branches-1b
+ .long _test_branches+3-1b
+ .long (_test_branches - _test_loads) + -2097152
+ .long (_test_calls - _test_loads) + -2097152
+
#endif
#if __ppc__ || __ppc64__
# call internal + addend
call _test_branches+0x19000
+ # 16-bit call internal
+ callw _test_branches
+
+ # 16-bit call internal + addend
+ callw _test_branches+13
+
# call external
call _external
movl _foo+10-1b(%eax),%esi
movl _test_branches-1b(%eax),%esi
movl _test_branches+3-1b(%eax),%esi
+ cmpl $(( (_test_branches - _test_loads) + -2097152 )),(%esp)
+ cmpl $(( (_test_calls - _test_loads) + -2097152 )),(%esp)
+
_word_relocs:
callw _pointer_diffs
.quad _test_branches - .
.quad _test_branches - L1
.quad L1 - _prev
+ #tests support for 32-bit absolute pointers
+ .long _prev
+ .long L1
# 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)
--- /dev/null
+##
+# Copyright (c) 2009 Apple 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
+
+#
+# <rdar://problem/6583757> i386 relocation error with negative offsets from local labels
+#
+
+ifeq (${ARCH},i386)
+ TARGET = run-i386
+else
+ TARGET = run-other
+endif
+
+run: ${TARGET}
+
+run-other:
+ ${PASS_IFF} /usr/bin/true
+
+
+run-i386:
+ ${CC} ${ASMFLAGS} test.s -c -o test.o
+ ${OBJECTDUMP} test.o | grep "__data@0 plus 0xFFFFFFE2" | ${FAIL_IF_EMPTY}
+
+ ${LD} -arch ${ARCH} -r -keep_private_externs test.o -o test-r.o
+ ${OBJECTDUMP} test-r.o | grep "__data@0 plus 0xFFFFFFE2" | ${PASS_IFF_STDIN}
+
+
+clean:
+ rm -rf *.o
--- /dev/null
+/*
+ * Copyright (c) 2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+
+#if __i386__
+ .text
+ .align 2
+
+_negative_offset_from_local_label:
+ nop
+ .space 100
+ movl -80+L3(,%eax,4), %edx
+ ret
+
+ .data
+L2: .space 50
+L3: .space 50
+_d: .long 0
+
+#endif
+
+
--- /dev/null
+##
+# Copyright (c) 2009 Apple 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
+
+#
+# Validate long section names are preserved
+# <rdar://problem/6805002> corrupt metaclass entry in dynamic library
+#
+
+all:
+ ${CC} ${CCFLAGS} main.c a.s c.s b.s -o main
+ nm -m main | grep __aaaaaaaaaaaaaa | grep __TEXT | grep _at | ${FAIL_IF_EMPTY}
+ nm -m main | grep __bbbbbbbbbbbbbb | grep __TEXT | grep _bt | ${FAIL_IF_EMPTY}
+ nm -m main | grep __cccccccccccccc | grep __TEXT | grep _ct | ${FAIL_IF_EMPTY}
+ nm -m main | grep __aaaaaaaaaaaaaa | grep __DATA | grep _ad | ${FAIL_IF_EMPTY}
+ nm -m main | grep __bbbbbbbbbbbbbb | grep __DATA | grep _bd | ${FAIL_IF_EMPTY}
+ nm -m main | grep __cccccccccccccc | grep __DATA | grep _cd | ${FAIL_IF_EMPTY}
+ ${PASS_IFF_GOOD_MACHO} main
+
+clean:
+ rm -rf main
--- /dev/null
+
+ .section __TEXT,__aaaaaaaaaaaaaa
+_at: .space 128
+
+ .section __DATA,__aaaaaaaaaaaaaa
+_ad: .space 128
+
+
+
--- /dev/null
+
+ .section __TEXT,__bbbbbbbbbbbbbb
+_bt: .space 128
+
+ .section __DATA,__bbbbbbbbbbbbbb
+_bd: .space 128
+
+
+
--- /dev/null
+
+ .section __TEXT,__cccccccccccccc
+_ct: .space 128
+
+
+ .section __DATA,__cccccccccccccc
+_cd: .space 128
+
+
+
+
--- /dev/null
+
+
+int main() { return 0; }
+
--- /dev/null
+##
+# Copyright (c) 2009 Apple 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
+
+#
+# Verify only dylibs with install paths in /System/Library or /usr/lib
+# get LC_SEGMENT_SPLIT_INFO
+#
+
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -install_name /usr/lib/libfoo.dylib
+ otool -lv libfoo.dylib | grep LC_SEGMENT_SPLIT_INFO | ${FAIL_IF_EMPTY}
+ ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -install_name /System/Library/Frameworks/Foo.framework/Foo
+ otool -lv libfoo.dylib | grep LC_SEGMENT_SPLIT_INFO | ${FAIL_IF_EMPTY}
+ ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -install_name /usr/local/lib/libfoo.dylib
+ otool -lv libfoo.dylib | grep LC_SEGMENT_SPLIT_INFO | ${FAIL_IF_STDIN}
+ ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib
+ otool -lv libfoo.dylib | grep LC_SEGMENT_SPLIT_INFO | ${FAIL_IF_STDIN}
+ ${PASS_IFF_GOOD_MACHO} libfoo.dylib
+
+clean:
+ rm libfoo.dylib
--- /dev/null
+
+void foo() {}
+
include ${TESTROOT}/include/common.makefile
#
-# Sanity check that -slow_stubs for i386 leaves no __IMPORT segment
+# Sanity check that i386 for 10.6 does not use __IMPORT segment
#
run: all
all:
- ${CC} ${CCFLAGS} hello.c -o hello -Wl,-slow_stubs
+ ${CC} ${CCFLAGS} hello.c -o hello
size -l hello | grep __IMPORT | ${FAIL_IF_STDIN}
- ${CC} ${CCFLAGS} hello.c -dynamiclib -o libhello.dylib -Wl,-slow_stubs
+ ${CC} ${CCFLAGS} hello.c -dynamiclib -o libhello.dylib
size -l libhello.dylib | grep __IMPORT | ${FAIL_IF_STDIN}
${PASS_IFF_GOOD_MACHO} hello
##
-# Copyright (c) 2007 Apple Inc. All rights reserved.
+# Copyright (c) 2007-2009 Apple Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
#
TESTROOT = ../..
include ${TESTROOT}/include/common.makefile
-ifeq "${ARCH}" "i386"
- POINTER_SEGMENT = __IMPORT
- POINTER_SECTION = __pointers
-else
- POINTER_SEGMENT = __DATA
- POINTER_SECTION = __nl_symbol_ptr
-endif
+POINTER_SEGMENT = __DATA
+POINTER_SECTION = __nl_symbol_ptr
#
# Test that using strip -R to selectively strip symbol names
# of of a .o file still works with ld.
+# And for i386 that there are no __IMPORT/__pointers left <rdar://problem/6666004>
#
run: all
${CC} ${CCFLAGS} all.o -dynamiclib -o dylib2
otool -X -s ${POINTER_SEGMENT} ${POINTER_SECTION} dylib1 >dylib1.pointers
otool -X -s ${POINTER_SEGMENT} ${POINTER_SECTION} dylib2 >dylib2.pointers
+ size -l dylib1 | grep __IMPORT | ${FAIL_IF_STDIN}
+ size -l dylib2 | grep __IMPORT | ${FAIL_IF_STDIN}
${PASS_IFF} diff dylib1.pointers dylib2.pointers
clean:
#
# Test that -mdynamic-no-pic jump table in the middle of
-# a function does not cause relocations.
+# a function does not cause relocations.
#
# <rdar://problem/5847206> SPEC2000/eon built with -mdynamic-no-pic won't run
#
-run: all
+run: test-run-${ARCH}
-all:
+
+test-run-ppc:
+ ${PASS_IFF} true
+
+test-run-x86_64:
+ ${PASS_IFF} true
+
+test-run-armv6: test-run-i386
+
+test-run-i386:
# check jump table in a weak function
${CC} ${CCFLAGS} main.c switch.s -o main
otool -rv main | grep _foo | ${FAIL_IF_STDIN}
# check jump table with -pie, should have no external and some local relocations
${CC} ${CCFLAGS} main.c switch.s -o main -Wl,-pie -read_only_relocs suppress
otool -rv main | grep "External relocation" | ${FAIL_IF_STDIN}
- otool -rv main | grep "Local relocation" | ${FAIL_IF_EMPTY}
+# otool -rv main | grep "Local relocation" | ${FAIL_IF_EMPTY}
${PASS_IFF_GOOD_MACHO} main
clean:
- rm -rf libbar.dylib libfoo.dylib main-10.4 main-10.5 main-10.4a main-10.5a
+ rm -rf libbar.dylib libfoo.dylib main-10.4 main-10.5 main-10.4a main-10.5a Frameworks
#
# Test how tentative definitions interact with archives
# main.c has a tenative definition for _var which
-# should *not* cause libfoo.a(foo.o) to be loaded.
+# should cause libfoo.a(foo.o) to be loaded which in turn
+# should cause _bar from libbar.dylib to be used.
#
# <rdar://problem/5779681> ld crashes building XsanFS
# <rdar://problem/5852023> -undefined dynamic_lookup causes spurious extra symbols
+# <rdar://problem/5613343> need to search for definitions for common symbols
#
run: all
all:
+ ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib
${CC} ${CCFLAGS} foo.c -c -o foo.o
- libtool -static foo.o -o libfoo.a
- ${CC} ${CCFLAGS} main.c libfoo.a -o main
+ libtool -static foo.o -o libfoo.a
+ ${CC} ${CCFLAGS} main.c libfoo.a libbar.dylib -o main
+ # verify _foo got pulled in because _var was a tentative in main.o
+ nm main | grep "_foo" | ${FAIL_IF_EMPTY}
+ # verify -dead_strip pulls in non-tentative _var from libfoo.a
+ ${CC} ${CCFLAGS} main.c libfoo.a libbar.dylib -o main -dead_strip
+ nm -m main | grep "_var" | grep __data | ${FAIL_IF_EMPTY}
+ # verify dynamic_lookup works and has no duplicate symbols
${CC} ${CCFLAGS} main.c libfoo.a -o main -undefined dynamic_lookup
- nm -m main | grep "looked up" | ${FAIL_IF_STDIN}
+ nm -m main | grep "looked up" | ${FAIL_IF_EMPTY}
${PASS_IFF_GOOD_MACHO} main
clean:
- rm -rf main libfoo.a foo.o
+ rm -rf main libfoo.a foo.o libbar.dylib
--- /dev/null
+void bar() {}
int var;
+int other_tent;
int main()
{
--- /dev/null
+##
+# Copyright (c) 2008 Apple 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
+
+
+#
+# <rdar://problem/6401277> pointers to thumb symbols can be mangled
+#
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} foo.c -c -o foo.o
+ ${CC} ${CCFLAGS} bar.c -c -o bar.o
+ # verify no +1 thumb errors
+ ${FAIL_IF_ERROR} ${OBJECTDUMP} foo.o | grep "plus 0x00000001" | ${FAIL_IF_STDIN}
+ ${FAIL_IF_ERROR} ${OBJECTDUMP} bar.o | grep "plus 0x00000001" | ${FAIL_IF_STDIN}
+ ${LD} -arch ${ARCH} -r -keep_private_externs foo.o bar.o -o foobar.o
+ ${LD} -arch ${ARCH} -r -keep_private_externs foobar.o -o foobar2.o
+ ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content foobar.o > foobar.o.dump
+ ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content foobar2.o > foobar2.o.dump
+ # verify no +1 thumb errors in merged result
+ grep "plus 0x00000001" foobar.o.dump | ${FAIL_IF_STDIN}
+ # verify round trip though ld -r works
+ ${PASS_IFF} diff foobar.o.dump foobar2.o.dump
+
+clean:
+ rm -rf foo.o bar.o foobar.o foobar2.o foobar.o.dump foobar2.o.dump
--- /dev/null
+
+void bar1() {}
+void bar2() {}
+char bar_array[3] = { 1,2,3 };
+
--- /dev/null
+
+
+extern void bar1();
+extern void bar2();
+extern char bar_array[];
+
+void foo1() {}
+void foo2() {}
+char foo_array[3] = { 1,2,3 };
+
+
+
+void* foostuff[] = { &foo1, &foo2, foo_array, &foo_array[3] };
+void* barstuff[] = { &bar1, &bar2, bar_array, &bar_array[3] };
--- /dev/null
+##
+# Copyright (c) 2008 Apple 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
+
+
+#
+# Verify -unexported_symbols_list works with -r
+# <rdar://problem/5905900> Building kext x86_64 with unexported symbols file causes linking problems
+#
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} foo.c -c -o foo.o
+ ${LD} -arch ${ARCH} foo.o -r -unexported_symbols_list foo.exp -o foo2.o
+ nm -m foo2.o | grep _foo | grep "non-external" | ${PASS_IFF_STDIN}
+
+clean:
+ rm -rf foo.o foo2.o
--- /dev/null
+
+extern int fooCount;
+extern int barCount;
+
+void foo() { fooCount++; }
+void bar() { barCount++; }
+int global = 4;
+int googoo = 5;
+
--- /dev/null
+_foo
+_global
+
--- /dev/null
+##
+# Copyright (c) 2008 Apple 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
+
+
+#
+#
+# Tests that a main executable with a weak symbol has MH_WEAK_DEFINES
+# Tests that a main executable with a weak symbol made non-global by
+# an export list does not has MH_WEAK_DEFINES
+#
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} main.c -o main
+ otool -hv main | grep WEAK_DEFINES | ${FAIL_IF_EMPTY}
+ ${CC} ${CCFLAGS} main.c -o main -Wl,-exported_symbol,_main
+ otool -hv main | grep WEAK_DEFINES | ${FAIL_IF_STDIN}
+ ${CC} ${CCFLAGS} main.c -o main -Wl,-exported_symbol,_my_weak
+ otool -hv main | grep WEAK_DEFINES | ${FAIL_IF_EMPTY}
+ ${PASS_IFF_GOOD_MACHO} main
+
+clean:
+ rm main
--- /dev/null
+#include <stdio.h>
+
+void __attribute__((weak)) my_weak()
+{
+
+}
+
+int main()
+{
+ my_weak();
+ return 0;
+}
+
--- /dev/null
+##
+# Copyright (c) 2008 Apple Computer, Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# Test that -weak_library marks all symbols used as weak
+#
+
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib
+ ${FAIL_IF_BAD_MACHO} libfoo.dylib
+ ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib
+ ${FAIL_IF_BAD_MACHO} libbar.dylib
+ ${CC} ${CCFLAGS} main.c -o main -weak_library libfoo.dylib libbar.dylib
+ nm -m main | grep _foo1 | grep weak | ${FAIL_IF_EMPTY}
+ nm -m main | grep _bar | grep weak | ${FAIL_IF_STDIN}
+ ${PASS_IFF_GOOD_MACHO} main
+
+clean:
+ rm -rf libfoo.dylib libbar.dylib main
--- /dev/null
+
+
+
+void bar1() {}
+void bar2() {}
+
+
+int bar_data1 = 0;
+int bar_data2 = 0;
--- /dev/null
+
+
+
+void foo1() {}
+void foo2() {}
+
+
+int foo_data1 = 0;
+int foo_data2 = 0;
--- /dev/null
+
+extern void foo1();
+extern void foo2();
+extern void bar1();
+extern void bar2();
+
+extern int foo_data1;
+extern int foo_data2;
+extern int bar_data1;
+extern int bar_data2;
+
+
+
+// make external relocation to foo_data1 and bar_data1
+int* pfoo = &foo_data1;
+int* pbar = &bar_data1;
+
+
+int main (void)
+{
+ // make non-lazy reference to foo1 and bar1
+ if ( &foo1 == &bar1 ) {
+ // make lazy reference to foo2 and bar2
+ foo2();
+ bar2();
+ }
+
+ // make non-lazy reference to foo_data2 and bar_data2
+ return *pfoo + *pbar + foo_data2 + bar_data2;
+}
+
${CC} ${CCFLAGS} -dynamiclib -single_module foo.c -o libfoo-${ARCH}.dylib
${FAIL_IF_BAD_MACHO} libfoo-${ARCH}.dylib
- ${CC} ${CCFLAGS} -mmacosx-version-min=10.4 main.c -o main-${ARCH} libfoo-${ARCH}.dylib
+ ${CC} ${CCFLAGS} main.c -o main-${ARCH} libfoo-${ARCH}.dylib
nm -m main-${ARCH} | grep _func1 | grep -v weak >/dev/null
nm -m main-${ARCH} | grep _func2 | grep weak >/dev/null
nm -m main-${ARCH} | grep _func3 | grep -v weak >/dev/null
nm -m main-${ARCH} | grep _data2 | grep weak >/dev/null
nm -m main-${ARCH} | grep _data3 | grep -v weak >/dev/null
nm -m main-${ARCH} | grep _data4 | grep weak >/dev/null
- otool -rv main-${ARCH} | grep _data6 > /dev/null
+ nm -m main-${ARCH} | grep _data5 | grep -v weak >/dev/null
+ nm -m main-${ARCH} | grep _data6 | grep weak >/dev/null
+ #otool -rv main-${ARCH} | grep _data6 > /dev/null
${FAIL_IF_BAD_MACHO} main-${ARCH}
- ${CC} ${CCFLAGS} -mmacosx-version-min=10.4 main.c -dynamiclib -o main-${ARCH}.dylib libfoo-${ARCH}.dylib
+ ${CC} ${CCFLAGS} main.c -dynamiclib -o main-${ARCH}.dylib libfoo-${ARCH}.dylib
nm -m main-${ARCH}.dylib | grep _func1 | grep -v weak >/dev/null
nm -m main-${ARCH}.dylib | grep _func2 | grep weak >/dev/null
nm -m main-${ARCH}.dylib | grep _func3 | grep -v weak >/dev/null
nm -m main-${ARCH}.dylib | grep _data2 | grep weak >/dev/null
nm -m main-${ARCH}.dylib | grep _data3 | grep -v weak >/dev/null
nm -m main-${ARCH}.dylib | grep _data4 | grep weak >/dev/null
- otool -rv main-${ARCH}.dylib | grep _data6 > /dev/null
+ #otool -rv main-${ARCH}.dylib | grep _data6 > /dev/null
${PASS_IFF_GOOD_MACHO} main-${ARCH}.dylib
clean: