+++ /dev/null
-
--------- tagged ld64-128.1
-
-2011-09-13 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/10111122> Enable dyld to be put into the dyld shared cache
-
-2011-09-13 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/10100056> Fix "using ld_classic" warning for i386 kexts
-
-2011-09-13 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/10052396> LTO many have eliminated need for some undefines
-
--------- tagged ld64-128
-
-2011-09-08 Nick Kledzik <kledzik@apple.com>
-
- Rework 8924157 fix to sort all sections
-
-2011-09-07 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/10089743> ER: -current_version should allow 64-bit a.b.c.d.e tuple
-
-2011-09-06 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/10038370> -mdynamic-no-pic broke with movw of weak thumb symbol
-
-2011-09-01 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/10057157> Turn crash into a warning if __dso_handle is defined in user code
- Added test case: unit-tests/test-cases/dso_handle
-
-2011-08-31 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/6780050> Add -fatal_warnings
-
-2011-08-30 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8482298> Support .weak_def_can_be_hidden via LTO interface
-
-2011-08-29 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/10043807> improve performance of zerofill->data ordering
-
-2011-08-29 Nick Kledzik <kledzik@apple.com>
-
- Implement -print_statistics
-
-2011-08-25 Nick Kledzik <kledzik@apple.com>
-
- check for overlaps between pinned segments and regular segments
-
-2011-08-23 Nick Kledzik <kledzik@apple.com>
-
- do got elimination more aggressively in static and preload mode
-
-2011-08-22 Nick Kledzik <kledzik@apple.com>
-
- enable __dso_handle in -preload
-
-2011-08-22 Nick Kledzik <kledzik@apple.com>
-
- fix section$end to sort to end of __mod_init_func section
-
-2011-08-18 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9737570> __constructor section removed with -dead_strip
-
-2011-08-11 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8828975> remove ld support for PowerPC
-
-2011-08-11 Nick Kledzik <kledzik@apple.com>
-
- Fix spurious -segaddr alignment warning
-
--------- tagged ld64-127.3
-
-2011-08-31 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8924157> [regression] C++ Initializers from archives not sorted
- Added test case: unit-tests/test-cases/archive-init-order
-
--------- tagged ld64-127.2
-
-2011-08-15 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9945513> suppress version load command for simulator builds
-
--------- tagged ld64-127.1
-
-2011-07-26 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9847280> Csu needs to support for armv7 variants
-
--------- tagged ld64-127
-
-2011-07-26 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9838090> crash with TLS + -dead_strip
-
-2011-07-20 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9778727> ld64-123.2.1/ChangeLog contains internal train names and radar titles
-
-2011-07-17 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9777977> ld crashes with an assertion failure when linking WebKit with LTO
-
-2011-07-14 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9779759> Personalities missing when using compact unwind
-
-2011-07-13 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9740166> force loaded archives not listed in LD_TRACE
-
-2011-07-05 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9716724> spurious warning: Codegen prevents image from working in dyld shared cache
-
-2011-07-01 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9707126> Fix -classic_linker option
-
--------- tagged ld64-126.5
-
-2011-06-15 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9618702> ld64-124.6: ld -r introduces duplicate symbols
-
-2011-06-15 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9610466> loosen check for 32-bit absolute address out of range
-
--------- tagged ld64-126.3.1
-
-2011-06-15 Nick Kledzik <kledzik@apple.com>
-
- Update armv7 variants
-
--------- tagged ld64-126.2
-
-2011-06-13 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9461567> iOS ld -r loses dont-dead-strip attribute on __objc_nlclslist section
-
-2011-06-13 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9231829> LC_ENCRYPTION_INFO size can be wrong
-
-
--------- tagged ld64-126.1
-
-2011-06-10 Nick Kledzik <kledzik@apple.com>
-
- Add back support for armv7 variants
-
--------- tagged ld64-126
-
-2011-06-09 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9581690> -ObjC does not work for simulator
-
-2011-06-09 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9551362> clang ld: bad codegen, pointer diff
- Added test case: unit-tests/test-cases/weak-def-hidden-and-global
-
-2011-06-03 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9553065> warning then assertion when libSystem.dylib is missing
-
-2011-06-02 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9544194> ld crash with resolver functions
-
-2011-06-01 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/7259423> define way for compilers to specify compact unwind info
- Added test case: unit-tests/test-cases/compact-unwind-basic
- Updated unwinddump tool to display compact unwind info in .o files
-
-2011-06-01 Nick Kledzik <kledzik@apple.com>
-
- Allow 8612550 (turn ordered zero fill symbols into zero data) to work not just for dyld
-
-2011-06-01 Nick Kledzik <kledzik@apple.com>
-
- Remove trailing /. in dwarf source dirs to cannoicalize paths
-
-2011-06-01 Nick Kledzik <kledzik@apple.com>
-
- Sort debug notes by output order instead of input order.
-
-2011-06-01 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9537755> remove support for invoking ld_classic in iOS
-
-2011-06-01 Nick Kledzik <kledzik@apple.com>
-
- Fix arm branch interworking in -r for armv6
-
-2011-06-01 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9521882> i386 regression with pointer-diff of same pointer
-
-2011-05-27 Nick Kledzik <kledzik@apple.com>
-
- Canonicalize dwarf source file dirname to always end in /
-
-2011-05-27 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9513487> support arm branch interworking in -r mode (use extern relocs)
-
-2011-05-27 Nick Kledzik <kledzik@apple.com>
-
- Add -page_align_data_atoms option
-
-2011-05-24 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9292295> align(16384) doesn't produce 16K aligned globals on ARMv7
-
-2011-05-24 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9493908> support arm shims in sections other than __text
-
-2011-05-23 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8750693> ld64 should only install to the platform in iOS
-
-2011-05-19 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9452006> Ld assertion with unusual section order
-
-2011-05-17 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9441273> Linker is not automatically weak loading dylibs when all references are weak
-
--------- tagged ld64-125.3
-
-2011-05-12 Nick Kledzik <kledzik@apple.com>
-
- Fix missing split-seg-info for kindSetTargetImageOffset
-
-2011-05-12 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9420745> Linker crashes with __gcc_except_tab data belonging to no FDE
-
-2011-05-11 Nick Kledzik <kledzik@apple.com>
-
- Fix nop padding for arm code
-
-2011-05-05 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9394006> x86_64: cmp of GOT slot loses weak_import bit
-
--------- tagged ld64-125.2
-
-2011-05-02 Nick Kledzik <kledzik@apple.com>
-
- Fix -flat_namespace issue with not all indirect dylibs being processed
-
-2011-04-29 Nick Kledzik <kledzik@apple.com>
-
- Fix sign extention on i386 addends of extern vanilla relocs
-
-2011-04-29 Nick Kledzik <kledzik@apple.com>
-
- Don't let -ObjC double load any archive members
-
-2011-04-29 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9356572> better warning about unaligned ARM functions
-
--------- tagged ld64-125.1
-
-2011-04-28 Nick Kledzik <kledzik@apple.com>
-
- Fix sign extention on arm sect-diff relocs so as to not trip rangeCheckAbsolute32()
-
--------- tagged ld64-125
-
-2011-04-24 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8866673> the entry point should start out initially undefined
-
-2011-04-24 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/5804214> ld should never have a symbol in the non-lazy indirect symbol table with index 0
-
-2011-04-24 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/6978069> ld adds undefined symbol from .exp file to kext bundle
-
-2011-04-24 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/7664544> Linker typo suggestions should ignore l- and L- symbols
-
-2011-04-24 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/7952137> -order_file_statistics warns about syms in multiple .o files even when names in order file are prefixed
-
-2011-04-23 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8642343> warning when a method is overridden in a category in the same link unit
- Add test case: unit-tests/test-cases/objc-category-warning
-
-2011-04-23 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/7890410> don't let function from archive override a tentative definition
- Add test case: unit-tests/test-cases/tentative-and-archive-code
-
-2011-04-23 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8822887> x86_64 -- lossy relocation at static link time (push/mov $imm)
-
-2011-04-23 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8989530> Add comment to error message when __ZTV symbols are undefined
-
-2011-04-23 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8995535> obsolete -no_compact_linkedit
-
-2011-04-23 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9281002> sect->sectname() passed to "%s" formats
-
-2011-04-14 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9275707> linking a sub library of libSystem should not warn about common symbols
-
-2011-04-14 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9282815> support movw/movt in static executables
-
-2011-04-12 Nick Kledzik <kledzik@apple.com>
-
- Rework ARM subtype handling to be table driven
-
-2011-04-11 Nick Kledzik <kledzik@apple.com>
-
- Error if -init or -e function not in image being linked
-
-2011-04-01 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9216420> -static and -stack_addr don't work together
-
-2011-03-31 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9183821> ld assert in LTO mode if libLTO suppresses a weak symbol it should have perserved
-
--------- tagged ld64-124.1
-
-2011-03-30 Nick Kledzik <kledzik@apple.com>
-
- log warning if ld_classic is invoked
-
-2011-03-30 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9209135> Support "-arch arm -force_cpusubtype_ALL" to keep gcc building
-
--------- tagged ld64-124
-
-2011-03-24 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9144456> make libgcc_s and libSystem work for any link order
-
-2011-03-18 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8750693> ld64 should only install to the platform in iOS trains
-
-2011-03-18 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9127471> ld64 should build stand-alone and not need libunwind headers
-
-2011-03-18 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9139782> add LC_VERSION_MIN_IPHONEOS to iOS targets, warn on mismatches
-
-2011-03-18 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8836481> Make iOS simulator a real platform with command line versioning
-
-2011-03-15 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8964869> static executables don't get function start information
-
-2011-03-15 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9112113> allow_sub_type_mismatches linker flag broken
-
-2011-03-15 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9106345> Add option to support merging zero fill sections
- Add test case: unit-tests/test-cases/merge_zero_fill_sections
-
-2011-03-15 Nick Kledzik <kledzik@apple.com>
-
- Improve error message about text-relocs caused by direct access to global weak symbols.
-
-2011-03-10 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9116044> ld assert linking armv7 kext bundle on b/bl to external function
-
--------- tagged ld64-123.10
-
-2011-03-03 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9085618> linking x86_64 causes assert from changes in ld64-123.9
-
--------- tagged ld64-123.9
-
-2011-03-03 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9055754> movw/movt don't work in dyld shared cache
-
-2011-03-03 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8995525> classic linkedit does not match compact for non-lazy pointers
-
-2011-02-24 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/9052679> Support armv7 variants
-
--------- tagged ld64-123.8
-
-2011-02-10 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8926992> Switch arm32 kexts to MH_KEXT_BUNDLE
-
--------- tagged ld64-123.7
-
-2011-02-10 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8926992> Switch arm32 kexts to MH_KEXT_BUNDLE, if LD_KEXT_BUNDLE is set
-
-2011-01-28 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8931747> spurious 'found branch-22 without store' warning
-
--------- tagged ld64-123.6
-
-2011-01-26 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8904405> crash with arm hi16/lo16 to external symbols
-
--------- tagged ld64-123.5
-
-2011-01-24 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8910802> dyld synthesized tail call stubs don't always work
-
--------- tagged ld64-123.4
-
-2011-01-19 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8866345> __text with >10 alignment should disable close-stub optimization
-
-2011-01-18 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8877072> :upper16: / :lower16: not working when targeting thumb functions
-
--------- tagged ld64-123.3
-
-2010-12-14 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8764917> ld64 making shims when not necessary
-
-2010-12-14 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8760268> Add work around for latest llvm-c/lto.h
-
--------- tagged ld64-123.2.1
-
-2011-03-07 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8955206> enable i386 ASLR
-
--------- tagged ld64-123.2
-
-2010-12-10 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8746980> Man page typo: "dysmutil" under object_path_lto
-
-2010-12-10 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8746896> ld64 crashes when warning about re-exported symbol
-
--------- tagged ld64-123.1
-
-2010-12-07 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8732097> assertion if symbol from re-exported dylib is in -exported_symbols_list
-
--------- tagged ld64-123
-
-2010-12-06 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8649182> Change default search order and add -search_dylibs_first to restore old behavior
-
-2010-12-06 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8626058> ld should consistently warn when resolvers are not exported
-
-2010-12-02 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8723307> data segment has file offset 0
-
--------- tagged ld64-122
-
-2010-12-01 Nick Kledzik <kledzik@apple.com>
-
- Add #define ARM_RELOC_HALF in case trying to build with old mach-o/arm/reloc.h header
-
-2010-11-30 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8134559> Linker should synthesize interworking stubs for tail calls
- added test case: unit-tests/test-cases/branch-interworking
-
-2010-11-30 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8708091> Link Time Optimization error with tentative defs and -dead_strip
- added test case: unit-tests/test-cases/lto-dead_strip-tentative
-
--------- tagged ld64-121
-
-2010-11-10 Nick Kledzik <kledzik@apple.com>
-
- Add -dylibs option to dyldinfo tool
-
-2010-11-03 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/7441442> Need support for ARM/thumb upper/lower 16 bit relocation
-
-2010-11-03 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8624334> Spelling typo in linker warning
-
-2010-11-02 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8616906> Xcode 4: ld -r doesn't work on files compiled with llvm-gcc -flto
-
-2010-11-01 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8618747> ld wrongly complaining about tlv relocs for i386
-
-2010-11-01 Nick Kledzik <kledzik@apple.com>
-
- Fix -why_live to list all references, not just first
-
-2010-11-01 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8612861> iOS is missing dof sections for armv7 slice
-
--------- tagged ld64-120.3
-
-2010-11-09 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8644314> revert default search order
-
--------- tagged ld64-120.2
-
-2010-11-09 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8510696> ld -r corrupts multiple non-lazy sections
-
--------- tagged ld64-120.1
-
-2010-10-25 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8612550> When order file used on data, turn ordered zero fill symbols into zero data
-
--------- tagged ld64-120
-
-2010-10-25 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8592564> ld should use this-image ordinal for symbols from re-exported non-public dylibs
- added test case: unit-tests/test-cases/re-export-and-use
-
-2010-10-25 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8284718> Value of N_SO stabs should be address of first atom from translation unit
-
-2010-10-25 Nick Kledzik <kledzik@apple.com>
-
- Always print arch name on undefined symbols error
-
-2010-10-25 Nick Kledzik <kledzik@apple.com>
-
- Add ld64 version number to crash logs
-
-2010-10-22 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/7952947> -objc_abi_version 1 not supported
-
-2010-10-22 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/5591394> Add support to ld64 for N_FUN stabs when used for symbolic constants
-
-2010-10-22 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/3560832> Change default search order and add -search_dylibs_first to restore old behavior
-
-2010-10-22 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/6955037> -L flag should support a space between it and its argument
-
-2010-10-22 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8553647> Hidden resolver functions don't work with DYLD_BIND_AT_LAUNCH
-
-2010-10-22 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8541707> Support resolver functions for function pointer use from same linkage unit
-
-2010-10-19 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8571323> section$start$ does not always point to start of built in section types
-
--------- tagged ld64-119.2
-
-2010-10-18 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8553312> make having an ObjC2 class symbol in an export list be a warning instead of an error
-
-2010-10-15 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8553283> lazily produce (crt1.o missing) error
-
--------- tagged ld64-119.1
-
-2010-10-05 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8527740> ld -r can produce output with LC_DYLD_INFO load command
-
-2010-10-05 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8516692> ld doesn't pass stabs debug info through to the final executable any longer
-
-2010-10-05 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/4251267> __UNIXSTACK placed incorrectly when -stack_addr < 0x4000000
-
-2010-10-05 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8267015> ld mishandles -lazy-l option
- Updated test case: unit-tests/test-cases/lazy-dylib
-
-2010-10-04 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8468240> -no_compact_unwind should suppress dwarf->CUE warnings
-
--------- tagged ld64-119
-
-2010-10-01 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/6599016> use ld64 to link iBoot
-
-2010-10-01 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8504770> crash when entrypoint is thumb
-
-2010-10-01 Nick Kledzik <kledzik@apple.com>
-
- If -ios_version_min is used with -arch i386, assume simulator
-
-2010-10-01 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8500061> crash with multiple re-exported dylibs with same install_name
-
-2010-09-28 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8032130> Linker complains about resolver functions when architecture is inferred.
-
-2010-09-28 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/6751424> ARM subtype not set on LTO programs
-
-2010-09-28 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8481987> Link-Time Optimization assertion
- Added test cases:
- unit-tests/test-cases/lto-dead_strip-objc
- unit-tests/test-cases/lto-dead_strip-some-hidden
-
-2010-09-24 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8441087> Support -dyld_env NAME=value
-
-2010-09-23 Nick Kledzik <kledzik@apple.com>
-
- Previous BranchIsland code changes to make buildable with clang++ were bad. Fix.
-
-2010-09-23 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8274443> ld64 objc category merging asserts on category from old framework
-
-2010-09-23 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8469063> ASLR Sidebuild: Many Projects Fail checksyms_read_only_relocs Verifier
-
-2010-09-22 Nick Kledzik <kledzik@apple.com>
-
- Fix DOF section name bug
-
-2010-09-22 Nick Kledzik <kledzik@apple.com>
-
- Fixes to build with clang++
-
-2010-09-21 Nick Kledzik <kledzik@apple.com>
-
- In Resolver::fillInHelpersInInternalState(), dyld never needs stubs
-
-2010-09-21 Ivan Krstic <ike@apple.com>
-
- <rdar://problem/8457083> ld: support non-executable heap Mach-O header flag
-
-2010-09-21 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8175282> Xcode 4 linker fails with "address not in any section"
-
-2010-09-20 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8198832> assertion failure reading i386 yasm .o (not using scattered reloc)
-
-2010-09-20 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8430751> ld crashes when parsing dwarf and all code is not in __text
-
-2010-09-17 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8396450> magic segment symbol names don't work with preload executables
- Update test case: unit-tests/test-cases/segment-labels
-
-2010-09-17 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8445985> OSO in DebugNotes for LTO should point to generated mach-o not, bitcode .o file
-
-2010-09-16 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8413931> FUN in debug map not rebased
- Update test case: unit-tests/test-cases/rebase-basic
-
-2010-09-16 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8427133> Relocation failure with i386 32-bit diff to stub
-
-2010-09-16 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8437287> assert when targeting 10.5 and crt1.o/dylib1.o is not supplied
-
--------- tagged ld64-118.1
-
-2010-09-15 Nick Kledzik <kledzik@apple.com>
-
- Fix missing rebase commands that broke perl
-
-2010-09-15 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8252819> assert when .objc_class_name_* symbol missing
- Add test case: unit-tests/test-cases/archive-ObjC-unexported
-
-2010-09-13 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8419850> linker does not honor $ld$hide for umbrella libraries
- Added test case: unit-tests/test-cases/symbol-hiding-umbrella
-
-2010-09-09 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/6723729> LC_UUID uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats
-
-2010-09-09 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/4942948> support -bind_at_load
-
-2010-09-07 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/7989734> ld mis-handling std::tr1::anonymous symbols
- Remove support for ordering gcc-4.0 compiled anonymous namespace symbols
-
-
--------- tagged ld64-118
-
-2010-09-02 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8308697> -preload should not have LINKEDIT segment
- Added test case: unit-tests/test-cases/efi-basic
-
-2010-09-02 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8389578> trivial Objective-C app fails when using libLTO
- Added test case: unit-tests/test-cases/lto-objc-image-info
-
-2010-09-02 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8384727> Add -reexport_symbols_list option to re-export certain symbols.
- Added test case: unit-tests/test-cases/reexport_symbols_list
-
-2010-09-01 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/7438246> LTO with 'dead code strip' can't ignore unused functions with undefined references
- Add test case: unit-tests/test-cases/lto-dead_strip-unused
-
-2010-09-01 Nick Kledzik <kledzik@apple.com>
-
- Warn if unaligned ARM code is detected
-
-2010-09-01 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8383175> Mach-O linked by current linker don't load in VIrtualBox any more
-
-2010-09-01 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8304984> Linker should pick default visibility instead of warning about conflicts
- Updated test case: unit-tests/test-cases/visibility-warning
-
-2010-09-01 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8249338> Enable new load commands
-
-2010-09-01 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8368679> Do not pass -demangle to ld_classic
-
-2010-09-01 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8336910> iOS 4.3 armv7 should be PIE by default
- <rdar://problem/5472933> better error message for direct access to external globals when not linking read_only_relocs
- <rdar://problem/7927510> linker does not error on direct (static) data references to a dylib symbol
-
--------- tagged ld64-117.11
-
-2010-09-03 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8385011> mask thumb bit off non lazy pointers content when parsing arm .o files
-
--------- tagged ld64-117.10
-
-2010-08-26 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8358084> 8F64 kills installtest devices
- Don't clear thumb bit on pointers inside thumb functions if addend is negative
-
--------- tagged ld64-117.9
-
-2010-08-25 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8345038> no more audio because of broken thunk
- Update of thumb22 b.w instruction was not clearing bits before or'ing in new ones
-
--------- tagged ld64-117.8
-
-2010-08-25 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8342028> prefetch abort in kernel mode: fault_addr=0xe58024e4
- Don't set thumb bit on .long pointers in thumb functions that point to some offset in same function
-
--------- tagged ld64-117.7
-
-2010-08-24 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8346444> dyld-179.4 fails to link, assert in setLoadCommandsPadding()
- Fix linker to always put __text first before other code-only sections
-
--------- tagged ld64-117.6
-
-2010-08-23 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8339702> ld no longer output static archive dependencies for dyld for B&I
- Add test case unit-tests/test-cases/dependency-logging
-
--------- tagged ld64-117.5
-
-2010-08-20 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8309595> SWB: ld64-117.1 on 8F54: Assertion failed:
- UTF16 CFStrings were not coalesced correctly when gcc built the .o files and the
- last string in the __ustring section only had a single zero byte at the end.
-
--------- tagged ld64-117.4
-
-2010-08-19 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8326544> DTSJ1J105: SpringBoard crashes on boot
- Fix order file to move aliases even when subsections_via_symbols it used
- Update test case unit-tests/test-cases/order_file
-
-2010-08-17 Nick Kledzik <kledzik@apple.com>
-
- Fix resolver functions to survive ld -r.
- Warn if resolver functions are made non-external.
-
-2010-08-17 Nick Kledzik <kledzik@apple.com>
-
- Make it an error for resolver functions to be used in anything but a dylib.
-
--------- tagged ld64-117.3
-
-2010-08-17 Nick Kledzik <kledzik@apple.com>
-
- Fix thumb resolver functions
- Enable updward dylibs and symbol re-exports for iOS 4.2
-
-2010-08-16 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8308697> Latest ld no longer supports EFI -preload
- Rearrange LINKEDIT chunks in -preload mode
-
--------- tagged ld64-117.2
-
-2010-08-14 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8308697> Latest ld no longer supports EFI -preload
- Add LC_UNIXTHREAD to -preload
-
-2010-08-14 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8309530> SWB: ld64-117.1 on 8F54: Assertion failed: (categoryAtom->size() == Category<A>::size())
- gcc-4.0 uses 'L' labels on categories. This merges them onto previous data and disable category optimzation
-
-2010-08-14 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8309917> SWB: ld64-117.1 on 8F54: bad category optimization
- Disable category optimization for i386 and arm until further testing
-
-2010-08-14 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8309608> SWB: ld64-117.1 on 8F54: address not in any section
- Handle pointer diff to stub for weak hidden function
-
-2010-08-13 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8308697> Latest ld no longer supports EFI -preload
-
--------- tagged ld64-117.1
-
-2010-08-11 Nick Kledzik <kledzik@apple.com>
-
- Make missing exported symbols a warning to help adoption of new linker
-
-2010-08-11 Nick Kledzik <kledzik@apple.com>
-
- Add ExternalRelocationsAtom<>::pointerReloc() to more easily support kext bundles
-
-2010-08-09 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8210380> SWB: ld64-116.2 fix branch to label-4
-
-2010-08-09 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8260073> Error with empty non_lazy_symbol_pointers section
-
-2010-08-06 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/7977374> Add command line options to control symbol weak-def-bit on exported symbols
-
--------- tagged ld64-117
-
-2010-07-28 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8243431> split seg info wrong for x86_64 stub helpers
-
-2010-07-26 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8237436> __nlcatlist categories should not be optimized
-
-2010-07-23 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8179273> ld64 assertion on object file
-
-2010-07-21 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/7435296> Reorder sections to reduce page faults in object files
-
-2010-06-30 Nick Kledzik <kledzik@apple.com>
-
- Support resolver functions in iOS dylibs
-
--------- tagged ld64-116.2
-
-2010-06-30 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8138287> C programs get objc GCness from dylibs
- Update: unit-tests/test-cases/objc-gc-checks
-
--------- tagged ld64-116.1
-
-2010-06-22 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8111013> address range check should not apply to preload executables
-
-2010-06-22 Nick Kledzik <kledzik@apple.com>
-
- Warn instead of error when CPU_SUBTYPE_ARM_ALL .o files used.
-
-2010-06-22 Nick Kledzik <kledzik@apple.com>
-
- Fix assert in objc category optimzation. Metaclass also has copy of propery list to update.
-
-2010-06-22 Nick Kledzik <kledzik@apple.com>
-
- Fix crash in -r mode with -alias.
-
--------- tagged ld64-116
-
-2010-06-21 Nick Kledzik <kledzik@apple.com>
-
- Add support for -ios_version_min as an alias for -iphoneos_version_min
-
-2010-06-21 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/7687304> linker could merge method lists from class and its categories
- Added test case: unit-tests/test-cases/objc-category-optimize
- Added option: -no_objc_category_merging to disable
-
-2010-06-21 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8113877> i386 TLV PIC reloc content is negated
-
-2010-06-15 Nick Kledzik <kledzik@apple.com>
-
- Added better error messages and asserts for bad thread local object files
-
-2010-06-09 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8076986> 'rebase' makes timestamps invalid/unreadable for GDB
-
-2010-06-09 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/7966333> executable has no debug symbols when compiled with LTO
- Added test case: unit-tests/test-cases/lto-object_path
-
-2010-06-09 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/7702923> stop promoting hidden referenced dynamically symbols to global
- Updated test case: unit-tests/test-cases/main-stripped
-
-2010-06-04 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/6144674> ER: individual symbol re-exports
- Added test case: unit-tests/test-cases/re-export-symbo
-
-2010-06-03 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/7474224> add functions start info to LINKEDIT
- * Support added but not on by default. Use -function_starts to enable.
- * Added test case: unit-tests/test-cases/function-start
-
-2010-06-02 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/5674241> ER: add load command for min OS version
- * Support added but not on by default. Use -version_load_command to enable.
-
-2010-06-02 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/8040089> provide better undefined symbol error message
-
-2010-05-28 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7780438> ld should also merge file attributes from lazy loaded archives
- * Move attribute gathering from InputFiles to Resolver
-
-2010-05-28 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/8038333> SWB: ld64-115.3: dylib on kext link line causes malformed kext
- * allow -static after -kext on command line
-
--------- tagged ld64-115.3
-
-2010-05-26 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/8024702> strip of .o files removes __objc_imageinfo section
- * Added test case: unit-tests/test-cases/dwarf-strip-objc
-
-2010-05-25 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/8023624> crash when parsing local vanilla reloc to weak def
-
--------- tagged ld64-115.2
-
-2010-05-21 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/8012536> switch back to using ld_classic for -static arm code
-
-
-2010-05-21 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/8012549> warn instead of error when seg1addr is out of range for ARM
-
-
-2010-05-21 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/8012526> fix -undefined dynamic_lookup -nodefaults to not error about missing dyld_stub_binder
-
--------- tagged ld64-115.1
-
-2010-05-19 Nick Kledzik <kledzik@apple.com>
-
- * Fix trie nodes for resolver functions to have second address be stub not helper
-
-2010-05-19 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7996423> work around for old checksyms tools
- * Make i386 stub section named "__symbol_stub" instead of "__stubs"
-
-2010-05-10 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7960396> linking with LTO prints "/tmp/lto.o"
-
--------- tagged ld64-115
-
-2010-05-06 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7892392> linker loses x86_64 addend to 'L' symbols
- * properly handle addend to 'L' symbols that are ignored
-
-2010-05-05 Nick Kledzik <kledzik@apple.com>
-
- * rework min OS version parsing to enable the linker to handle unknown OS versions
-
-
-2010-05-05 Nick Kledzik <kledzik@apple.com>
-
- * Implement magic section$start$xxx$yyyy and section$end$xxx$yyyy symbols
- * Implement magic segment$start$xxx and segment$end$xxx symbols
- * Add test case: unit-tests/test-cases/segment-labels
-
-
-2010-05-03 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7173071> implement optional demangling in linker
- * Add option: -demangle
- * Add test case: unit-tests/test-cases/demangle
-
-
-2010-05-03 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7931759> ld64 doesn't grok the modern-objc-ABI-on-i386
- * Add support for -objc_abi_version command line option
- * Added test case: unit-tests/test-cases/objc-abi
-
-
-2010-05-03 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7929974> -alias does not work with __OBJC sections
- * sort contents of sections with aliases
- * Added test case: unit-tests/test-cases/objc-class-alias
-
-
-2010-04-28 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/4966836> Feature: Thread local storage
- * Add test case: unit-tests/test-cases/tlv-basic
- * Add test case: unit-tests/test-cases/tlv-dylib
-
-
-2010-04-27 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/4383612> Accelerate needs way to dispatch based on instruction execution time characteristics.
- * Add support for "symbol resolver" functions
- * Add test case unit-tests/test-cases/symbol-resolver-basic
-
-
-2010-04-26 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7711820> range check fat archives
- * check that fat file slice being used does not extend beyond end of file
- * check that member being used does not extend beyond end of slice/file
-
-2010-04-26 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7666015> The documentation for the -allowable_client option doesn't say enough about it
-
-2010-04-26 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7854068> back out LD_NO_PIE
-
-2010-04-22 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7831043> More ICU make check failures with 0-terminated UTF16 strings
- * Change UTF16StringSection to break into atoms just on label boundaries
- * Added test case: unit-tests/test-cases/utf16-nul
-
-
--------- tagged ld64-114.12
-
-2010-04-14 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7811357> Crash with messed up BNSYM
-
-
-2010-04-07 Nick Kledzik <kledzik@apple.com>
-
- * Fix crash with blank dylib stubs
-
--------- tagged ld64-114.11
-
-2010-04-07 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7798495> for ppc, add split-seg info for TEXT pointers to DATA
-
-
-2010-04-07 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7831379> Cannot build ppc64 target with ObjC code
-
-
-2010-04-01 Nick Kledzik <kledzik@apple.com>
-
- * let .exp files override auto-hide so that they can be exported if needed
-
-
-2010-04-01 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/6783167> support auto hidden weak symbols: .weak_def_can_be_hidden
- * added test case: unit-tests/test-cases/weak-def-auto-hide
-
-
-2010-04-01 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7818475> 'l' symbols not being automatically removed
-
-
-2010-03-31 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7808258> weak defs should not cause indirection in static executables
- * Update test case: unit-tests/test-cases/static-executable-weak-defines
-
--------- tagged ld64-114.10
-
-2010-03-31 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7735120> assert with .o file with two LSDA sections
-
-
--------- tagged ld64-114.9
-
-2010-03-30 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7791161> L4 locks up starting a second processor, works fine with old linker
- * properly get addend from content in x86_64 substractor when target is direct
-
-
-2010-03-29 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7805172> ld should excludes debug notes when computing UUID
- * added test case: unit-tests/test-cases/dwarf-debug-notes-uuid
-
--------- tagged ld64-114.8
-
-2010-03-26 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7788474> __objc_catlist section loses don't dead strip bit in ld -r mode
- * Update test case unit-tests/test-cases/static-executable
-
--------- tagged ld64-114.7
-
-2010-03-25 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7796313> Support LD_NO_PIE again
-
-2010-03-25 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7765885> Rosetta crashes on launch in 11A133a
- * Fix -segaddr __TEXT to cause other floating segments to be contiguous with TEXT
-
-2010-03-24 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7786326> Page Zero segment seems to be getting dead stripped
-
-2010-03-24 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7785574> kernel sdt dtrace probes not visible
-
--------- tagged ld64-114.6
-
-2010-03-23 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7783918> new linker makes dylibs with no __text section, causing codesign_allocate tool to fail
- * make sure there is always a __text section in dylibs and bundles
-
--------- tagged ld64-114.5
-
-2010-03-22 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7767311> missing __objc_imageinfo section
- * Real fix will be in 7780438. For now have Resolver also accumulate objc constraint info
-
-
-2010-03-19 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7772740> ld64-114 does not error on missing exported symbols with -dead_strip
-
--------- tagged ld64-114.4
-
-2010-03-16 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7762146> dyld missing LC_ID_DYLINKER
-
--------- tagged ld64-114.3
-
-2010-03-15 Nick Kledzik <kledzik@apple.com>
-
- * force i386 kexts to be built with ld_classic
- * preserve 'l' labels in static executables
- * sync section offsets and addresses in segments with command line addresses
-
-
--------- tagged ld64-114.2
-
-2010-03-13 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7751930> ld64-114 generates x86_64 kext external call sites with incorrect addend
-
--------- tagged ld64-114.1
-
-2010-03-12 Nick Kledzik <kledzik@apple.com>
-
- * Fix dyldinfo tool to correct show ordinal info for classic linkedit
-
-2010-03-12 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/7748047> ld64-114 is causing read_only_reloc verification errors for ppc
- * Update machochecker to check this.
-
--------- tagged ld64-114
-
-2010-03-11 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7744174> i386 dylibs built with ld64-112 cause runtime errors when incorporated into the dyld shared cache
- * Add -shared_region option to dyldinfo tool
-
--------- tagged ld64-113
-
-2010-03-11 Nick Kledzik <kledzik@apple.com>
-
- * Allow CPU_SUBTYPE_ARM_ALL .o files to be linked into any arm arch linkage
-
-2010-03-11 Nick Kledzik <kledzik@apple.com>
-
- <rdar://problem/7741238> ld64-112 with -undefined dynamic_lookup marks all symbols as being flat
- * update test case at: unit-tests/test-cases/undefined-dynamic-lookup
-
-2010-03-10 Nick Kledzik <kledzik@apple.com>
-
- * prevent possible crash in warning about can't export hidden symbols
-
-2010-03-10 Nick Kledzik <kledzik@apple.com>
-
- * make sure split-info data is zero terminated
-
--------- tagged ld64-112
-
-2010-03-09 Nick Kledzik <kledzik@apple.com>
-
- * Never dead strip sections added with -sectcreate
- * Added test case: unit-tests/test-cases/sectcreate-dead_strip
-
-
--------- tagged ld64-111
-
-2010-03-03 Nick Kledzik <kledzik@apple.com>
-
- * Add support for "-arch arm -force_cpusubtype_ALL" to keep gcc building
-
-2010-03-02 Nick Kledzik <kledzik@apple.com>
-
- * Add some checking to the use of upward dylibs
-
--------- tagged ld64-110
-
-2010-03-01 Nick Kledzik <kledzik@apple.com>
-
- * Don't coalesce cstrings across segments
-
-2010-03-01 Nick Kledzik <kledzik@apple.com>
-
- * Emulate previous linker bug where hidden symbols with dynamically-referenced
- bit were promoted to global.
- * Added test case: unit-tests/test-cases/unstrippable-symbols
-
--------- tagged ld64-109.1
-
-2010-02-26 Nick Kledzik <kledzik@apple.com>
-
- * Make sure building dyld results in a thread load command
-
--------- tagged ld64-109
-
-2010-02-26 Nick Kledzik <kledzik@apple.com>
-
- * Better sorting of zero-fill sections to preserve discovery order
- * Zero out file offsets in dynamic symbol table entries that are not used
-
-2010-02-26 Nick Kledzik <kledzik@apple.com>
-
- * Support pointer-diffs to zero sized atom in zero sized section
-
-2010-02-25 Nick Kledzik <kledzik@apple.com>
-
- * Add support for -r mode with ppc64
-
-2010-02-25 Nick Kledzik <kledzik@apple.com>
-
- * Handle multiple labels on the same coalesable literal by making an
- atom for each label, each with the same content.
- * Add test case: unit-tests/test-cases/literals-labels
-
-
-2010-02-25 Nick Kledzik <kledzik@apple.com>
-
- * Handle old ppc .o files that have stubs to static functions
-
-2010-02-25 Nick Kledzik <kledzik@apple.com>
-
- * Add basic ppc64 support
-
-2010-02-24 Nick Kledzik <kledzik@apple.com>
-
- * Range check TOC entries in archives
-
-
-2010-02-23 Nick Kledzik <kledzik@apple.com>
-
- * Fix spurious dylib re-export warnings that are just regular linkage cycles
-
-
-2010-02-23 Nick Kledzik <kledzik@apple.com>
-
- * re-partition bits in mach_o::relocatable::Atom ivars to allow more functions per file
-
-
-2010-02-22 Nick Kledzik <kledzik@apple.com>
-
- * re-partition bits in mach_o::relocatable::Atom ivars to allow more fixups per function
-
-
-2010-02-22 Nick Kledzik <kledzik@apple.com>
-
- * Handle re-exported dylibs that are re-exported again
- * Added test case: unit-tests/test-cases/re-export-layers
-
-
-2010-02-22 Nick Kledzik <kledzik@apple.com>
-
- * Properly handle X86_64_RELOC_SUBTRACTOR with non-external target symbol
-
-
--------- tagged ld64-108
-
-2010-02-17 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7658740> ER: Support upward dylib dependencies
- * Add test case: unit-tests/test-cases/dylib-upward
-
-
-2010-02-17 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7401775> ld(1) man page typo
- * <rdar://problem/7230340> Linker (ld) man page typo: "unredable" in -pagezero_size option description
- * <rdar://problem/7207483> Typo in ld(1) man page, "-x" option
- * <rdar://problem/6239264> man ld: Change "if" -> "is"
- * <rdar://problem/7393216> DOC: ld(1) mentions -dynamiclib when it means -dylib
-
-
-2010-02-17 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7625461> Wordsmith ld warning about missing directories
-
-
-2010-02-17 Nick Kledzik <kledzik@apple.com>
-
- * Fix -umbrella to work when umbrella is a dylib instead of a framework
- * Add test case: unit-tests/test-cases/umbrella-dylib
-
-
--------- tagged ld64-107
-
-2010-02-16 Nick Kledzik <kledzik@apple.com>
-
- * Fix bugs with -preload
-
-
-2010-02-16 Nick Kledzik <kledzik@apple.com>
-
- * Fix dylib re-export cylce detection
-
-
-2010-02-16 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/6018216> -ObjC not pulling in members with categories only
- * scan for non-zero __objc_catlist section in archive members when -ObjC is used
- * Added test case: unit-tests/test-cases/objc-category-archive
-
-
-2010-02-15 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7398610> ld glibly removes /dev/null
-
-
-2010-02-15 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7196255> Linker should be able to validate GC intentions
- * Add -objc_gc and -objc_gc_only. Error when used and RR based .o file is linked in
- * Update test case: unit-tests/test-cases/objc-gc-checks
-
-
-2010-02-15 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7421695> Linker should provide a way to mark binaries that support compaction
- * Added -objc_gc_compaction option
- * Update test case: unit-tests/test-cases/objc-gc-checks
-
-
-2010-02-15 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7546367> ER: Need a way to detect weak exports in dev tools
- * implement -warn_weak_exports
-
-2010-02-15 Nick Kledzik <kledzik@apple.com>
-
- * Add support for LD_DYLD_PATH
-
-
-2010-02-15 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7636072> cfstring backing store points to global cstring
- * Force all by-name references in cfstring to be direct references
- * add test case: unit-tests/test-cases/cfstring-and-cstring
-
-
--------- tagged ld64-106
-
-2010-02-12 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7644673> Assertion failed: when class is translation unit scoped
- * added test case unit-tests/test-cases/objc-visibility
-
-
-2010-02-12 Nick Kledzik <kledzik@apple.com>
-
- * <rdar://problem/7644828> crash with missing crt?
-
-
-2010-02-12 Nick Kledzik <kledzik@apple.com>
-
- * Suppress indirect symbol table in static executables
-
-
-2010-02-12 Nick Kledzik <kledzik@apple.com>
-
- * Rework CIE parsing to work with icc generated code
-
-
-2010-02-11 Nick Kledzik <kledzik@apple.com>
-
- * Fix creation of debug notes
- * Tweak unit-tests/test-cases/dwarf-debug-notes to match llvm symbol layout
-
-
-2010-02-11 Nick Kledzik <kledzik@apple.com>
-
- * Don't assert when infering ppc subtype that is ALL.
- * Fix spurious warning about mismatched subtypes, when subtype is inferred
-
-
--------- tagged ld64-105
-
-2010-02-11 Nick Kledzik <kledzik@apple.com>
-
- * Use symbolic constants for bit field sizes
-
-
-2010-02-10 Nick Kledzik <kledzik@apple.com>
-
- * Handle out of order sections in .o files
-
-
--------- tagged ld64-104
-
-2010-02-10 Nick Kledzik <kledzik@apple.com>
-
- * Rename __tentative internal section name to __comm/tent to sort like __common
- * Fix test case in unit-tests/test-cases/tentative-to-real-r
-
-
-2010-02-10 Nick Kledzik <kledzik@apple.com>
-
- * Better warning messages about mismatched architectures
-
-
-2010-02-10 Nick Kledzik <kledzik@apple.com>
-
- * Gracefully ignore if there are >8000 line info per function in debug info
-
-
-2010-02-10 Nick Kledzik <kledzik@apple.com>
-
- * Properly handle when regular definition is weak_imported
- * Add unit-tests/test-cases/weak_import-local
-
-
-2010-02-10 Nick Kledzik <kledzik@apple.com>
-
- * Don't try to coalesce zero length cstrings in mach-o parser.
-
-
-2010-02-10 Nick Kledzik <kledzik@apple.com>
-
- * Add work around for llvm using L labels for backing string of CFString with -fwritable-strings
-
-
-2010-02-09 Nick Kledzik <kledzik@apple.com>
-
- * Ignore labels in __gcc_except_tab section
- * Properly apply local relocations to __eh_frame section so CFI parser works
- * Update unit-tests/test-cases/eh-stripped-symbols to reproduce problem
-
-
-2010-02-09 Nick Kledzik <kledzik@apple.com>
-
- * Fix file offset computation with large zero-fill sections
-
-
-2010-02-09 Nick Kledzik <kledzik@apple.com>
-
- * Force global 'l' to be hidden
- * Add test case with objc properties: unit-tests/test-cases/objc-properties
-
-
--------- tagged ld64-103
-
-2010-02-04 Nick Kledzik <kledzik@apple.com>
-
- * Temporarily change assert() to call exit(1) instead of abort()
-
-
-2010-02-04 Nick Kledzik <kledzik@apple.com>
-
- * Fix another case in -r mode where the vmsize was less that filesize
-
-
-2010-02-04 Nick Kledzik <kledzik@apple.com>
-
- * Fix assert when generating GSYM stab debug notes
-
-
-2010-02-04 Nick Kledzik <kledzik@apple.com>
-
- * Add SRCROOT to crash logs
-
-
-2010-02-04 Nick Kledzik <kledzik@apple.com>
-
- * Remove architectureName() from InputFiles
-
-
--------- tagged ld64-102
-
-2010-02-03 Nick Kledzik <kledzik@apple.com>
-
- * Add follow-on reference from symbol text atom to non-symboled text atom
-
-
--------- tagged ld64-101
-
-2010-01-29 Nick Kledzik <kledzik@apple.com>
-
- * fix -alias symbols to be global by default
-
-
--------- tagged ld64-100
-
-2010-01-28 Nick Kledzik <kledzik@apple.com>
-
- * Merge new/refactored linker to trunk
-
--- /dev/null
+#!/bin/csh
+
+# Attempt to find the architecture.
+# First look through the command line args.
+set arch=unknown
+set link_cmd=(`cat link_command`)
+while ( $#link_cmd > 0 )
+ if ( "$link_cmd[1]" == "-arch" ) then
+ set arch=$link_cmd[2]
+ endif
+ shift link_cmd
+end
+
+# look for an explicit arch file
+if ( "$arch" == "unknown" ) then
+ if ( -e arch ) then
+ set arch=`cat arch`
+ endif
+endif
+
+if ( "$arch" == "unknown" ) then
+echo "***** Unable to determine architecture."
+exit 1
+endif
+
+# Create .dylibs for each file in the dylib_stubs directory.
+if ( -e dylib_stubs ) then
+ set files=`cd dylib_stubs ; echo *`
+ mkdir -p dylibs
+ foreach file ($files)
+ if ( ! -e dylibs/$file ) then
+ clang -arch $arch -c -fno-builtin -o tmp_object.o -x c dylib_stubs/$file
+ ld -arch $arch -dylib -macosx_version_min 10.1 -no_version_load_command -o dylibs/$file tmp_object.o
+ endif
+ end
+endif
+
+# Create .frameworks for each file in the framework_stubs directory.
+if ( -e framework_stubs ) then
+ set files=`cd framework_stubs ; echo *`
+ foreach file ($files)
+ if ( ! -e frameworks/$file.framework ) then
+ clang -arch $arch -c -fno-builtin -o tmp_object.o -x c framework_stubs/$file
+ mkdir -p frameworks/$file.framework
+ ld -arch $arch -dylib -macosx_version_min 10.1 -no_version_load_command -o frameworks/$file.framework/$file tmp_object.o
+ endif
+ end
+endif
+
+# Clean up.
+rm -f tmp_object.o
--- /dev/null
+<html>
+<head>
+ <title>Linker</title>
+</head>
+<body>
+
+
+<h1>
+ Inside the Linker
+</h1>
+<div class="doc_author">
+ <p>Written by <a href="mailto:kledzik@apple.com">Nick Kledzik</a></p>
+</div>
+
+
+<h2>
+ <a name="introduction">Introduction</a>
+</h2>
+
+<p>The Darwin linker is a new generation of linker. It is not "section" based
+like traditional linkers which mostly just interlace sections from multiple
+object files into the output file. The Darwin linker is based on "Atoms".
+Traditional section based linking work well for simple linking, but their model
+makes advanced linking features difficult to implement. Features like dead code
+stripping, reordering functions for locality, and C++ coalescing require the
+linker to work at a finer grain.
+</p>
+
+<p>An atom is an indivisible chunk of code or data. An atom has a set of
+attributes, such as: name, scope, content-type, alignment, etc. An atom also
+has a list of Fixups. A Fixup contains: a kind, an optional offset, an optional
+addend, and an optional target atom.</p>
+
+<p>The Atom model allows the linker to use standard graph theory models for
+linking data structures. Each atom is a node, and each Fixup is an edge.
+The feature of dead code stripping is implemented by following edges to mark
+all live atoms, and then delete the non-live atoms.</p>
+<br>
+<h2>
+ <a name="Atom model">Atom model</a>
+</h2>
+
+<p>An atom is an indivisible chuck of code or data. Typically each user
+written function or global variable is an atom. In addition, the compiler may
+emit other atoms, such as for literal c-strings or floating point constants, or
+for runtime data structures like dwarf unwind info or pointers to initializers.
+</p>
+
+<p>A simple "hello world" object file would be modeled like this:</p>
+<img src="hello.png" alt="hello world graphic"/>
+<p>There are two atoms: main and an anonymous atom containing the c-string
+literal "hello world". The Atom "main" has two fixups. One is the call site
+for the call to printf, and the other is a fixup for the instruction that loads
+the address of the c-string literal. </p>
+
+<br>
+<h2>
+ <a name="File model">File model</a>
+</h2>
+
+<p>The linker views the input files as basically containers of Atoms and Fixups,
+ and just a few attributes of their own. The linker works with three kinds
+of files: object files, static libraries, and dynamic libraries. Each kind
+of file has reader object which presents the file in the model expected by
+the linker.</p>
+<h4> <a>Object File</a>
+</h4>
+An object file is just a container of atoms. When linking with
+an object file, all atoms are added to the initial graph of atoms.
+
+<h4> <a>Static Library (Archive)</a>
+</h4>
+This is the traditional unix static archive which is just a collection of
+object files with a "table of contents". When linking with a static library,
+by default nothing is added to the initial graph of atoms. Instead, if there
+are unresolved references (dangling edges) in the master graph of all atoms,
+and the table of contents for a static library says that one of the object files
+in the library defines one of the missing symbols (dangling edge),
+the set of atoms from the specified object file in the static library is added
+to the master graph of atoms.
+
+<h4> <a>Dynamic Library (Shared Object)</a>
+</h4>
+Dynamic libraries are unique in that the don't directly add add any atoms.
+Their purpose is to check at build time that all references are resolved and
+provide a list of dynamic libraries (SO_NEEDED) that will be needed at runtime.
+The way this is modeled in the linker is that a dynamic library contributes
+no atoms to the initial graph of atoms. Instead, (like static libraries) if
+there are unresolved references (dangling edges) in the master graph of all atoms,
+if a dynamic library exports a required symbol, then a "proxy" atom is
+instantiated by the linker. The proxy atom allows the master atom graph to have
+all edges resolved and also records from which dynamic library a symbol came.</p>
+
+<br>
+<h2>
+ <a name="Linking Steps">Linking Steps</a>
+</h2>
+<p>Through the use of abstract Atoms, the core of linking is architecture
+independent and file format independent. All command line parsing is factored
+out into a separate "options" abstraction which enables the linker to be driven
+with different command line sets.</p>
+<p>The overall steps in linking are:<p>
+<ol>
+ <li>Command line processing</li>
+ <li>Parsing input files</li>
+ <li>Resolving</li>
+ <li>Passes/Optimizations</li>
+ <li>Generate output file</li>
+</ol>
+
+<p>The Resolving and Passes steps are done purely on the master graph of atoms,
+so they have no notion of file formats such as mach-o or ELF.</p>
+
+<h4> <a>Resolving</a>
+</h4>
+<p>The resolving step takes all the atoms graphs from each object file and
+combines them into one master object graph. Unfortunately, it is not as simple
+as appending the atom list from each file into one big list. There are many
+cases where atoms need to be coalesced. That is, two or more atoms need to
+be coalesced into one atom. This is necessary to support: C language
+ "tentative definitions", C++ weak symbols for templates and inlines defined
+in headers, and for merging copies of constants like c-strings and floating
+point constants.</p>
+
+<p>The linker support coalescing by-name and by-content. By-name is used for
+tentative definitions and weak symbols. By-content is used for constant data
+that can be merged. </p>
+
+<p>When one atom has a reference (FixUp) to another atom, there is also a binding
+type: by-name, direct, or indirect. A Fixup contains a tagged union that if
+the binding type is by-name, the union field is a pointer to a c-string. If
+the binding type is direct, the union is a pointer to an Atom. If the binding
+type is indirect, the union is a index into a table of pointers to Atoms. Below
+is a graphical representation of the binding types:</p>
+<img src="bindings.png" alt="binding types graphic"/>
+
+<p>Input file Atoms contain only direct and by-name references. Direct
+references are used for atoms defined in the same object file for which the
+target atom is either unnamed or cannot change. For instance, calling
+a static function in a translation unit will result in a direct reference
+to the static functions's atom. Also the FDE (dwarf unwind info) for a function
+has a direct reference to its function. On the other hand references to
+global symbols (e.g. call to printf) use by-name binding in object files.
+</p>
+
+<p>The resolving process maintains some global linking "state", including:
+a "symbol table" which is a map from c-string to Atom*, an indirect symbol
+table which is a growable array of Atom*, and for each kind of coalesable
+constants there is a content to Atom* map. With these data structures,
+the linker walks all atoms in all input files. For each
+atom, it checks if the atom should be in one symbol table or one of the
+coalescing tables. If so, it attempts to add the atom. If there already is
+a matching atom in that table, that means the current atom needs to be
+coalesced with the found atom.
+</p>
+
+<p>To support coalescing, all references to coalesable atoms are changed to
+indirect binding and an entry is added to the indirect table which points
+to the current chosen atom. When all input atoms have been processed by
+the resolver, there should be only direct and indirect bindings left. If
+there are any NULL entries in the indirect table, that means there are
+undefined references. The linker then looks to the supplied libraries (both
+static and dynamic) to resolve those references.
+</p>
+
+<p>Dead code stripping (if requested) is done at the end of resolving. The
+linker does a simple mark-and-sweep. It starts with "root" atoms (like "main"
+in a main executable) and follows each references and marks each Atom that
+it visits as "live". When done, all atoms not marked "live" are removed.
+</p>
+
+<h4> <a>Passes</a>
+</h4>
+<p>The Passes step
+is an open ended set of routines that each get a change to modify or enhance
+the master graph of atoms. Passes are only run if the master graph of
+atoms is completely resolved (no dangling edges).
+The current set of Passes in the Darwin linker are:</p>
+<ul>
+ <li>Objective-C optimizations (Apple)</li>
+ <li>stub (PLT) generation</li>
+ <li>GOT instantiation</li>
+ <li>TLV instantiation (Apple)</li>
+ <li>order_file optimization</li>
+ <li>branch island generation</li>
+ <li>branch shim generation</li>
+ <li>dtrace probe processing (Apple)</li>
+ <li>compact unwind encoding (Apple)</li>
+</ul>
+<p>Some of these passes are specific to Apple's runtime environments. But many
+of the passes are applicable to any OS (such as generating branch island for
+out of range branch instructions).</p>
+
+<p>The general structure of a pass is to walk the master graph inspecting each
+atom and doing something. For instance, the stub pass, walks the graph looking
+for atoms with call sites to proxy atoms (e.g. call to printf). It then
+instantiates a "stub" atom (PLT entry) and a "lazy pointer" atom for each
+proxy atom needed, and these new atoms are added to the master graph. Next
+all the noted call sites to proxy atoms are replaced with calls to the
+corresponding stub atom.</p>
+
+<h4><a>Generate Output File</a>
+</h4>
+<p>Once the passes are done, the output file generator is given a sorted list
+of atoms. Its job is to create the executable content file wrapper and place
+the content of the atoms into it.
+</p>
+
+
+<h2>
+ <a name="Future Directions">Future Directions</a>
+</h2>
+
+<h4><a>Sections</a>
+</h4>
+<p>The current use of sections in mach-o .o files over-constrains the linker.
+By default, the linker should preserve the section an atom is in. But since
+all sections must be contiguous in the output, that limits the ability of
+the linker to order atoms for locality. It would be helpful to enrich the
+object file with with reason something is in the section it is. For instance,
+is the section found at runtime? Or was the use of a section just a quick
+way to group some content together?
+</p>
+<p>The ELF model for sections is a little better than mach-o because ELF
+sections have write and execute bits, whereas mach-o sections must be in some
+segment and the segment has the write and execute bits.
+</p>
+
+<h4><a>Mach-o Object File Format</a>
+</h4>
+<p>
+The messiest part of the linker is the mach-o parser. This is because mach-o
+is a traditional section and symbols based file format. The parser must infer
+atom boundaries using two approaches. The first is that some section types have
+well defined content which the linker can parse into atoms (e.g. __cstring,
+__eh_frame). The other approach is a naming convention (which the compiler follows)
+by which the linker breaks sections into atoms at any non-local (not starting
+with 'L') symbol. The processing the linker has to do parse mach-o .o files is a
+significant part of the link time.
+</p>
+
+<p>Given that the assembler writes object files once, whereas the linker reads
+them many times (during development), it would make sense to optimize the object
+file format to be something the linker can read/parse efficiently.</p>
+
+<h4><a>New Object File Model</a>
+</h4>
+<p>LLVM has a nice model for its IR. There are three representations:
+the binary bit code file, the in-memory object model, and a textual
+representation. LLVM contains utility possible code for converting between these
+representations. The same model makes sense for atoms too. There should be
+three representations for atoms: binary file, in-memory, and textual. The Darwin
+linker already has an in-memory C++ object model for Atoms. All we need is a
+textual representation and binary file format.
+</p>
+<p>Note: in the darwin linker the binary format for input object files is
+independent of the output executable format. That is, we could have one
+universal object file format which the linker could use as input to produce
+mach-o, ELF, or PE executables.</p>
+<p>
+The object file binary format should be designed to instantiate into atoms
+as fast as possible. The obvious way to do that is that the
+file format would be an array of atoms. The linker just mmaps in the file and
+looks at the header to see how many atoms there and instantiate that many atoms
+with the atom attribute information coming from that array. The trick is
+designing this in a way that can be extended as the Atom mode evolves and new
+attributes are added.
+</p>
+<p>
+In designing a textual format we want something easy for humans to read and
+easy for the linker to parse. Since an atom has lots of attributes most of
+which are usually just the default, we should define default values for
+every attribute so that those can be omitted from the text representation.
+One possile format is YAML. Here is the atoms for a simple hello world
+program expressed in YAML.
+</p>
+<pre>
+---
+target-triple: x86_64-apple-darwin11
+source:
+
+atoms:
+ - name: _main
+ scope: linkage-unit
+ type: code
+ alignment:
+ power: 4
+ content: [ 55, 48, 89, e5, 48, 8d, 3d, 00, 00, 00, 00, 30, c0, e8, 00, 00,
+ 00, 00, 31, c0, 5d, c3 ]
+ fixups:
+ - offset: 07
+ kind: pcrel32
+ target: 2
+ - offset: 0E
+ kind: call32
+ target: _fprintf
+
+ - type: c-string
+ merge: by-content
+ content: [ 73, 5A, 00 ]
+
+...
+</pre>
+
+<p>One big use for the textual format will be writing test cases. The Darwin
+linker test suite test cases are written mostly in C/C++ and a few assembly
+files. The use of C means the same test case can be compiled for different
+architectures. But writing test cases in C is problematic because the compiler
+may vary its output over time for its own optimization reasons which my
+inadvertently disable or break the linker feature trying to be tested. By
+writing test cases in the linkers own textual format, we can exactly specify
+every attribute of every atom and thus target specific linker logic.
+</p>
+
+<h4><a>Debug Info</a>
+</h4>
+<p>Around 2005 when Apple switched from using STABS to using DWARF for debug
+information, we made a design decision to have the linker ignore DWARF in
+.o files. This improves linking performance because the linker is not
+copying tons of debug info. Instead, the linker adds "debug notes" into
+output binary that contain the paths of the original .o files. During development
+the Darwin debugger will notice the debug notes and the load the dwarf
+debug information from the original object files. For release builds,
+a tool named dsymutil is run on the program. It finds the debug notes and
+then the original object files, then reads, merges and optimizes all the dwarf
+debug information into one .dSYM file which can be loaded by the debugger
+if needed.</p>
+
+<p>The current way DWARF is generated is that all debug information for all
+functions in a translation unit are merged and optimized into sections based
+on debug info kind. For instance the mapping of instructions to source line
+numbers for all functions is compressed and put in one section. This does not
+play well in an Atom based file format. One idea is to have the compiler
+emit some intermediate representation debug information (one which is
+partitioned per atom) into the Atom based file format. The linker could
+then have code to convert that intermediate debug into to final dwarf.
+This is still an open question.</p>
+
+<h4><a>Extending Atom attributes to ELF and XCOFF</a>
+</h4>
+<p>The current set of attributes defined for Atoms in the darwin linker
+were chosen to meet the requirements of developing code to run on iOS and
+Mac OS X. Below is a list of the attributes and their possible values.
+It may just require adding more values to support ELF and XCOFF. Or there
+may need to be new attributes added to capture new functionality.
+</p>
+<ul>
+ <li>Name</li>
+ <li>Size</li>
+ <li>Section (I'd like to get rid of this)</li>
+ <li>ContentType (currently some of this comes from section)</li>
+ <ul>
+ <li>code</li>
+ <li>stub</li>
+ <li>data</li>
+ <li>zeroFill</li>
+ <li>initializerPointer</li>
+ <li>objc1Class</li>
+ <li>objc2Class</li>
+ <li>objcClassPointer</li>
+ <li>objc2CategoryList</li>
+ <li>non-lazy-pointer</li>
+ <li>lazy-pointer</li>
+ <li>constant</li>
+ <li>literal4</li>
+ <li>literal8</li>
+ <li>literal16</li>
+ <li>cstring</li>
+ <li>cstringPointer</li>
+ <li>utf16string</li>
+ <li>CFString</li>
+ <li>CFI</li>
+ <li>LSDA</li>
+ </ul>
+ </li>
+ <li>Scope
+ <ul>
+ <li>translationUnit (static functions)</li>
+ <li>linkageUnit (visibility hidden)</li>
+ <li>global</li>
+ </ul>
+ </li>
+ <li>DefinitionKind
+ <ul>
+ <li>regular</li>
+ <li>tentative (ANSI C feature)</li>
+ <li>absolute (assembly code feature)</li>
+ <li>proxy (stand-in for dynamic library symbol)</li>
+ </ul>
+ </li>
+ <li>Combine
+ <ul>
+ <li>never</li>
+ <li>byName (weak symbols)</li>
+ <li>byContent (simple constants)</li>
+ <li>byContentAndReferences (complex constants)</li>
+ </ul>
+ </li>
+ <li>SymbolTableStatus
+ <ul>
+ <li>In</li>
+ <li>notIn (anonymous)</li>
+ <li>inAsAbsolute (assembly code feature)</li>
+ <li>inAndNeverStrip (tell strip tool to leave)</li>
+ <li>inWithRandomName (mach-o .o feature)</li>
+ </ul>
+ <li>Alignment
+ <ul>
+ <li>powerOfTwo</li>
+ <li>modulus</li>
+ </ul>
+ <li>NeverDeadStrip (boolean)</li>
+ <li>IsThumb (ARM specific)</li>
+</ul>
+<p>Where does dllexport fit in here? Where does visibility protected and
+internal fit? Protected seems like scope=global plus the rule to not
+indirect references to it. Internal is like hidden plus enables some
+compiler optimizations. I'm not sure the linker needs to know about internal.
+</p>
+
+</body>
+</html>
+
Write minimal stabs which causes the debugger to open and read the original .o file for full stabs.
This style of debugging is obsolete in Mac OS X 10.5. This option is obsolete.
.It Fl X
-Strip local symbols that being the 'L'. This is the default. This option is obsolete.
+Strip local symbols that begin with 'L'. This is the default. This option is obsolete.
.It Fl s
Completely strip the output, including removing the symbol table. This file format variant is no longer supported.
This option is obsolete.
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
+ B3B672421406D42800A376BB /* Snapshot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B3B672411406D42800A376BB /* Snapshot.cpp */; };
F9023C4E06D5A272001BBF46 /* ld.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9023C3F06D5A254001BBF46 /* ld.cpp */; };
F933E3D9092E855B0083EAC8 /* ObjectDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F971EED706D5AD240041D381 /* ObjectDump.cpp */; };
F93CB248116E69EB003233B8 /* tlvp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F93CB246116E69EB003233B8 /* tlvp.cpp */; };
F9BA955E10A233000097A440 /* huge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9BA955C10A233000097A440 /* huge.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 */; };
+ F9CC24191461FB4300A92174 /* blob.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9CC24141461FB4300A92174 /* blob.cpp */; };
F9EA72D5097454FF008B4F1D /* machochecker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9EA72D4097454FF008B4F1D /* machochecker.cpp */; };
F9EA7584097882F3008B4F1D /* debugline.c in Sources */ = {isa = PBXBuildFile; fileRef = F9EA7582097882F3008B4F1D /* debugline.c */; };
F9EA75BC09788857008B4F1D /* debugline.c in Sources */ = {isa = PBXBuildFile; fileRef = F9EA7582097882F3008B4F1D /* debugline.c */; };
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
- C02A29DE0953B26E001FB8C1 /* ChangeLog */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = ChangeLog; sourceTree = "<group>"; };
+ B3B672411406D42800A376BB /* Snapshot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Snapshot.cpp; path = src/ld/Snapshot.cpp; sourceTree = "<group>"; };
+ B3B672441406D44300A376BB /* Snapshot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Snapshot.h; path = src/ld/Snapshot.h; sourceTree = "<group>"; };
+ B3C7A09914295B9C005FC714 /* compile_stubs */ = {isa = PBXFileReference; lastKnownFileType = text.script.csh; path = compile_stubs; sourceTree = "<group>"; };
F9023C3906D5A23E001BBF46 /* ld */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ld; sourceTree = BUILT_PRODUCTS_DIR; };
F9023C3F06D5A254001BBF46 /* ld.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ld.cpp; path = src/ld/ld.cpp; sourceTree = "<group>"; };
F92D9C2710657AAB00FF369B /* stub_x86_64_classic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_x86_64_classic.hpp; sourceTree = "<group>"; };
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>"; };
+ F9CC24141461FB4300A92174 /* blob.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = blob.cpp; sourceTree = "<group>"; };
+ F9CC24151461FB4300A92174 /* blob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = blob.h; sourceTree = "<group>"; };
+ F9CC24161461FB4300A92174 /* endian.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = endian.h; sourceTree = "<group>"; };
+ F9CC24171461FB4300A92174 /* memutils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = memutils.h; sourceTree = "<group>"; };
+ F9CC24181461FB4300A92174 /* superblob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = superblob.h; sourceTree = "<group>"; };
+ F9CCF761144CE1AD007CB524 /* create_configure */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = create_configure; path = src/create_configure; 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/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; };
F9023C2C06D5A227001BBF46 = {
isa = PBXGroup;
children = (
- C02A29DE0953B26E001FB8C1 /* ChangeLog */,
F9B813A80EC27B6300F94C13 /* abstraction */,
F9B813AD0EC27B8500F94C13 /* ld */,
F9B813B00EC27B9E00F94C13 /* other */,
F989D7E91072DEC20014B60C /* HeaderAndLoadCommands.hpp */,
F989D3AA10684F5B0014B60C /* LinkEdit.hpp */,
F989D44B10694F2E0014B60C /* LinkEditClassic.hpp */,
+ F9CC24131461FB4300A92174 /* code-sign-blobs */,
F9AA650B1051BD2B003E3539 /* passes */,
F9AA65861051E750003E3539 /* parsers */,
F933DC37092A82480083EAC8 /* Architectures.hpp */,
F9EA7582097882F3008B4F1D /* debugline.c */,
F9EA7583097882F3008B4F1D /* debugline.h */,
+ B3B672411406D42800A376BB /* Snapshot.cpp */,
+ B3B672441406D44300A376BB /* Snapshot.h */,
);
name = ld;
sourceTree = "<group>";
F9B813B00EC27B9E00F94C13 /* other */ = {
isa = PBXGroup;
children = (
+ B3C7A09914295B9C005FC714 /* compile_stubs */,
+ F9CCF761144CE1AD007CB524 /* create_configure */,
F9EA72D4097454FF008B4F1D /* machochecker.cpp */,
F971EED706D5AD240041D381 /* ObjectDump.cpp */,
F9BA515B0ECE58AA00D1D62E /* dyldinfo.cpp */,
name = other;
sourceTree = "<group>";
};
+ F9CC24131461FB4300A92174 /* code-sign-blobs */ = {
+ isa = PBXGroup;
+ children = (
+ F9CC24141461FB4300A92174 /* blob.cpp */,
+ F9CC24151461FB4300A92174 /* blob.h */,
+ F9CC24161461FB4300A92174 /* endian.h */,
+ F9CC24171461FB4300A92174 /* memutils.h */,
+ F9CC24181461FB4300A92174 /* superblob.h */,
+ );
+ name = "code-sign-blobs";
+ path = "src/ld/code-sign-blobs";
+ sourceTree = "<group>";
+ };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
isa = PBXNativeTarget;
buildConfigurationList = F933D91B09291AC90083EAC8 /* Build configuration list for PBXNativeTarget "ld" */;
buildPhases = (
- F9E8DB4D11921594007B4D6A /* make config.h */,
+ F9E8DB4D11921594007B4D6A /* make configure.h */,
+ B3C7A09714295B60005FC714 /* make compile_stub string */,
F9023C3606D5A23E001BBF46 /* Sources */,
F9023C3706D5A23E001BBF46 /* Frameworks */,
F97F5025070D0B6300B9FCD7 /* copy man page */,
isa = PBXNativeTarget;
buildConfigurationList = F933D91F09291AC90083EAC8 /* Build configuration list for PBXNativeTarget "ObjectDump" */;
buildPhases = (
+ F9CCF773144CE304007CB524 /* make configure.h */,
F971EED006D5ACF60041D381 /* Sources */,
F971EED106D5ACF60041D381 /* Frameworks */,
);
isa = PBXNativeTarget;
buildConfigurationList = F9A3DDCF0ED762C100C590B9 /* Build configuration list for PBXNativeTarget "libprunetrie" */;
buildPhases = (
+ F9CCF781144CE3DF007CB524 /* make configure.h */,
F9A3DDC70ED762B700C590B9 /* Sources */,
F9A3DE140ED76D7700C590B9 /* CopyFiles */,
);
isa = PBXNativeTarget;
buildConfigurationList = F9B670050DDA176100E6D0DA /* Build configuration list for PBXNativeTarget "unwinddump" */;
buildPhases = (
+ F9CCF77C144CE36B007CB524 /* make configure.h */,
F9B670020DDA176100E6D0DA /* Sources */,
F9B670040DDA176100E6D0DA /* Frameworks */,
F9B813870EC2659600F94C13 /* install man page */,
isa = PBXNativeTarget;
buildConfigurationList = F9BA516D0ECE58DA00D1D62E /* Build configuration list for PBXNativeTarget "dyldinfo" */;
buildPhases = (
+ F9CCF76B144CE2AD007CB524 /* make configure.h */,
F9BA515E0ECE58BE00D1D62E /* Sources */,
F9BA515F0ECE58BE00D1D62E /* Frameworks */,
F9C12EA50ED63E05005BC69D /* install man page */,
isa = PBXNativeTarget;
buildConfigurationList = F9EA72CF097454D5008B4F1D /* Build configuration list for PBXNativeTarget "machocheck" */;
buildPhases = (
+ F9CCF76F144CE2D6007CB524 /* make configure.h */,
F9EA72C8097454A6008B4F1D /* Sources */,
F9EA72C9097454A6008B4F1D /* Frameworks */,
);
isa = PBXNativeTarget;
buildConfigurationList = F9EC77F00A2F8616002A3E39 /* Build configuration list for PBXNativeTarget "rebase" */;
buildPhases = (
+ F9CCF765144CE244007CB524 /* make configure.h */,
F9EC77EB0A2F85F6002A3E39 /* Sources */,
F9EC77EC0A2F85F6002A3E39 /* Frameworks */,
F9B1A25E0A3A44CB00DA8FAB /* install man page */,
/* End PBXProject section */
/* Begin PBXShellScriptBuildPhase section */
+ B3C7A09714295B60005FC714 /* make compile_stub string */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "$(SRCROOT)/compile_stubs",
+ );
+ name = "make compile_stub string";
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/compile_stubs.h",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/csh;
+ shellScript = "echo \"static const char *compile_stubs = \" > $DERIVED_FILE_DIR/compile_stubs.h\ncat compile_stubs | sed s/\\\"/\\\\\\\\\\\"/g | sed s/^/\\\"/ | sed s/\\$/\\\\\\\\n\\\"/ >> $DERIVED_FILE_DIR/compile_stubs.h\necho \";\" >> $DERIVED_FILE_DIR/compile_stubs.h";
+ showEnvVarsInLog = 0;
+ };
F96D5367094A2754008E9EE8 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/csh;
- shellScript = "# Let tests set MACOSX_DEPLOYMENT_TARGET as they need\nunsetenv MACOSX_DEPLOYMENT_TARGET\n\n# make linker relative libLTO.dylib\nmkdir -p ${BUILD_DIR}/lib\nln -sf /Developer/usr/lib/libLTO.dylib ${BUILD_DIR}/lib/libLTO.dylib\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";
+ shellScript = "# Let tests set MACOSX_DEPLOYMENT_TARGET as they need\nunsetenv MACOSX_DEPLOYMENT_TARGET\n\n# make linker relative libLTO.dylib\nmkdir -p ${BUILD_DIR}/lib\nln -sf /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libLTO.dylib ${BUILD_DIR}/lib/libLTO.dylib\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;
};
F9871A3413340B4600DB3F24 /* Platform install */ = {
);
runOnlyForDeploymentPostprocessing = 1;
shellPath = /bin/sh;
- shellScript = "\nif [ -n \"${DT_TOOLCHAIN_DIR}\" ]\nthen\n\tmkdir -p \"${DSTROOT}/${DT_TOOLCHAIN_DIR}\"\n\tmv ${DSTROOT}/usr \"${DSTROOT}/${DT_TOOLCHAIN_DIR}\"\nelse\n\tif [ -n \"${RC_PURPLE}\" ]\n\tthen\n\t\tmkdir -p ${DSTROOT}/Developer/Platforms/iPhoneOS.platform/Developer/\n\t\tmv ${DSTROOT}/usr ${DSTROOT}/Developer/Platforms/iPhoneOS.platform/Developer\n\tfi\nfi\n\n";
+ shellScript = "\nif [ -n \"${DT_TOOLCHAIN_DIR}\" ]\nthen\n\tmkdir -p \"${DSTROOT}/${DT_TOOLCHAIN_DIR}\"\n\tmv ${DSTROOT}/usr \"${DSTROOT}/${DT_TOOLCHAIN_DIR}\"\nelse\n\tif [ -n \"${RC_PURPLE}\" ]\n\tthen\n\t\tmkdir -p ${DSTROOT}/Developer/Platforms/iPhoneOS.platform/Developer/\n\t\tmv ${DSTROOT}/usr ${DSTROOT}/Developer/Platforms/iPhoneOS.platform/Developer\n\telse\n\t\tmkdir -p ${DSTROOT}/Developer/usr/bin\n\t\tcp ${DSTROOT}/usr/bin/ld ${DSTROOT}/Developer/usr/bin\n\tfi\nfi\n\n";
+ showEnvVarsInLog = 0;
+ };
+ F9CCF765144CE244007CB524 /* make configure.h */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "make configure.h";
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/configure.h",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "${SRCROOT}/src/create_configure\n";
+ showEnvVarsInLog = 0;
+ };
+ F9CCF76B144CE2AD007CB524 /* make configure.h */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "make configure.h";
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/configure.h",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "${SRCROOT}/src/create_configure\n";
+ showEnvVarsInLog = 0;
+ };
+ F9CCF76F144CE2D6007CB524 /* make configure.h */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "make configure.h";
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/configure.h",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "${SRCROOT}/src/create_configure\n";
+ showEnvVarsInLog = 0;
+ };
+ F9CCF773144CE304007CB524 /* make configure.h */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "make configure.h";
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/configure.h",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "${SRCROOT}/src/create_configure\n";
+ showEnvVarsInLog = 0;
+ };
+ F9CCF77C144CE36B007CB524 /* make configure.h */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "make configure.h";
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/configure.h",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "${SRCROOT}/src/create_configure\n";
showEnvVarsInLog = 0;
};
- F9E8DB4D11921594007B4D6A /* make config.h */ = {
+ F9CCF781144CE3DF007CB524 /* make configure.h */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
- name = "make config.h";
+ name = "make configure.h";
outputPaths = (
"$(DERIVED_FILE_DIR)/configure.h",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "echo \"\" > ${DERIVED_FILE_DIR}/configure.h\n\nif [ -n \"${IPHONEOS_DEPLOYMENT_TARGET}\" ]; then\n\techo \"#define DEFAULT_IPHONEOS_MIN_VERSION \\\"${IPHONEOS_DEPLOYMENT_TARGET}\\\"\" >> ${DERIVED_FILE_DIR}/configure.h\nelse\n if [ -n \"${MACOSX_DEPLOYMENT_TARGET}\" ]; then\n\techo \"#define DEFAULT_MACOSX_MIN_VERSION \\\"${MACOSX_DEPLOYMENT_TARGET}\\\"\" >> ${DERIVED_FILE_DIR}/configure.h\n fi\nfi\n";
+ shellScript = "${SRCROOT}/src/create_configure\n";
+ showEnvVarsInLog = 0;
+ };
+ F9E8DB4D11921594007B4D6A /* make configure.h */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "make configure.h";
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/configure.h",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/bash;
+ shellScript = "${SRCROOT}/src/create_configure\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
F9AE20FF1107D1440007ED5D /* dylibs.cpp in Sources */,
F93CB248116E69EB003233B8 /* tlvp.cpp in Sources */,
F9AA44DC1294885F00CB8390 /* branch_shim.cpp in Sources */,
+ B3B672421406D42800A376BB /* Snapshot.cpp in Sources */,
+ F9CC24191461FB4300A92174 /* blob.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
F933D91C09291AC90083EAC8 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
- ARCHS = "$(ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1)";
- ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1 = x86_64;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
DEAD_CODE_STRIPPING = YES;
GCC_WARN_MISSING_PARENTHESES = YES;
GCC_WARN_NON_VIRTUAL_DESTRUCTOR = NO;
GCC_WARN_PEDANTIC = NO;
- GCC_WARN_SHADOW = YES;
+ GCC_WARN_SHADOW = NO;
GCC_WARN_SIGN_COMPARE = YES;
GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
GCC_WARN_UNINITIALIZED_AUTOS = NO;
GCC_WARN_UNUSED_VALUE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
+ "$(DT_TOOLCHAIN_DIR)/usr/local/include",
"$(DEVELOPER_DIR)/usr/local/include",
"$(DEVELOPER_DIR)/usr/include",
);
LINKER_DISPLAYS_MANGLED_NAMES = NO;
MACOSX_DEPLOYMENT_TARGET = "";
OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)";
- OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib";
+ OTHER_LDFLAGS = (
+ "@$(DERIVED_SOURCES_DIR)/LTO_option.txt",
+ "-Wl,-exported_symbol,__mh_execute_header",
+ );
PREBINDING = NO;
PRODUCT_NAME = ld;
SECTORDER_FLAGS = "";
F933D91D09291AC90083EAC8 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
- ARCHS = "$(ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1)";
- ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1 = x86_64;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
DEAD_CODE_STRIPPING = YES;
GCC_WARN_UNUSED_VALUE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
+ "$(DT_TOOLCHAIN_DIR)/usr/local/include",
"$(DEVELOPER_DIR)/usr/local/include",
"$(DEVELOPER_DIR)/usr/include",
);
INSTALL_PATH = /usr/bin;
OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)";
OTHER_LDFLAGS = (
- "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib",
+ "@$(DERIVED_SOURCES_DIR)/LTO_option.txt",
"-Wl,-exported_symbol,__mh_execute_header",
);
PREBINDING = NO;
F933D92009291AC90083EAC8 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
- ARCHS = "$(ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1)";
- ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1 = x86_64;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
"$(DEVELOPER_DIR)/usr/local/include",
);
INSTALL_PATH = "$(HOME)/bin";
- OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib";
+ OTHER_LDFLAGS = "@$(DERIVED_SOURCES_DIR)/LTO_option.txt";
OTHER_REZFLAGS = "";
PREBINDING = NO;
PRODUCT_NAME = ObjectDump;
"$(DEVELOPER_DIR)/usr/local/include",
);
INSTALL_PATH = "$(HOME)/bin";
- OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib";
+ OTHER_LDFLAGS = "@$(DERIVED_SOURCES_DIR)/LTO_option.txt";
OTHER_REZFLAGS = "";
PREBINDING = NO;
PRODUCT_NAME = ObjectDump;
F933D92409291AC90083EAC8 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1)";
+ ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1 = x86_64;
GCC_DYNAMIC_NO_PIC = NO;
GCC_TREAT_WARNINGS_AS_ERRORS = NO;
};
F933D92509291AC90083EAC8 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1)";
+ ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1 = x86_64;
GCC_DYNAMIC_NO_PIC = NO;
GCC_TREAT_WARNINGS_AS_ERRORS = NO;
};
F9849FF810B5DE8E009E9878 /* Release-assert */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1)";
+ ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1 = x86_64;
GCC_DYNAMIC_NO_PIC = NO;
GCC_TREAT_WARNINGS_AS_ERRORS = NO;
};
F9849FFA10B5DE8E009E9878 /* Release-assert */ = {
isa = XCBuildConfiguration;
buildSettings = {
- ARCHS = "$(ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1)";
- ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1 = x86_64;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
DEAD_CODE_STRIPPING = YES;
GCC_WARN_UNUSED_VALUE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
+ "$(DT_TOOLCHAIN_DIR)/usr/local/include",
"$(DEVELOPER_DIR)/usr/local/include",
"$(DEVELOPER_DIR)/usr/include",
);
INSTALL_PATH = /usr/bin;
OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)";
OTHER_LDFLAGS = (
- "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib",
+ "@$(DERIVED_SOURCES_DIR)/LTO_option.txt",
"-Wl,-exported_symbol,__mh_execute_header",
);
PREBINDING = NO;
"$(DEVELOPER_DIR)/usr/local/include",
);
INSTALL_PATH = "$(HOME)/bin";
- OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib";
+ OTHER_LDFLAGS = "@$(DERIVED_SOURCES_DIR)/LTO_option.txt";
OTHER_REZFLAGS = "";
PREBINDING = NO;
PRODUCT_NAME = ObjectDump;
#include <mach-o/stab.h>
#include <mach-o/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 <stddef.h>
#include "FileAbstraction.hpp"
+#include "configure.h"
// stuff that will eventually go away once newer cctools headers are widespread
#ifndef LC_LOAD_UPWARD_DYLIB
#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
#define LC_DYLD_ENVIRONMENT 0x27
#endif
-// hack until newer <mach-o/arm/reloc.h> everywhere
-#define ARM_RELOC_HALF 8
-#define ARM_RELOC_HALF_SECTDIFF 9
+#ifndef LC_DATA_IN_CODE
+ #define LC_DATA_IN_CODE 0x29 /* table of non-instructions in __text */
+ struct data_in_code_entry {
+ uint32_t offset;
+ uint16_t length;
+ uint16_t kind;
+ };
+#endif
+
+#ifndef LC_DYLIB_CODE_SIGN_DRS
+ #define LC_DYLIB_CODE_SIGN_DRS 0x2B
+#endif
#ifndef CPU_SUBTYPE_ARM_V7F
#define CPU_SUBTYPE_ARM_V7F ((cpu_subtype_t) 10)
#define CPU_SUBTYPE_ARM_V7K ((cpu_subtype_t) 12)
#endif
-struct ARMSubType {
- const char* subTypeName;
+
+#ifndef LC_SOURCE_VERSION
+ #define LC_SOURCE_VERSION 0x2A
+ struct source_version_command {
+ uint32_t cmd; /* LC_SOURCE_VERSION */
+ uint32_t cmdsize; /* 16 */
+ uint64_t version; /* A.B.C.D.E packed as a24.b10.c10.d10.e10 */
+ };
+#endif
+
+#ifndef LC_MAIN
+ #define LC_MAIN (0x28|LC_REQ_DYLD) /* replacement for LC_UNIXTHREAD */
+ struct entry_point_command {
+ uint32_t cmd; /* LC_MAIN only used in MH_EXECUTE filetypes */
+ uint32_t cmdsize; /* 24 */
+ uint64_t entryoff; /* file (__TEXT) offset of main() */
+ uint64_t stacksize;/* if not zero, initial stack size */
+ };
+#endif
+
+#ifndef LC_DYLIB_CODE_SIGN_DRS
+ #define LC_DYLIB_CODE_SIGN_DRS 0x2B
+#endif
+
+
+struct ArchInfo {
+ const char* archName;
+ cpu_type_t cpuType;
+ cpu_subtype_t cpuSubType;
const char* llvmTriplePrefix;
- cpu_subtype_t subType;
+ const char* llvmTriplePrefixAlt;
+ bool isSubType;
bool supportsThumb2;
};
-static const ARMSubType ARMSubTypes[] = {
- { "armv4t","armv4t-", CPU_SUBTYPE_ARM_V4T, false },
- { "armv5", "armv5e-", CPU_SUBTYPE_ARM_V5TEJ, false },
- { "armv6", "armv6-", CPU_SUBTYPE_ARM_V6, false },
- { "armv7", "thumbv7-", CPU_SUBTYPE_ARM_V7, true },
- { "armv7f", "thumbv7f-", CPU_SUBTYPE_ARM_V7F, true },
- { "armv7k", "thumbv7k-", CPU_SUBTYPE_ARM_V7K, true },
- { 0, NULL, false }
+static const ArchInfo archInfoArray[] = {
+#if SUPPORT_ARCH_x86_64
+ { "x86_64", CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL, "x86_64-", "", false, false },
+#endif
+#if SUPPORT_ARCH_i386
+ { "i386", CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL, "i386-", "", false, false },
+#endif
+#if SUPPORT_ARCH_armv4t
+ { "armv4t", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V4T, "armv4t-", "", true, false },
+ #define SUPPORT_ARCH_arm_any 1
+#endif
+#if SUPPORT_ARCH_armv5
+ { "armv5", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V5TEJ, "armv5e-", "", true, false },
+ #define SUPPORT_ARCH_arm_any 1
+#endif
+#if SUPPORT_ARCH_armv6
+ { "armv6", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6, "armv6-", "", true, false },
+ #define SUPPORT_ARCH_arm_any 1
+#endif
+#if SUPPORT_ARCH_armv7
+ { "armv7", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7, "thumbv7-", "armv7-", true, true },
+ #define SUPPORT_ARCH_arm_any 1
+#endif
+ { NULL, 0, 0, NULL, NULL, false, false }
};
+// weird, but this include must wait until after SUPPORT_ARCH_arm_any is set up
+#if SUPPORT_ARCH_arm_any
+#include <mach-o/arm/reloc.h>
+#endif
+
+// hack until newer <mach-o/arm/reloc.h> everywhere
+#define ARM_RELOC_HALF 8
+#define ARM_RELOC_HALF_SECTDIFF 9
+
//
uint32_t version() const INLINE { return fields.version; }
void set_version(uint32_t value) INLINE { E::set32(fields.version, value); }
-#ifdef LC_SOURCE_VERSION
+#ifdef DICE_KIND_DATA
uint32_t sdk() const INLINE { return fields.sdk; }
void set_sdk(uint32_t value) INLINE { E::set32(fields.sdk, value); }
#else
};
+//
+// mach-o source version load command
+//
+template <typename P>
+class macho_source_version_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); }
+
+ uint64_t version() const INLINE { return fields.version; }
+ void set_version(uint64_t value) INLINE { E::set64(fields.version, value); }
+
+ typedef typename P::E E;
+private:
+ source_version_command fields;
+};
+
+
+//
+// mach-o source version load command
+//
+template <typename P>
+class macho_entry_point_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); }
+
+ uint64_t entryoff() const INLINE { return fields.entryoff; }
+ void set_entryoff(uint64_t value) INLINE { E::set64(fields.entryoff, value); }
+
+ uint64_t stacksize() const INLINE { return fields.stacksize; }
+ void set_stacksize(uint64_t value) INLINE { E::set64(fields.stacksize, value); }
+
+ typedef typename P::E E;
+private:
+ entry_point_command fields;
+};
+
+
+
+template <typename P>
+class macho_data_in_code_entry {
+public:
+ uint32_t offset() const INLINE { return E::get32(fields.offset); }
+ void set_offset(uint32_t value) INLINE { E::set32(fields.offset, value); }
+
+ uint16_t length() const INLINE { return E::get16(fields.length); }
+ void set_length(uint16_t value) INLINE { E::set16((uint16_t&)fields.length, value); }
+
+ uint16_t kind() const INLINE { return E::get16(fields.kind); }
+ void set_kind(uint16_t value) INLINE { E::set16((uint16_t&)fields.kind, value); }
+
+ typedef typename P::E E;
+private:
+ data_in_code_entry fields;
+};
+
+
#endif // __MACH_O_FILE_ABSTRACTION__
++edgeStrLen;
}
cummulativeString[curStrOffset+edgeStrLen] = *s++;
- uint32_t childNodeOffet = read_uleb128(s, end);
- processExportNode(start, start+childNodeOffet, end, cummulativeString, curStrOffset+edgeStrLen, output);
+ uint32_t childNodeOffset = read_uleb128(s, end);
+ if (childNodeOffset == 0)
+ throw "malformed trie, childNodeOffset==0";
+ processExportNode(start, start+childNodeOffset, end, cummulativeString, curStrOffset+edgeStrLen, output);
}
}
--- /dev/null
+#!/bin/bash
+
+echo "" > ${DERIVED_FILE_DIR}/configure.h
+
+if [ -n "${IPHONEOS_DEPLOYMENT_TARGET}" ]; then
+ echo "#define DEFAULT_IPHONEOS_MIN_VERSION \"${IPHONEOS_DEPLOYMENT_TARGET}\"" >> ${DERIVED_FILE_DIR}/configure.h
+else
+ if [ -n "${MACOSX_DEPLOYMENT_TARGET}" ]; then
+ echo "#define DEFAULT_MACOSX_MIN_VERSION \"${MACOSX_DEPLOYMENT_TARGET}\"" >> ${DERIVED_FILE_DIR}/configure.h
+ fi
+fi
+
+if [ -z "${RC_SUPPORTED_ARCHS}" ]; then
+ RC_SUPPORTED_ARCHS="i386 x86_64"
+fi
+
+for ANARCH in ${RC_SUPPORTED_ARCHS}
+do
+ KNOWN_ARCHS=",armv4t,armv5,armv6,armv7,i386,x86_64,"
+ FOUND=`echo "$KNOWN_ARCHS" | grep ",$ANARCH,"`
+ if [ $FOUND ]; then
+ echo "#define SUPPORT_ARCH_$ANARCH 1" >> ${DERIVED_FILE_DIR}/configure.h
+ else
+ echo "#error uknown architecture: $ANARCH" >> ${DERIVED_FILE_DIR}/configure.h
+ fi
+done
+
+echo "#define ALL_SUPPORTED_ARCHS \"${RC_SUPPORTED_ARCHS}\"" >> ${DERIVED_FILE_DIR}/configure.h
+
+
+# <rdar://problem/10897631> ld64 hardcodes a reference to /Developer/usr/lib/libLTO.dylib
+if [ -n "${DT_TOOLCHAIN_DIR}" ]
+then
+ echo "-Wl,-lazy_library,${DT_TOOLCHAIN_DIR}/usr/lib/libLTO.dylib" > ${DERIVED_SOURCES_DIR}/LTO_option.txt
+else
+ if [ -e "/Developer/usr/lib/libLTO.dylib" ]
+ then
+ echo "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib" > ${DERIVED_SOURCES_DIR}/LTO_option.txt
+ else
+ echo "-Wl,-lazy_library,${BUILT_PRODUCTS_DIR}/../lib/libLTO.dylib" > ${DERIVED_SOURCES_DIR}/LTO_option.txt
+ fi
+fi
+
+
+
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-*
*
- * Copyright (c) 2009 Apple Inc. All rights reserved.
+ * Copyright (c) 2009-2011 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
uint8_t* copyRoutinesLoadCommand(uint8_t* p) const;
uint8_t* copyUUIDLoadCommand(uint8_t* p) const;
uint8_t* copyVersionLoadCommand(uint8_t* p) const;
+ uint8_t* copySourceVersionLoadCommand(uint8_t* p) const;
uint8_t* copyThreadsLoadCommand(uint8_t* p) const;
+ uint8_t* copyEntryPointLoadCommand(uint8_t* p) const;
uint8_t* copyEncryptionLoadCommand(uint8_t* p) const;
uint8_t* copySplitSegInfoLoadCommand(uint8_t* p) const;
uint8_t* copyDylibLoadCommand(uint8_t* p, const ld::dylib::File*) const;
uint8_t* copySubLibraryLoadCommand(uint8_t* p, const char* name) const;
uint8_t* copySubUmbrellaLoadCommand(uint8_t* p, const char* name) const;
uint8_t* copyFunctionStartsLoadCommand(uint8_t* p) const;
+ uint8_t* copyDataInCodeLoadCommand(uint8_t* p) const;
+ uint8_t* copyDependentDRLoadCommand(uint8_t* p) const;
uint8_t* copyDyldEnvLoadCommand(uint8_t* p, const char* env) const;
uint32_t sectionFlags(ld::Internal::FinalSection* sect) const;
bool _hasDyldLoadCommand;
bool _hasDylibIDLoadCommand;
bool _hasThreadLoadCommand;
+ bool _hasEntryPointLoadCommand;
bool _hasEncryptionLoadCommand;
bool _hasSplitSegInfoLoadCommand;
bool _hasRoutinesLoadCommand;
bool _hasSubFrameworkLoadCommand;
bool _hasVersionLoadCommand;
bool _hasFunctionStartsLoadCommand;
+ bool _hasDataInCodeLoadCommand;
+ bool _hasSourceVersionLoadCommand;
+ bool _hasDependentDRInfo;
uint32_t _dylibLoadCommmandsCount;
uint32_t _allowableClientLoadCommmandsCount;
uint32_t _dyldEnvironExrasCount;
_hasDyldInfoLoadCommand = opts.makeCompressedDyldInfo();
_hasDyldLoadCommand = ((opts.outputKind() == Options::kDynamicExecutable) || (_options.outputKind() == Options::kDyld));
_hasDylibIDLoadCommand = (opts.outputKind() == Options::kDynamicLibrary);
- _hasThreadLoadCommand = _hasDyldLoadCommand || (opts.outputKind() == Options::kStaticExecutable)
- || (opts.outputKind() == Options::kPreload)
- || (opts.outputKind() == Options::kDyld);
+ _hasThreadLoadCommand = _options.needsThreadLoadCommand();
+ _hasEntryPointLoadCommand = _options.needsEntryPointLoadCommand();
_hasEncryptionLoadCommand = opts.makeEncryptable();
_hasSplitSegInfoLoadCommand = opts.sharedRegionEligible();
_hasRoutinesLoadCommand = (opts.initFunctionName() != NULL);
}
break;
case Options::kStaticExecutable:
- _hasDynamicSymbolTableLoadCommand = false;
+ _hasDynamicSymbolTableLoadCommand = opts.positionIndependentExecutable();
break;
case Options::kPreload:
_hasDynamicSymbolTableLoadCommand = opts.positionIndependentExecutable();
_hasSubFrameworkLoadCommand = (_options.umbrellaName() != NULL);
_hasVersionLoadCommand = _options.addVersionLoadCommand();
_hasFunctionStartsLoadCommand = _options.addFunctionStarts();
+ _hasDataInCodeLoadCommand = _options.addDataInCodeInfo();
+ _hasSourceVersionLoadCommand = _options.needsSourceVersionLoadCommand();
+ _hasDependentDRInfo = _options.needsDependentDRInfo();
_dylibLoadCommmandsCount = _writer.dylibCount();
_allowableClientLoadCommmandsCount = _options.allowableClients().size();
_dyldEnvironExrasCount = _options.dyldEnvironExtras().size();
if ( _hasVersionLoadCommand )
sz += sizeof(macho_version_min_command<P>);
+
+ if ( _hasSourceVersionLoadCommand )
+ sz += sizeof(macho_source_version_command<P>);
if ( _hasThreadLoadCommand )
sz += this->threadLoadCommandSize();
+
+ if ( _hasEntryPointLoadCommand )
+ sz += sizeof(macho_entry_point_command<P>);
if ( _hasEncryptionLoadCommand )
sz += sizeof(macho_encryption_info_command<P>);
if ( _hasFunctionStartsLoadCommand )
sz += sizeof(macho_linkedit_data_command<P>);
+ if ( _hasDataInCodeLoadCommand )
+ sz += sizeof(macho_linkedit_data_command<P>);
+
+ if ( _hasDependentDRInfo )
+ sz += sizeof(macho_linkedit_data_command<P>);
+
return sz;
}
if ( _hasVersionLoadCommand )
++count;
+ if ( _hasSourceVersionLoadCommand )
+ ++count;
+
if ( _hasThreadLoadCommand )
++count;
+
+ if ( _hasEntryPointLoadCommand )
+ ++count;
if ( _hasEncryptionLoadCommand )
++count;
if ( _hasFunctionStartsLoadCommand )
++count;
+ if ( _hasDataInCodeLoadCommand )
+ ++count;
+
+ if ( _hasDependentDRInfo )
+ ++count;
+
return count;
}
else {
if ( _options.outputKind() == Options::kStaticExecutable ) {
bits |= MH_NOUNDEFS;
+ if ( _options.positionIndependentExecutable() )
+ bits |= MH_PIE;
}
else if ( _options.outputKind() == Options::kPreload ) {
bits |= MH_NOUNDEFS;
else
return S_SYMBOL_STUBS | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS;
case ld::Section::typeNonLazyPointer:
- return S_NON_LAZY_SYMBOL_POINTERS;
+ if ( _options.outputKind() == Options::kKextBundle )
+ return S_REGULAR;
+ else if ( (_options.outputKind() == Options::kStaticExecutable) && _options.positionIndependentExecutable() )
+ return S_REGULAR;
+ else
+ return S_NON_LAZY_SYMBOL_POINTERS;
case ld::Section::typeDyldInfo:
return S_REGULAR;
case ld::Section::typeLazyDylibPointer:
else
return S_REGULAR | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS;
case ld::Section::typeInitializerPointers:
- return S_MOD_INIT_FUNC_POINTERS;
+ // <rdar://problem/11456679> i386 kexts need different section type
+ if ( (_options.outputKind() == Options::kObjectFile)
+ && (strcmp(sect->sectionName(), "__constructor") == 0)
+ && (strcmp(sect->segmentName(), "__TEXT") == 0) )
+ return S_REGULAR;
+ else
+ return S_MOD_INIT_FUNC_POINTERS;
case ld::Section::typeTerminatorPointers:
return S_MOD_TERM_FUNC_POINTERS;
case ld::Section::typeTLVInitialValues:
cmd->set_cmd(LC_VERSION_MIN_MACOSX);
cmd->set_cmdsize(sizeof(macho_version_min_command<P>));
cmd->set_version((uint32_t)macVersion);
- cmd->set_sdk(0);
+ cmd->set_sdk(_options.sdkVersion());
}
else {
cmd->set_cmd(LC_VERSION_MIN_IPHONEOS);
cmd->set_cmdsize(sizeof(macho_version_min_command<P>));
cmd->set_version((uint32_t)iOSVersion);
- cmd->set_sdk(0);
+ cmd->set_sdk(_options.sdkVersion());
}
return p + sizeof(macho_version_min_command<P>);
}
+template <typename A>
+uint8_t* HeaderAndLoadCommandsAtom<A>::copySourceVersionLoadCommand(uint8_t* p) const
+{
+ macho_source_version_command<P>* cmd = (macho_source_version_command<P>*)p;
+ cmd->set_cmd(LC_SOURCE_VERSION);
+ cmd->set_cmdsize(sizeof(macho_source_version_command<P>));
+ cmd->set_version(_options.sourceVersion());
+ return p + sizeof(macho_source_version_command<P>);
+}
template <>
return p + threadLoadCommandSize();
}
+
+
+template <typename A>
+uint8_t* HeaderAndLoadCommandsAtom<A>::copyEntryPointLoadCommand(uint8_t* p) const
+{
+ macho_entry_point_command<P>* cmd = (macho_entry_point_command<P>*)p;
+ cmd->set_cmd(LC_MAIN);
+ cmd->set_cmdsize(sizeof(macho_entry_point_command<P>));
+ assert(_state.entryPoint != NULL);
+ pint_t start = _state.entryPoint->finalAddress();
+ if ( _state.entryPoint->isThumb() )
+ start |= 1ULL;
+ cmd->set_entryoff(start - this->finalAddress());
+ cmd->set_stacksize(_options.hasCustomStack() ? _options.customStackSize() : 0 );
+ return p + sizeof(macho_entry_point_command<P>);
+}
+
+
template <typename A>
uint8_t* HeaderAndLoadCommandsAtom<A>::copyEncryptionLoadCommand(uint8_t* p) const
{
}
+template <typename A>
+uint8_t* HeaderAndLoadCommandsAtom<A>::copyDataInCodeLoadCommand(uint8_t* p) const
+{
+ macho_linkedit_data_command<P>* cmd = (macho_linkedit_data_command<P>*)p;
+ cmd->set_cmd(LC_DATA_IN_CODE);
+ cmd->set_cmdsize(sizeof(macho_linkedit_data_command<P>));
+ cmd->set_dataoff(_writer.dataInCodeSection->fileOffset);
+ cmd->set_datasize(_writer.dataInCodeSection->size);
+ return p + sizeof(macho_linkedit_data_command<P>);
+}
+
+
+template <typename A>
+uint8_t* HeaderAndLoadCommandsAtom<A>::copyDependentDRLoadCommand(uint8_t* p) const
+{
+ macho_linkedit_data_command<P>* cmd = (macho_linkedit_data_command<P>*)p;
+ cmd->set_cmd(LC_DYLIB_CODE_SIGN_DRS);
+ cmd->set_cmdsize(sizeof(macho_linkedit_data_command<P>));
+ cmd->set_dataoff(_writer.dependentDRsSection->fileOffset);
+ cmd->set_datasize(_writer.dependentDRsSection->size);
+ return p + sizeof(macho_linkedit_data_command<P>);
+}
+
+
template <typename A>
void HeaderAndLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
{
if ( _hasVersionLoadCommand )
p = this->copyVersionLoadCommand(p);
+ if ( _hasSourceVersionLoadCommand )
+ p = this->copySourceVersionLoadCommand(p);
+
if ( _hasThreadLoadCommand )
p = this->copyThreadsLoadCommand(p);
+
+ if ( _hasEntryPointLoadCommand )
+ p = this->copyEntryPointLoadCommand(p);
if ( _hasEncryptionLoadCommand )
p = this->copyEncryptionLoadCommand(p);
if ( _hasFunctionStartsLoadCommand )
p = this->copyFunctionStartsLoadCommand(p);
-
+
+ if ( _hasDataInCodeLoadCommand )
+ p = this->copyDataInCodeLoadCommand(p);
+
+ if ( _hasDependentDRInfo )
+ p = this->copyDependentDRLoadCommand(p);
+
}
#include <dlfcn.h>
#include <mach-o/dyld.h>
#include <mach-o/fat.h>
+#include <sys/sysctl.h>
+#include <libkern/OSAtomic.h>
#include <string>
#include <map>
#include "archive_file.h"
#include "lto_file.h"
#include "opaque_section_file.h"
+#include "Snapshot.h"
+const bool _s_logPThreads = false;
namespace ld {
namespace tool {
-
+class IgnoredFile : public ld::File {
+public:
+ IgnoredFile(const char* pth, time_t modTime, Ordinal ord, Type type) : ld::File(pth, modTime, ord, type) {};
+ virtual bool forEachAtom(AtomHandler&) const { return false; };
+ virtual bool justInTimeforEachAtom(const char* name, AtomHandler&) const { return false; };
+};
class DSOHandleAtom : public ld::Atom {
if ( strncmp((const char*)p, "!<arch>\n", 8) == 0 )
return "archive";
- return "unsupported file format";
+ char *unsupported = (char *)malloc(128);
+ strcpy(unsupported, "unsupported file format (");
+ for (unsigned i=0; i<len && i < 16; i++) {
+ char buf[8];
+ sprintf(buf, " 0x%2x", p[i]);
+ strcat(unsupported, buf);
+ }
+ strcat(unsupported, " )");
+ return unsupported;
}
// if fat file, skip to architecture we want
// Note: fat header is always big-endian
bool isFatFile = false;
+ uint32_t sliceToUse, sliceCount;
const fat_header* fh = (fat_header*)p;
if ( fh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
isFatFile = true;
const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header));
- uint32_t sliceToUse;
bool sliceFound = false;
+ sliceCount = OSSwapBigToHostInt32(fh->nfat_arch);
if ( _options.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) {
+ for (uint32_t i=0; i < sliceCount; ++i) {
if ( (OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)_options.architecture())
&& (OSSwapBigToHostInt32(archs[i].cpusubtype) == (uint32_t)_options.subArchitecture()) ) {
sliceToUse = i;
}
if ( !sliceFound ) {
// look for any slice that matches just cpu-type
- for (uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) {
+ for (uint32_t i=0; i < sliceCount; ++i) {
if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)_options.architecture() ) {
sliceToUse = i;
sliceFound = true;
objOpts.logAllFiles = _options.logAllFiles();
objOpts.convertUnwindInfo = _options.needsUnwindInfoSection();
objOpts.subType = _options.subArchitecture();
- ld::relocatable::File* objResult = mach_o::relocatable::parse(p, len, info.path, info.modTime, _nextInputOrdinal, objOpts);
- if ( objResult != NULL )
- return this->addObject(objResult, info, len);
+ ld::relocatable::File* objResult = mach_o::relocatable::parse(p, len, info.path, info.modTime, info.ordinal, objOpts);
+ if ( objResult != NULL ) {
+ OSAtomicAdd64(len, &_totalObjectSize);
+ OSAtomicIncrement32(&_totalObjectLoaded);
+ return objResult;
+ }
// see if it is an llvm object file
- objResult = lto::parse(p, len, info.path, info.modTime, _nextInputOrdinal, _options.architecture(), _options.subArchitecture(), _options.logAllFiles());
- if ( objResult != NULL )
- return this->addObject(objResult, info, len);
-
+ objResult = lto::parse(p, len, info.path, info.modTime, _options.architecture(), _options.subArchitecture(), _options.logAllFiles());
+ if ( objResult != NULL ) {
+ OSAtomicAdd64(len, &_totalObjectSize);
+ OSAtomicIncrement32(&_totalObjectLoaded);
+ return objResult;
+ }
+
// see if it is a dynamic library
- ld::dylib::File* dylibResult = mach_o::dylib::parse(p, len, info.path, info.modTime, _options, _nextInputOrdinal, info.options.fBundleLoader, indirectDylib);
- if ( dylibResult != NULL )
- return this->addDylib(dylibResult, info, len);
+ ld::dylib::File* dylibResult = mach_o::dylib::parse(p, len, info.path, info.modTime, _options, info.ordinal, info.options.fBundleLoader, indirectDylib);
+ if ( dylibResult != NULL ) {
+ return dylibResult;
+ }
// see if it is a static library
::archive::ParserOptions archOpts;
archOpts.objcABI2 = _options.objCABIVersion2POverride();
archOpts.verboseLoad = _options.whyLoad();
archOpts.logAllFiles = _options.logAllFiles();
- ld::archive::File* archiveResult = ::archive::parse(p, len, info.path, info.modTime, _nextInputOrdinal, archOpts);
+ ld::archive::File* archiveResult = ::archive::parse(p, len, info.path, info.modTime, info.ordinal, archOpts);
if ( archiveResult != NULL ) {
- // <rdar://problem/9740166> force loaded archives should be in LD_TRACE
- if ( (info.options.fForceLoad || _options.fullyLoadArchives()) && _options.traceArchives() )
- logArchive(archiveResult);
- return this->addArchive(archiveResult, info, len);
+ OSAtomicAdd64(len, &_totalArchiveSize);
+ OSAtomicIncrement32(&_totalArchivesLoaded);
+ return archiveResult;
}
// does not seem to be any valid linker input file, check LTO misconfiguration problems
if ( lto::archName((uint8_t*)p, len) != NULL ) {
if ( lto::libLTOisLoaded() ) {
- throwf("file was built for %s which is not the architecture being linked (%s)", fileArch(p, len), _options.architectureName());
+ throwf("lto file was built for %s which is not the architecture being linked (%s): %s", fileArch(p, len), _options.architectureName(), info.path);
}
else {
const char* libLTO = "libLTO.dylib";
char tmpPath[PATH_MAX];
char libLTOPath[PATH_MAX];
uint32_t bufSize = PATH_MAX;
- if ( _NSGetExecutablePath(ldPath, &bufSize) != -1 ) {
+ if ( _options.overridePathlibLTO() != NULL ) {
+ libLTO = _options.overridePathlibLTO();
+ }
+ else if ( _NSGetExecutablePath(ldPath, &bufSize) != -1 ) {
if ( realpath(ldPath, tmpPath) != NULL ) {
char* lastSlash = strrchr(tmpPath, '/');
if ( lastSlash != NULL )
// error handling
if ( ((fat_header*)p)->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
- throwf("missing required architecture %s in file", _options.architectureName());
+ throwf("missing required architecture %s in file %s (%u slices)", _options.architectureName(), info.path, sliceCount);
}
else {
if ( isFatFile )
- throwf("file is universal but does not contain a(n) %s slice", _options.architectureName());
+ throwf("file is universal (%u slices) but does not contain a(n) %s slice: %s", sliceCount, _options.architectureName(), info.path);
else
- throwf("file was built for %s which is not the architecture being linked (%s)", fileArch(p, len), _options.architectureName());
+ throwf("file was built for %s which is not the architecture being linked (%s): %s", fileArch(p, len), _options.architectureName(), info.path);
}
}
if ( strcmp(dit->installName,installPath) == 0 ) {
try {
Options::FileInfo info = _options.findFile(dit->useInstead);
+ _indirectDylibOrdinal = _indirectDylibOrdinal.nextIndirectDylibOrdinal();
+ info.ordinal = _indirectDylibOrdinal;
ld::File* reader = this->makeFile(info, true);
ld::dylib::File* dylibReader = dynamic_cast<ld::dylib::File*>(reader);
if ( dylibReader != NULL ) {
+ addDylib(dylibReader, info);
//_installPathToDylibs[strdup(installPath)] = dylibReader;
this->logDylib(dylibReader, true);
return dylibReader;
// note: @executable_path case is handled inside findFileUsingPaths()
// search for dylib using -F and -L paths
Options::FileInfo info = _options.findFileUsingPaths(installPath);
+ _indirectDylibOrdinal = _indirectDylibOrdinal.nextIndirectDylibOrdinal();
+ info.ordinal = _indirectDylibOrdinal;
try {
ld::File* reader = this->makeFile(info, true);
ld::dylib::File* dylibReader = dynamic_cast<ld::dylib::File*>(reader);
if ( dylibReader != NULL ) {
//assert(_installPathToDylibs.find(installPath) != _installPathToDylibs.end());
//_installPathToDylibs[strdup(installPath)] = dylibReader;
+ addDylib(dylibReader, info);
this->logDylib(dylibReader, true);
return dylibReader;
}
void InputFiles::createIndirectDylibs()
{
_allDirectDylibsLoaded = true;
-
+ _indirectDylibOrdinal = ld::File::Ordinal::indirectDylibBase();
+
// mark all dylibs initially specified as required and check if they can be used
for (InstallNameToDylib::iterator it=_installPathToDylibs.begin(); it != _installPathToDylibs.end(); it++) {
it->second->setExplicitlyLinked();
{
// extra command line section always at end
for (Options::ExtraSection::const_iterator it=_options.extraSectionsBegin(); it != _options.extraSectionsEnd(); ++it) {
- _inputFiles.push_back(opaque_section::parse(it->segmentName, it->sectionName, it->path, it->data,
- it->dataLen, _nextInputOrdinal));
- // bump ordinal
- _nextInputOrdinal++;
+ _inputFiles.push_back(opaque_section::parse(it->segmentName, it->sectionName, it->path, it->data, it->dataLen));
}
}
InputFiles::InputFiles(Options& opts, const char** archName)
: _totalObjectSize(0), _totalArchiveSize(0),
_totalObjectLoaded(0), _totalArchivesLoaded(0), _totalDylibsLoaded(0),
- _options(opts), _bundleLoader(NULL), _nextInputOrdinal(1),
- _allDirectDylibsLoaded(false), _inferredArch(false)
+ _options(opts), _bundleLoader(NULL),
+ _allDirectDylibsLoaded(false), _inferredArch(false), _fileMonitor(-1),
+ _exception(NULL)
{
// fStartCreateReadersTime = mach_absolute_time();
if ( opts.architecture() == 0 ) {
// command line missing -arch, so guess arch
inferArchitecture(opts, archName);
}
-
- const std::vector<Options::FileInfo>& files = opts.getInputFiles();
+#if HAVE_PTHREADS
+ pthread_mutex_init(&_parseLock, NULL);
+ pthread_cond_init(&_parseWorkReady, NULL);
+ pthread_cond_init(&_newFileAvailable, NULL);
+#endif
+ const std::vector<Options::FileInfo>& files = _options.getInputFiles();
if ( files.size() == 0 )
throw "no object files specified";
- // add all direct object, archives, and dylibs
+
_inputFiles.reserve(files.size());
+#if HAVE_PTHREADS
+ unsigned int inputFileSlot = 0;
+ _availableInputFiles = 0;
+ _parseCursor = 0;
+#endif
+ Options::FileInfo* entry;
for (std::vector<Options::FileInfo>::const_iterator it = files.begin(); it != files.end(); ++it) {
- const Options::FileInfo& entry = *it;
- try {
- _inputFiles.push_back(this->makeFile(entry, false));
- }
- catch (const char* msg) {
- if ( (strstr(msg, "architecture") != NULL) && !_options.errorOnOtherArchFiles() ) {
- if ( opts.ignoreOtherArchInputFiles() ) {
- // ignore, because this is about an architecture not in use
- }
- else {
- warning("ignoring file %s, %s", entry.path, msg);
- }
- }
- else {
- throwf("in %s, %s", entry.path, msg);
- }
- }
+ entry = (Options::FileInfo*)&(*it);
+#if HAVE_PTHREADS
+ // Assign input file slots to all the FileInfos.
+ // Also chain all FileInfos into one big list to set up for worker threads to do parsing.
+ entry->inputFileSlot = inputFileSlot;
+ entry->readyToParse = !entry->fromFileList || !_options.pipelineEnabled();
+ if (entry->readyToParse)
+ _availableInputFiles++;
+ _inputFiles.push_back(NULL);
+ inputFileSlot++;
+#else
+ // In the non-threaded case just parse the file now.
+ _inputFiles.push_back(makeFile(*entry, false));
+#endif
+ }
+
+#if HAVE_PTHREADS
+ _remainingInputFiles = files.size();
+
+ // initialize info for parsing input files on worker threads
+ unsigned int ncpus;
+ int mib[2];
+ size_t len = sizeof(ncpus);
+ mib[0] = CTL_HW;
+ mib[1] = HW_NCPU;
+ if (sysctl(mib, 2, &ncpus, &len, NULL, 0) != 0) {
+ ncpus = 1;
+ }
+ _availableWorkers = MIN(ncpus, files.size()); // max # workers we permit
+ _idleWorkers = 0;
+
+ if (_options.pipelineEnabled()) {
+ // start up a thread to listen for available input files
+ startThread(InputFiles::waitForInputFiles);
}
- this->createIndirectDylibs();
- this->createOpaqueFileSections();
+ // Start up one parser thread. More start on demand as parsed input files get consumed.
+ startThread(InputFiles::parseWorkerThread);
+ _availableWorkers--;
+#else
+ if (_options.pipelineEnabled()) {
+ throwf("pipelined linking not supported on this platform");
+ }
+#endif
}
-
-ld::File* InputFiles::addArchive(ld::File* reader, const Options::FileInfo& info, uint64_t mappedLen)
-{
- // bump ordinal
- _nextInputOrdinal += reader->subFileCount();
-
- // update stats
- _totalArchiveSize += mappedLen;
- _totalArchivesLoaded++;
- return reader;
+#if HAVE_PTHREADS
+void InputFiles::startThread(void (*threadFunc)(InputFiles *)) const {
+ pthread_t thread;
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ // set a nice big stack (same as main thread) because some code uses potentially large stack buffers
+ pthread_attr_setstacksize(&attr, 8 * 1024 * 1024);
+ pthread_create(&thread, &attr, (void *(*)(void*))threadFunc, (void *)this);
+ pthread_detach(thread);
+ pthread_attr_destroy(&attr);
}
+// Work loop for input file parsing threads
+void InputFiles::parseWorkerThread() {
+ ld::File *file;
+ const char *exception = NULL;
+ pthread_mutex_lock(&_parseLock);
+ const std::vector<Options::FileInfo>& files = _options.getInputFiles();
+ if (_s_logPThreads) printf("worker starting\n");
+ do {
+ if (_availableInputFiles == 0) {
+ _idleWorkers++;
+ pthread_cond_wait(&_parseWorkReady, &_parseLock);
+ _idleWorkers--;
+ } else {
+ int slot = _parseCursor;
+ while (slot < (int)files.size() && (_inputFiles[slot] != NULL || !files[slot].readyToParse))
+ slot++;
+ assert(slot < (int)files.size());
+ Options::FileInfo& entry = (Options::FileInfo&)files[slot];
+ _parseCursor = slot+1;
+ _availableInputFiles--;
+ entry.readyToParse = false; // to avoid multiple threads finding this file
+ pthread_mutex_unlock(&_parseLock);
+ if (_s_logPThreads) printf("parsing index %u\n", slot);
+ try {
+ file = makeFile(entry, false);
+ } catch (const char *msg) {
+ if ( (strstr(msg, "architecture") != NULL) && !_options.errorOnOtherArchFiles() ) {
+ if ( _options.ignoreOtherArchInputFiles() ) {
+ // ignore, because this is about an architecture not in use
+ }
+ else {
+ warning("ignoring file %s, %s", entry.path, msg);
+ }
+ } else {
+ exception = msg;
+ }
+ file = new IgnoredFile(entry.path, entry.modTime, entry.ordinal, ld::File::Other);
+ }
+ pthread_mutex_lock(&_parseLock);
+ if (_remainingInputFiles > 0)
+ _remainingInputFiles--;
+ if (_s_logPThreads) printf("done with index %u, %d remaining\n", slot, _remainingInputFiles);
+ if (exception) {
+ // We are about to die, so set to zero to stop other threads from doing unneeded work.
+ _remainingInputFiles = 0;
+ _exception = exception;
+ } else {
+ _inputFiles[slot] = file;
+ if (_neededFileSlot == slot)
+ pthread_cond_signal(&_newFileAvailable);
+ }
+ }
+ } while (_remainingInputFiles);
+ if (_s_logPThreads) printf("worker exiting\n");
+ pthread_cond_broadcast(&_parseWorkReady);
+ pthread_cond_signal(&_newFileAvailable);
+ pthread_mutex_unlock(&_parseLock);
+}
-ld::File* InputFiles::addObject(ld::relocatable::File* file, const Options::FileInfo& info, uint64_t mappedLen)
-{
- // bump ordinal
- _nextInputOrdinal++;
- // update stats
- _totalObjectSize += mappedLen;
- _totalObjectLoaded++;
- return file;
+void InputFiles::parseWorkerThread(InputFiles *inputFiles) {
+ inputFiles->parseWorkerThread();
}
+#endif
-ld::File* InputFiles::addDylib(ld::dylib::File* reader, const Options::FileInfo& info, uint64_t mappedLen)
+ld::File* InputFiles::addDylib(ld::dylib::File* reader, const Options::FileInfo& info)
{
_allDylibs.insert(reader);
}
}
}
- if ( !dylibOnCommandLineTwice && !isSymlink )
- warning("dylibs with same install name: %s and %s", pos->second->path(), reader->path());
+ // remove warning for <rdar://problem/10860629> Same install name for CoreServices and CFNetwork?
+ //if ( !dylibOnCommandLineTwice && !isSymlink )
+ // warning("dylibs with same install name: %s and %s", pos->second->path(), reader->path());
}
}
else if ( info.options.fBundleLoader )
if ( !_allDirectDylibsLoaded )
this->logDylib(reader, false);
- // bump ordinal
- _nextInputOrdinal++;
-
// update stats
_totalDylibsLoaded++;
+ _searchLibraries.push_back(LibraryInfo(reader));
return reader;
}
-bool InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler) const
+#if HAVE_PTHREADS
+// Called during pipelined linking to listen for available input files.
+// Available files are enqueued for parsing.
+void InputFiles::waitForInputFiles()
{
- bool didSomething = false;
- for (std::vector<ld::File*>::const_iterator it=_inputFiles.begin(); it != _inputFiles.end(); ++it) {
- didSomething |= (*it)->forEachAtom(handler);
- }
- if ( didSomething || true ) {
- switch ( _options.outputKind() ) {
- case Options::kStaticExecutable:
- case Options::kDynamicExecutable:
- // add implicit __dso_handle label
- handler.doAtom(DSOHandleAtom::_s_atomExecutable);
- handler.doAtom(DSOHandleAtom::_s_atomAll);
- if ( _options.pageZeroSize() != 0 )
- handler.doAtom(*new PageZeroAtom(_options.pageZeroSize()));
- if ( _options.hasCustomStack() )
- handler.doAtom(*new CustomStackAtom(_options.customStackSize()));
- break;
- case Options::kDynamicLibrary:
- // add implicit __dso_handle label
- handler.doAtom(DSOHandleAtom::_s_atomDylib);
- handler.doAtom(DSOHandleAtom::_s_atomAll);
- break;
- case Options::kDynamicBundle:
- // add implicit __dso_handle label
- handler.doAtom(DSOHandleAtom::_s_atomBundle);
- handler.doAtom(DSOHandleAtom::_s_atomAll);
- break;
- case Options::kDyld:
- // add implicit __dso_handle label
- handler.doAtom(DSOHandleAtom::_s_atomDyld);
- handler.doAtom(DSOHandleAtom::_s_atomAll);
- break;
- case Options::kPreload:
- // add implicit __mh_preload_header label
- handler.doAtom(DSOHandleAtom::_s_atomPreload);
- handler.doAtom(DSOHandleAtom::_s_atomAll);
- break;
- case Options::kObjectFile:
- handler.doAtom(DSOHandleAtom::_s_atomObjectFile);
- break;
- case Options::kKextBundle:
- // add implicit __dso_handle label
- handler.doAtom(DSOHandleAtom::_s_atomAll);
- break;
+ if (_s_logPThreads) printf("starting pipeline listener\n");
+ try {
+ const char *fifo = _options.pipelineFifo();
+ assert(fifo);
+ std::map<const char *, const Options::FileInfo*, strcompclass> fileMap;
+ const std::vector<Options::FileInfo>& files = _options.getInputFiles();
+ for (std::vector<Options::FileInfo>::const_iterator it = files.begin(); it != files.end(); ++it) {
+ const Options::FileInfo& entry = *it;
+ if (entry.fromFileList) {
+ fileMap[entry.path] = &entry;
+ }
+ }
+ FILE *fileStream = fopen(fifo, "r");
+ if (!fileStream)
+ throwf("pipelined linking error - failed to open stream. fopen() returns %s for \"%s\"\n", strerror(errno), fifo);
+ while (fileMap.size() > 0) {
+ char path_buf[PATH_MAX+1];
+ if (fgets(path_buf, PATH_MAX, fileStream) == NULL)
+ throwf("pipelined linking error - %lu missing input files", fileMap.size());
+ int len = strlen(path_buf);
+ if (path_buf[len-1] == '\n')
+ path_buf[len-1] = 0;
+ std::map<const char *, const Options::FileInfo*, strcompclass>::iterator it = fileMap.find(path_buf);
+ if (it == fileMap.end())
+ throwf("pipelined linking error - not in file list: %s\n", path_buf);
+ Options::FileInfo* inputInfo = (Options::FileInfo*)it->second;
+ if (!inputInfo->checkFileExists())
+ throwf("pipelined linking error - file does not exist: %s\n", inputInfo->path);
+ pthread_mutex_lock(&_parseLock);
+ if (_idleWorkers)
+ pthread_cond_signal(&_parseWorkReady);
+ inputInfo->readyToParse = true;
+ if (_parseCursor > inputInfo->inputFileSlot)
+ _parseCursor = inputInfo->inputFileSlot;
+ _availableInputFiles++;
+ if (_s_logPThreads) printf("pipeline listener: %s slot=%d, _parseCursor=%d, _availableInputFiles = %d remaining = %ld\n", path_buf, inputInfo->inputFileSlot, _parseCursor, _availableInputFiles, fileMap.size()-1);
+ pthread_mutex_unlock(&_parseLock);
+ fileMap.erase(it);
}
+ } catch (const char *msg) {
+ pthread_mutex_lock(&_parseLock);
+ _exception = msg;
+ pthread_cond_signal(&_newFileAvailable);
+ pthread_mutex_unlock(&_parseLock);
}
- return didSomething;
}
-bool InputFiles::searchLibraries(const char* name, bool searchDylibs, bool searchArchives, bool dataSymbolOnly, ld::File::AtomHandler& handler) const
+void InputFiles::waitForInputFiles(InputFiles *inputFiles) {
+ inputFiles->waitForInputFiles();
+}
+#endif
+
+
+void InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler)
{
- // check each input file
- for (std::vector<ld::File*>::const_iterator it=_inputFiles.begin(); it != _inputFiles.end(); ++it) {
- ld::File* file = *it;
- // 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.
- ld::dylib::File* dylibFile = dynamic_cast<ld::dylib::File*>(file);
- ld::archive::File* archiveFile = dynamic_cast<ld::archive::File*>(file);
- if ( searchDylibs && (dylibFile != NULL) ) {
- //fprintf(stderr, "searchLibraries(%s), looking in linked %s\n", name, dylibFile->path() );
- if ( dylibFile->justInTimeforEachAtom(name, handler) ) {
- // we found a definition in this dylib
- // done, unless it is a weak definition in which case we keep searching
- if ( !dylibFile->hasWeakExternals() || !dylibFile->hasWeakDefinition(name))
- return true;
- // else continue search for a non-weak definition
+ // add all direct object, archives, and dylibs
+ const std::vector<Options::FileInfo>& files = _options.getInputFiles();
+ size_t fileIndex;
+ for (fileIndex=0; fileIndex<_inputFiles.size(); fileIndex++) {
+ ld::File *file;
+#if HAVE_PTHREADS
+ pthread_mutex_lock(&_parseLock);
+
+ // this loop waits for the needed file to be ready (parsed by worker thread)
+ while (_inputFiles[fileIndex] == NULL && _exception == NULL) {
+ // We are starved for input. If there are still files to parse and we have
+ // not maxed out the worker thread count start a new worker thread.
+ if (_availableInputFiles > 0 && _availableWorkers > 0) {
+ if (_s_logPThreads) printf("starting worker\n");
+ startThread(InputFiles::parseWorkerThread);
+ _availableWorkers--;
}
+ _neededFileSlot = fileIndex;
+ if (_s_logPThreads) printf("consumer blocking for %lu: %s\n", fileIndex, files[fileIndex].path);
+ pthread_cond_wait(&_newFileAvailable, &_parseLock);
}
- else if ( searchArchives && (archiveFile != NULL) ) {
- if ( dataSymbolOnly ) {
- if ( archiveFile->justInTimeDataOnlyforEachAtom(name, handler) ) {
- if ( _options.traceArchives() )
- logArchive(file);
- // found data definition in static library, done
- return true;
- }
+
+ if (_exception)
+ throw _exception;
+
+ // The input file is parsed. Assimilate it and call its atom iterator.
+ if (_s_logPThreads) printf("consuming slot %lu\n", fileIndex);
+ file = _inputFiles[fileIndex];
+ pthread_mutex_unlock(&_parseLock);
+#else
+ file = _inputFiles[fileIndex];
+#endif
+ const Options::FileInfo& info = files[fileIndex];
+ switch (file->type()) {
+ case ld::File::Reloc:
+ {
+ ld::relocatable::File* reloc = (ld::relocatable::File*)file;
+ _options.snapshot().recordObjectFile(reloc->path());
}
- else {
- if ( archiveFile->justInTimeforEachAtom(name, handler) ) {
- if ( _options.traceArchives() )
- logArchive(file);
- // found definition in static library, done
- return true;
- }
+ break;
+ case ld::File::Dylib:
+ {
+ ld::dylib::File* dylib = (ld::dylib::File*)file;
+ addDylib(dylib, info);
}
+ break;
+ case ld::File::Archive:
+ {
+ ld::archive::File* archive = (ld::archive::File*)file;
+ // <rdar://problem/9740166> force loaded archives should be in LD_TRACE
+ if ( (info.options.fForceLoad || _options.fullyLoadArchives()) && _options.traceArchives() )
+ logArchive(archive);
+ _searchLibraries.push_back(LibraryInfo(archive));
+ }
+ break;
+ case ld::File::Other:
+ break;
+ default:
+ {
+ throwf("Unknown file type for %s", file->path());
+ }
+ break;
}
+ file->forEachAtom(handler);
}
+ createIndirectDylibs();
+ createOpaqueFileSections();
+
+ while (fileIndex < _inputFiles.size()) {
+ ld::File *file = _inputFiles[fileIndex];
+ file->forEachAtom(handler);
+ fileIndex++;
+ }
+
+ switch ( _options.outputKind() ) {
+ case Options::kStaticExecutable:
+ case Options::kDynamicExecutable:
+ // add implicit __dso_handle label
+ handler.doAtom(DSOHandleAtom::_s_atomExecutable);
+ handler.doAtom(DSOHandleAtom::_s_atomAll);
+ if ( _options.pageZeroSize() != 0 )
+ handler.doAtom(*new PageZeroAtom(_options.pageZeroSize()));
+ if ( _options.hasCustomStack() && !_options.needsEntryPointLoadCommand() )
+ handler.doAtom(*new CustomStackAtom(_options.customStackSize()));
+ break;
+ case Options::kDynamicLibrary:
+ // add implicit __dso_handle label
+ handler.doAtom(DSOHandleAtom::_s_atomDylib);
+ handler.doAtom(DSOHandleAtom::_s_atomAll);
+ break;
+ case Options::kDynamicBundle:
+ // add implicit __dso_handle label
+ handler.doAtom(DSOHandleAtom::_s_atomBundle);
+ handler.doAtom(DSOHandleAtom::_s_atomAll);
+ break;
+ case Options::kDyld:
+ // add implicit __dso_handle label
+ handler.doAtom(DSOHandleAtom::_s_atomDyld);
+ handler.doAtom(DSOHandleAtom::_s_atomAll);
+ break;
+ case Options::kPreload:
+ // add implicit __mh_preload_header label
+ handler.doAtom(DSOHandleAtom::_s_atomPreload);
+ handler.doAtom(DSOHandleAtom::_s_atomAll);
+ break;
+ case Options::kObjectFile:
+ handler.doAtom(DSOHandleAtom::_s_atomObjectFile);
+ break;
+ case Options::kKextBundle:
+ // add implicit __dso_handle label
+ handler.doAtom(DSOHandleAtom::_s_atomAll);
+ break;
+ }
+}
+
+
+bool InputFiles::searchLibraries(const char* name, bool searchDylibs, bool searchArchives, bool dataSymbolOnly, ld::File::AtomHandler& handler) const
+{
+ // Check each input library.
+ std::vector<LibraryInfo>::const_iterator libIterator = _searchLibraries.begin();
+
+
+ while (libIterator != _searchLibraries.end()) {
+ LibraryInfo lib = *libIterator;
+ if (lib.isDylib()) {
+ if (searchDylibs) {
+ ld::dylib::File *dylibFile = lib.dylib();
+ //fprintf(stderr, "searchLibraries(%s), looking in linked %s\n", name, dylibFile->path() );
+ if ( dylibFile->justInTimeforEachAtom(name, handler) ) {
+ // we found a definition in this dylib
+ // done, unless it is a weak definition in which case we keep searching
+ _options.snapshot().recordDylibSymbol(dylibFile, name);
+ if ( !dylibFile->hasWeakExternals() || !dylibFile->hasWeakDefinition(name)) {
+ return true;
+ }
+ // else continue search for a non-weak definition
+ }
+ }
+ } else {
+ if (searchArchives) {
+ ld::archive::File *archiveFile = lib.archive();
+ if ( dataSymbolOnly ) {
+ if ( archiveFile->justInTimeDataOnlyforEachAtom(name, handler) ) {
+ if ( _options.traceArchives() )
+ logArchive(archiveFile);
+ _options.snapshot().recordArchive(archiveFile->path());
+ // found data definition in static library, done
+ return true;
+ }
+ }
+ else {
+ if ( archiveFile->justInTimeforEachAtom(name, handler) ) {
+ if ( _options.traceArchives() )
+ logArchive(archiveFile);
+ _options.snapshot().recordArchive(archiveFile->path());
+ // found definition in static library, done
+ return true;
+ }
+ }
+ }
+ }
+ libIterator++;
+ }
+
// search indirect dylibs
if ( searchDylibs ) {
for (InstallNameToDylib::const_iterator it=_installPathToDylibs.begin(); it != _installPathToDylibs.end(); ++it) {
if ( dylibFile->justInTimeforEachAtom(name, handler) ) {
// we found a definition in this dylib
// done, unless it is a weak definition in which case we keep searching
- if ( !dylibFile->hasWeakExternals() || !dylibFile->hasWeakDefinition(name))
+ _options.snapshot().recordDylibSymbol(dylibFile, name);
+ if ( !dylibFile->hasWeakExternals() || !dylibFile->hasWeakDefinition(name)) {
return true;
+ }
// else continue search for a non-weak definition
}
}
}
return false;
}
+
+static bool vectorContains(const std::vector<ld::dylib::File*>& vec, ld::dylib::File* key)
+{
+ return std::find(vec.begin(), vec.end(), key) != vec.end();
+}
void InputFiles::dylibs(ld::Internal& state)
{
ld::dylib::File* dylibFile = dynamic_cast<ld::dylib::File*>(*it);
// only add dylibs that are not "blank" dylib stubs
if ( (dylibFile != NULL) && ((dylibFile->installPath() != NULL) || (dylibFile == _bundleLoader)) ) {
- if ( dylibsOK )
- state.dylibs.push_back(dylibFile);
+ if ( dylibsOK ) {
+ if ( ! vectorContains(state.dylibs, dylibFile) ) {
+ state.dylibs.push_back(dylibFile);
+ }
+ }
else
warning("unexpected dylib (%s) on link line", dylibFile->path());
}
if ( _options.nameSpace() == Options::kTwoLevelNameSpace ) {
for (InstallNameToDylib::const_iterator it=_installPathToDylibs.begin(); it != _installPathToDylibs.end(); ++it) {
ld::dylib::File* dylibFile = it->second;
- if ( dylibFile->implicitlyLinked() && dylibsOK )
- state.dylibs.push_back(dylibFile);
+ if ( dylibFile->implicitlyLinked() && dylibsOK ) {
+ if ( ! vectorContains(state.dylibs, dylibFile) ) {
+ state.dylibs.push_back(dylibFile);
+ }
+ }
}
}
+
+ //fprintf(stderr, "all dylibs:\n");
+ //for(std::vector<ld::dylib::File*>::iterator it=state.dylibs.begin(); it != state.dylibs.end(); ++it) {
+ // const ld::dylib::File* dylib = *it;
+ // fprintf(stderr, " %p %s\n", dylib, dylib->path());
+ //}
+
// and -bundle_loader
state.bundleLoader = _bundleLoader;
+
+ // <rdar://problem/10807040> give an error when -nostdlib is used and libSystem is missing
+ if ( (state.dylibs.size() == 0) && _options.needsEntryPointLoadCommand() )
+ throw "dynamic main executables must link with libSystem.dylib";
}
} // namespace tool
} // namespace ld
+
#ifndef __INPUT_FILES_H__
#define __INPUT_FILES_H__
+#define HAVE_PTHREADS 1
+
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <mach/mach_host.h>
#include <dlfcn.h>
#include <mach-o/dyld.h>
+#if HAVE_PTHREADS
+#include <pthread.h>
+#endif
#include <vector>
virtual ld::dylib::File* findDylib(const char* installPath, const char* fromPath);
// iterates all atoms in initial files
- bool forEachInitialAtom(ld::File::AtomHandler&) const;
+ void forEachInitialAtom(ld::File::AtomHandler&);
// searches libraries for name
bool searchLibraries(const char* name, bool searchDylibs, bool searchArchives,
bool dataSymbolOnly, ld::File::AtomHandler&) const;
bool inferredArch() const { return _inferredArch; }
- uint32_t nextInputOrdinal() const { return _nextInputOrdinal++; }
-
// for -print_statistics
- uint64_t _totalObjectSize;
- uint64_t _totalArchiveSize;
- uint32_t _totalObjectLoaded;
- uint32_t _totalArchivesLoaded;
- uint32_t _totalDylibsLoaded;
+ volatile int64_t _totalObjectSize;
+ volatile int64_t _totalArchiveSize;
+ volatile int32_t _totalObjectLoaded;
+ volatile int32_t _totalArchivesLoaded;
+ volatile int32_t _totalDylibsLoaded;
private:
void inferArchitecture(Options& opts, const char** archName);
const char* fileArch(const uint8_t* p, unsigned len);
ld::File* makeFile(const Options::FileInfo& info, bool indirectDylib);
- ld::File* addDylib(ld::dylib::File* f, const Options::FileInfo& info, uint64_t mappedLen);
- ld::File* addObject(ld::relocatable::File* f, const Options::FileInfo& info, uint64_t mappedLen);
- ld::File* addArchive(ld::File* f, const Options::FileInfo& info, uint64_t mappedLen);
+ ld::File* addDylib(ld::dylib::File* f, const Options::FileInfo& info);
void logTraceInfo (const char* format, ...) const;
void logDylib(ld::File*, bool indirect);
void logArchive(ld::File*) const;
void createIndirectDylibs();
void checkDylibClientRestrictions(ld::dylib::File*);
void createOpaqueFileSections();
+
+ // for pipelined linking
+ void waitForInputFiles();
+ static void waitForInputFiles(InputFiles *inputFiles);
+
+ // for threaded input file processing
+ void parseWorkerThread();
+ static void parseWorkerThread(InputFiles *inputFiles);
+ void startThread(void (*threadFunc)(InputFiles *)) const;
class CStringEquals {
public:
InstallNameToDylib _installPathToDylibs;
std::set<ld::dylib::File*> _allDylibs;
ld::dylib::File* _bundleLoader;
- mutable uint32_t _nextInputOrdinal;
bool _allDirectDylibsLoaded;
bool _inferredArch;
+ int _fileMonitor;
+ struct strcompclass {
+ bool operator() (const char *a, const char *b) const { return ::strcmp(a, b) < 0; }
+ };
+
+ // for threaded input file processing
+#if HAVE_PTHREADS
+ pthread_mutex_t _parseLock;
+ pthread_cond_t _parseWorkReady; // used by parse threads to block for work
+ pthread_cond_t _newFileAvailable; // used by main thread to block for parsed input files
+ int _availableWorkers; // number of remaining unstarted parse threads
+ int _idleWorkers; // number of running parse threads that are idle
+ int _neededFileSlot; // input file the resolver is currently blocked waiting for
+ int _parseCursor; // slot to begin searching for a file to parse
+ int _availableInputFiles; // number of input fileinfos with readyToParse==true
+#endif
+ const char * _exception; // passes an exception message from parse thread to main thread
+ int _remainingInputFiles; // number of input files still to parse
+
+ ld::File::Ordinal _indirectDylibOrdinal;
+
+ class LibraryInfo {
+ ld::File* _lib;
+ bool _isDylib;
+ public:
+ LibraryInfo(ld::dylib::File* dylib) : _lib(dylib), _isDylib(true) {};
+ LibraryInfo(ld::archive::File* dylib) : _lib(dylib), _isDylib(false) {};
+
+ bool isDylib() { return _isDylib; }
+ ld::dylib::File *dylib() { return (ld::dylib::File*)_lib; }
+ ld::archive::File *archive() { return (ld::archive::File*)_lib; }
+ };
+ std::vector<LibraryInfo> _searchLibraries;
};
} // namespace tool
#include "ld.hpp"
#include "Architectures.hpp"
#include "MachOFileAbstraction.hpp"
+#include "code-sign-blobs/superblob.h"
namespace ld {
namespace tool {
_64bitPointerLocations.clear();
}
-
template <typename A>
class FunctionStartsAtom : public LinkEditAtom
{
std::vector<const ld::Atom*>& atoms = sect->atoms;
for (std::vector<const ld::Atom*>::iterator ait = atoms.begin(); ait != atoms.end(); ++ait) {
const ld::Atom* atom = *ait;
+ // <rdar://problem/10422823> filter out zero-length atoms, so LC_FUNCTION_STARTS address can't spill into next section
+ if ( atom->size() == 0 )
+ continue;
uint64_t nextAddr = atom->finalAddress();
if ( atom->isThumb() )
nextAddr |= 1;
}
+// <rdar://problem/9218847> Need way to formalize data in code
+template <typename A>
+class DataInCodeAtom : public LinkEditAtom
+{
+public:
+ DataInCodeAtom(const Options& opts, ld::Internal& state, OutputFile& writer)
+ : LinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) { }
+
+ // overrides of ld::Atom
+ virtual const char* name() const { return "data-in-code info"; }
+ // overrides of LinkEditAtom
+ virtual void encode() const;
+
+private:
+ typedef typename A::P P;
+ typedef typename A::P::E E;
+ typedef typename A::P::uint_t pint_t;
+
+ struct FixupByAddressSorter
+ {
+ bool operator()(const ld::Fixup* left, const ld::Fixup* right)
+ {
+ return (left->offsetInAtom < right->offsetInAtom);
+ }
+ };
+
+ void encodeEntry(uint32_t startImageOffset, int len, ld::Fixup::Kind kind) const {
+ //fprintf(stderr, "encodeEntry(start=0x%08X, len=0x%04X, kind=%04X\n", startImageOffset, len, kind);
+ do {
+ macho_data_in_code_entry<P> entry;
+ entry.set_offset(startImageOffset);
+ entry.set_length(len);
+ switch ( kind ) {
+ case ld::Fixup::kindDataInCodeStartData:
+ entry.set_kind(1);
+ break;
+ case ld::Fixup::kindDataInCodeStartJT8:
+ entry.set_kind(2);
+ break;
+ case ld::Fixup::kindDataInCodeStartJT16:
+ entry.set_kind(3);
+ break;
+ case ld::Fixup::kindDataInCodeStartJT32:
+ entry.set_kind(4);
+ break;
+ case ld::Fixup::kindDataInCodeStartJTA32:
+ entry.set_kind(5);
+ break;
+ default:
+ assert(0 && "bad L$start$ label to encode");
+ }
+ uint8_t* bp = (uint8_t*)&entry;
+ this->_encodedData.append_byte(bp[0]);
+ this->_encodedData.append_byte(bp[1]);
+ this->_encodedData.append_byte(bp[2]);
+ this->_encodedData.append_byte(bp[3]);
+ this->_encodedData.append_byte(bp[4]);
+ this->_encodedData.append_byte(bp[5]);
+ this->_encodedData.append_byte(bp[6]);
+ this->_encodedData.append_byte(bp[7]);
+ // in rare case data range is huge, create multiple entries
+ len -= 0xFFF8;
+ startImageOffset += 0xFFF8;
+ } while ( len > 0 );
+ }
+
+ static ld::Section _s_section;
+};
+
+template <typename A>
+ld::Section DataInCodeAtom<A>::_s_section("__LINKEDIT", "__dataInCode", ld::Section::typeLinkEdit, true);
+
+
+template <typename A>
+void DataInCodeAtom<A>::encode() const
+{
+ if ( this->_writer.hasDataInCode ) {
+ uint64_t mhAddress = 0;
+ for (std::vector<ld::Internal::FinalSection*>::iterator sit = _state.sections.begin(); sit != _state.sections.end(); ++sit) {
+ ld::Internal::FinalSection* sect = *sit;
+ if ( sect->type() == ld::Section::typeMachHeader )
+ mhAddress = sect->address;
+ if ( sect->type() != ld::Section::typeCode )
+ continue;
+ for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
+ const ld::Atom* atom = *ait;
+ // gather all code-in-data labels
+ std::vector<const ld::Fixup*> dataInCodeLabels;
+ for (ld::Fixup::iterator fit = atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) {
+ switch ( fit->kind ) {
+ case ld::Fixup::kindDataInCodeStartData:
+ case ld::Fixup::kindDataInCodeStartJT8:
+ case ld::Fixup::kindDataInCodeStartJT16:
+ case ld::Fixup::kindDataInCodeStartJT32:
+ case ld::Fixup::kindDataInCodeStartJTA32:
+ case ld::Fixup::kindDataInCodeEnd:
+ dataInCodeLabels.push_back(fit);
+ break;
+ default:
+ break;
+ }
+ }
+ // to do: sort labels by address
+ std::sort(dataInCodeLabels.begin(), dataInCodeLabels.end(), FixupByAddressSorter());
+
+ // convert to array of struct data_in_code_entry
+ ld::Fixup::Kind prevKind = ld::Fixup::kindDataInCodeEnd;
+ uint32_t prevOffset = 0;
+ for ( std::vector<const ld::Fixup*>::iterator sfit = dataInCodeLabels.begin(); sfit != dataInCodeLabels.end(); ++sfit) {
+ if ( ((*sfit)->kind != prevKind) && (prevKind != ld::Fixup::kindDataInCodeEnd) ) {
+ int len = (*sfit)->offsetInAtom - prevOffset;
+ if ( len == 0 )
+ warning("multiple L$start$ labels found at same address in %s at offset 0x%04X", atom->name(), prevOffset);
+ this->encodeEntry(atom->finalAddress()+prevOffset-mhAddress, (*sfit)->offsetInAtom - prevOffset, prevKind);
+ }
+ prevKind = (*sfit)->kind;
+ prevOffset = (*sfit)->offsetInAtom;
+ }
+ if ( prevKind != ld::Fixup::kindDataInCodeEnd ) {
+ // add entry if function ends with data
+ this->encodeEntry(atom->finalAddress()+prevOffset-mhAddress, atom->size() - prevOffset, prevKind);
+ }
+ }
+ }
+ }
+
+ this->_encoded = true;
+}
+
+
+
+
+
+// <rdar://problem/7209249> linker needs to cache "Designated Requirements" in linked binary
+template <typename A>
+class DependentDRAtom : public LinkEditAtom
+{
+public:
+ DependentDRAtom(const Options& opts, ld::Internal& state, OutputFile& writer)
+ : LinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) { }
+
+ // overrides of ld::Atom
+ virtual const char* name() const { return "dependent dylib DR info"; }
+ // overrides of LinkEditAtom
+ virtual void encode() const;
+
+private:
+ typedef typename A::P P;
+ typedef typename A::P::E E;
+ typedef typename A::P::uint_t pint_t;
+
+ static ld::Section _s_section;
+
+};
+
+template <typename A>
+ld::Section DependentDRAtom<A>::_s_section("__LINKEDIT", "__dependentDR", ld::Section::typeLinkEdit, true);
+
+
+template <typename A>
+void DependentDRAtom<A>::encode() const
+{
+ Security::SuperBlobCore<Security::SuperBlob<Security::kSecCodeMagicDRList>, Security::kSecCodeMagicDRList, uint32_t>::Maker maker;
+
+ uint32_t index = 0;
+ for(std::vector<ld::dylib::File*>::iterator it=_state.dylibs.begin(); it != _state.dylibs.end(); ++it) {
+ const ld::dylib::File* dylib = *it;
+ Security::BlobCore* dylibDR = (Security::BlobCore*)dylib->codeSignatureDR();
+ void* dup = NULL;
+ if ( dylibDR != NULL ) {
+ // <rdar://problem/11315321> Maker takes ownership of every blob added
+ // We need to make a copy here because dylib still owns the pointer returned by codeSignatureDR()
+ dup = ::malloc(dylibDR->length());
+ ::memcpy(dup, dylibDR, dylibDR->length());
+ }
+ maker.add(index, (Security::BlobCore*)dup);
+ ++index;
+ }
+
+ Security::SuperBlob<Security::kSecCodeMagicDRList>* topBlob = maker.make();
+ const uint8_t* data = (uint8_t*)topBlob->data();
+ for(size_t i=0; i < topBlob->length(); ++i)
+ _encodedData.append_byte(data[i]);
+
+ this->_encoded = true;
+}
+
+
} // namespace tool
} // namespace ld
uint32_t stringOffsetForStab(const ld::relocatable::File::Stab& stab, StringPoolAtom* pool);
uint64_t valueForStab(const ld::relocatable::File::Stab& stab);
uint8_t sectionIndexForStab(const ld::relocatable::File::Stab& stab);
+ void addDataInCodeLabels(const ld::Atom* atom, uint32_t& symbolIndex);
mutable std::vector<macho_nlist<P> > _globals;
uint32_t _stabsIndexEnd;
static ld::Section _s_section;
+ static int _s_anonNameIndex;
+
};
template <typename A>
ld::Section SymbolTableAtom<A>::_s_section("__LINKEDIT", "__symbol_table", ld::Section::typeLinkEdit, true);
+template <typename A>
+int SymbolTableAtom<A>::_s_anonNameIndex = 1;
template <typename A>
bool SymbolTableAtom<A>::addLocal(const ld::Atom* atom, StringPoolAtom* pool)
{
macho_nlist<P> entry;
- static int s_anonNameIndex = 1;
assert(atom->symbolTableInclusion() != ld::Atom::symbolTableNotIn);
// set n_strx
if ( atom->combine() == ld::Atom::combineByNameAndContent ) {
// don't use 'l' labels for x86_64 strings
// <rdar://problem/6605499> x86_64 obj-c runtime confused when static lib is stripped
- sprintf(anonName, "LC%u", s_anonNameIndex++);
+ sprintf(anonName, "LC%u", _s_anonNameIndex++);
symbolName = anonName;
}
}
}
else if ( atom->symbolTableInclusion() == ld::Atom::symbolTableInWithRandomAutoStripLabel ) {
// make auto-strip anonymous name for symbol
- sprintf(anonName, "l%03u", s_anonNameIndex++);
+ sprintf(anonName, "l%03u", _s_anonNameIndex++);
symbolName = anonName;
}
}
macho_nlist<P> entry;
// set n_strx
- entry.set_n_strx(pool->add(atom->name()));
+ const char* symbolName = atom->name();
+ char anonName[32];
+ if ( this->_options.outputKind() == Options::kObjectFile ) {
+ if ( atom->symbolTableInclusion() == ld::Atom::symbolTableInWithRandomAutoStripLabel ) {
+ // make auto-strip anonymous name for symbol
+ sprintf(anonName, "l%03u", _s_anonNameIndex++);
+ symbolName = anonName;
+ }
+ }
+ entry.set_n_strx(pool->add(symbolName));
// set n_type
if ( atom->definition() == ld::Atom::definitionAbsolute ) {
entry.set_n_type(N_EXT | N_SECT | N_PEXT);
}
else if ( (atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip)
- && (atom->section().type() == ld::Section::typeMachHeader) ) {
- // the __mh_execute_header is historical magic and must be an absolute symbol
+ && (atom->section().type() == ld::Section::typeMachHeader)
+ && !_options.positionIndependentExecutable() ) {
+ // the __mh_execute_header is historical magic in non-pie executabls and must be an absolute symbol
entry.set_n_type(N_EXT | N_ABS);
}
}
return ( (_stabsIndexStart != _stabsIndexEnd) || (_stabsStringsOffsetStart != _stabsStringsOffsetEnd) );
}
+
+template <typename A>
+void SymbolTableAtom<A>::addDataInCodeLabels(const ld::Atom* atom, uint32_t& symbolIndex)
+{
+ char label[64];
+ for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
+ label[0] = '\0';
+ switch ( fit->kind ) {
+ case ld::Fixup::kindDataInCodeStartData:
+ sprintf(label, "L$start$data$%03u", symbolIndex);
+ break;
+ case ld::Fixup::kindDataInCodeStartJT8:
+ sprintf(label, "L$start$jt8$%03u", symbolIndex);
+ break;
+ case ld::Fixup::kindDataInCodeStartJT16:
+ sprintf(label, "L$start$jt16$%03u", symbolIndex);
+ break;
+ case ld::Fixup::kindDataInCodeStartJT32:
+ sprintf(label, "L$start$jt32$%03u", symbolIndex);
+ break;
+ case ld::Fixup::kindDataInCodeStartJTA32:
+ sprintf(label, "L$start$jta32$%03u", symbolIndex);
+ break;
+ case ld::Fixup::kindDataInCodeEnd:
+ sprintf(label, "L$start$code$%03u", symbolIndex);
+ break;
+ default:
+ break;
+ }
+ if ( label[0] != '\0' ) {
+ macho_nlist<P> entry;
+ entry.set_n_type(N_SECT);
+ entry.set_n_sect(atom->machoSection());
+ entry.set_n_desc(0);
+ entry.set_n_value(atom->finalAddress() + fit->offsetInAtom);
+ entry.set_n_strx(this->_writer._stringPoolAtom->add(label));
+ _locals.push_back(entry);
+ ++symbolIndex;
+ }
+ }
+}
+
+
template <typename A>
void SymbolTableAtom<A>::encode()
{
// make nlist entries for all local symbols
std::vector<const ld::Atom*>& localAtoms = this->_writer._localAtoms;
+ std::vector<const ld::Atom*>& globalAtoms = this->_writer._exportedAtoms;
_locals.reserve(localAtoms.size()+this->_state.stabs.size());
this->_writer._localSymbolsStartIndex = 0;
// make nlist entries for all debug notes
if ( this->addLocal(atom, this->_writer._stringPoolAtom) )
this->_writer._atomToSymbolIndex[atom] = symbolIndex++;
}
+ // <rdar://problem/9218847> recreate L$start$ labels in -r mode
+ if ( (_options.outputKind() == Options::kObjectFile) && this->_writer.hasDataInCode ) {
+ for (std::vector<const ld::Atom*>::const_iterator it=globalAtoms.begin(); it != globalAtoms.end(); ++it) {
+ this->addDataInCodeLabels(*it, symbolIndex);
+ }
+ for (std::vector<const ld::Atom*>::const_iterator it=localAtoms.begin(); it != localAtoms.end(); ++it) {
+ this->addDataInCodeLabels(*it, symbolIndex);
+ }
+ }
this->_writer._localSymbolsCount = symbolIndex;
// make nlist entries for all global symbols
- std::vector<const ld::Atom*>& globalAtoms = this->_writer._exportedAtoms;
_globals.reserve(globalAtoms.size());
this->_writer._globalSymbolsStartIndex = symbolIndex;
for (std::vector<const ld::Atom*>::const_iterator it=globalAtoms.begin(); it != globalAtoms.end(); ++it) {
return (_pointerLocations.size() + _callSiteLocations.size()) * sizeof(macho_relocation_info<P>);
}
+#if SUPPORT_ARCH_arm_any
template <> uint32_t ExternalRelocationsAtom<arm>::pointerReloc() { return ARM_RELOC_VANILLA; }
+#endif
template <> uint32_t ExternalRelocationsAtom<x86>::pointerReloc() { return GENERIC_RELOC_VANILLA; }
template <> uint32_t ExternalRelocationsAtom<x86_64>::pointerReloc() { return X86_64_RELOC_UNSIGNED; }
}
else {
// regular pointer
- if ( !external && (entry.toAddend != 0) ) {
- // use scattered reloc is target offset is non-zero
+ if ( !external && (entry.toAddend != 0) && (entry.toTarget->symbolTableInclusion() != ld::Atom::symbolTableNotIn) ) {
+ // use scattered reloc if target offset is non-zero into named atom (5658046)
sreloc1->set_r_scattered(true);
sreloc1->set_r_pcrel(false);
sreloc1->set_r_length(2);
}
+#if SUPPORT_ARCH_arm_any
template <>
void SectionRelocationsAtom<arm>::encodeSectionReloc(ld::Internal::FinalSection* sect,
const Entry& entry, std::vector<macho_relocation_info<P> >& relocs)
}
}
+#endif
template <typename A>
void IndirectSymbolTableAtom<A>::encode()
{
- // static executables should not have an indirect symbol table
- if ( this->_options.outputKind() == Options::kStaticExecutable )
+ // static executables should not have an indirect symbol table, unless PIE
+ if ( (this->_options.outputKind() == Options::kStaticExecutable) && !_options.positionIndependentExecutable() )
return;
// x86_64 kext bundles should not have an indirect symbol table
if ( (this->_options.outputKind() == Options::kKextBundle) && kextBundlesDontHaveIndirectSymbolTable() )
return;
+ // slidable static executables (-static -pie) should not have an indirect symbol table
+ if ( (this->_options.outputKind() == Options::kStaticExecutable) && this->_options.positionIndependentExecutable() )
+ return;
+
// find all special sections that need a range of the indirect symbol table section
for (std::vector<ld::Internal::FinalSection*>::iterator sit = this->_state.sections.begin(); sit != this->_state.sections.end(); ++sit) {
ld::Internal::FinalSection* sect = *sit;
#include <sys/types.h>
#include <sys/stat.h>
#include <mach/vm_prot.h>
+#include <sys/sysctl.h>
#include <mach-o/dyld.h>
#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <spawn.h>
+#include <cxxabi.h>
+#include <Availability.h>
#include <vector>
-#include "configure.h"
#include "Options.h"
#include "Architectures.hpp"
#include "MachOFileAbstraction.hpp"
+#include "Snapshot.h"
// upward dependency on lto::version()
namespace lto {
// magic to place command line in crash reports
const int crashreporterBufferSize = 2000;
-extern "C" char* __crashreporter_info__;
static char crashreporterBuffer[crashreporterBufferSize];
-char* __crashreporter_info__ = crashreporterBuffer;
+#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
+ #include <CrashReporterClient.h>
+ // hack until ld does not need to build on 10.6 anymore
+ struct crashreporter_annotations_t gCRAnnotations
+ __attribute__((section("__DATA," CRASHREPORTER_ANNOTATIONS_SECTION)))
+ = { CRASHREPORTER_ANNOTATIONS_VERSION, 0, 0, 0, 0, 0, 0 };
+#else
+ extern "C" char* __crashreporter_info__;
+ __attribute__((used))
+ char* __crashreporter_info__ = crashreporterBuffer;
+#endif
+
static bool sEmitWarnings = true;
static bool sFatalWarnings = false;
throw t;
}
+bool Options::FileInfo::checkFileExists(const char *p)
+{
+ struct stat statBuffer;
+ if (p == NULL) p = path;
+ if ( stat(p, &statBuffer) == 0 ) {
+ if (p != path) path = strdup(p);
+ fileLen = statBuffer.st_size;
+ modTime = statBuffer.st_mtime;
+ return true;
+ }
+ return false;
+}
+
Options::Options(int argc, const char* argv[])
: fOutputFile("a.out"), fArchitecture(0), fSubArchitecture(0), fArchitectureName("unknown"), fOutputKind(kDynamicExecutable),
fHasPreferredSubType(false), fArchSupportsThumb2(false), fPrebind(false), fBindAtLoad(false), fKeepPrivateExterns(false),
fClientName(NULL),
fUmbrellaName(NULL), fInitFunctionName(NULL), fDotOutputFile(NULL), fExecutablePath(NULL),
fBundleLoader(NULL), fDtraceScriptName(NULL), fSegAddrTablePath(NULL), fMapPath(NULL),
- fDyldInstallPath("/usr/lib/dyld"), fTempLtoObjectPath(NULL),
- fZeroPageSize(ULLONG_MAX), fStackSize(0), fStackAddr(0), fExecutableStack(false),
+ fDyldInstallPath("/usr/lib/dyld"), fTempLtoObjectPath(NULL), fOverridePathlibLTO(NULL),
+ fZeroPageSize(ULLONG_MAX), fStackSize(0), fStackAddr(0), fSourceVersion(0), fSDKVersion(0), fExecutableStack(false),
fNonExecutableHeap(false), fDisableNonExecutableHeap(false),
fMinimumHeaderPad(32), fSegmentAlignment(4096),
fCommonsMode(kCommonsIgnoreDylibs), fUUIDMode(kUUIDContent), fLocalSymbolHandling(kLocalSymbolsAll), fWarnCommons(false),
fSharedRegionEligible(false), fPrintOrderFileStatistics(false),
fReadOnlyx86Stubs(false), fPositionIndependentExecutable(false), fPIEOnCommandLine(false),
fDisablePositionIndependentExecutable(false), fMaxMinimumHeaderPad(false),
- fDeadStripDylibs(false), fAllowTextRelocs(false), fWarnTextRelocs(false),
+ fDeadStripDylibs(false), fAllowTextRelocs(false), fWarnTextRelocs(false), fKextsUseStubs(false),
fUsingLazyDylibLinking(false), fEncryptable(true),
fOrderData(true), fMarkDeadStrippableDylib(false),
fMakeCompressedDyldInfo(true), fMakeCompressedDyldInfoForceOff(false), fNoEHLabels(false),
fVersionLoadCommand(false), fVersionLoadCommandForcedOn(false),
fVersionLoadCommandForcedOff(false), fFunctionStartsLoadCommand(false),
fFunctionStartsForcedOn(false), fFunctionStartsForcedOff(false),
+ fDataInCodeInfoLoadCommand(false),
fCanReExportSymbols(false), fObjcCategoryMerging(true), fPageAlignDataAtoms(false),
+ fNeedsThreadLoadCommand(false), fEntryPointLoadCommand(false),
+ fEntryPointLoadCommandForceOn(false), fEntryPointLoadCommandForceOff(false),
+ fSourceVersionLoadCommand(false),
+ fSourceVersionLoadCommandForceOn(false), fSourceVersionLoadCommandForceOff(false),
+ fDependentDRInfo(false), fDependentDRInfoForcedOn(false), fDependentDRInfoForcedOff(false),
fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL),
fMacVersionMin(ld::macVersionUnset), fIOSVersionMin(ld::iOSVersionUnset),
- fSaveTempFiles(false)
+ fSaveTempFiles(false), fSnapshotRequested(false), fPipelineFifo(NULL)
{
this->checkForClassic(argc, argv);
this->parsePreCommandLineEnvironmentSettings();
{
}
-
bool Options::errorBecauseOfWarnings() const
{
return (sFatalWarnings && (sWarningsCount > 0));
void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype)
{
- fArchitecture = type;
- fSubArchitecture = subtype;
- switch ( type ) {
- case CPU_TYPE_I386:
- fArchitectureName = "i386";
- if ( (fMacVersionMin == ld::macVersionUnset) && (fOutputKind != Options::kObjectFile) ) {
- #ifdef DEFAULT_MACOSX_MIN_VERSION
- warning("-macosx_version_min not specificed, assuming " DEFAULT_MACOSX_MIN_VERSION);
- setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION);
- #else
- warning("-macosx_version_min not specificed, assuming 10.6");
- fMacVersionMin = ld::mac10_6;
- #endif
- }
- if ( !fMakeCompressedDyldInfo && (fMacVersionMin >= ld::mac10_6) && !fMakeCompressedDyldInfoForceOff )
- fMakeCompressedDyldInfo = true;
- break;
- case CPU_TYPE_X86_64:
- fArchitectureName = "x86_64";
- if ( (fMacVersionMin == ld::macVersionUnset) && (fOutputKind != Options::kObjectFile) ) {
- #ifdef DEFAULT_MACOSX_MIN_VERSION
- warning("-macosx_version_min not specificed, assuming " DEFAULT_MACOSX_MIN_VERSION);
- setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION);
- #else
- warning("-macosx_version_min not specificed, assuming 10.6");
- fMacVersionMin = ld::mac10_6;
- #endif
- }
- if ( !fMakeCompressedDyldInfo && (fMacVersionMin >= ld::mac10_6) && !fMakeCompressedDyldInfoForceOff )
- fMakeCompressedDyldInfo = true;
- break;
- case CPU_TYPE_ARM:
- fHasPreferredSubType = true;
- for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) {
- if ( t->subType == subtype ) {
- fArchitectureName = t->subTypeName;
- fArchSupportsThumb2 = t->supportsThumb2;
- break;
+ for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) {
+ if ( (type == t->cpuType) && (subtype == t->cpuSubType) ) {
+ fArchitecture = type;
+ fSubArchitecture = subtype;
+ fArchitectureName = t->archName;
+ fHasPreferredSubType = t->isSubType;
+ fArchSupportsThumb2 = t->supportsThumb2;
+ switch ( type ) {
+ case CPU_TYPE_I386:
+ case CPU_TYPE_X86_64:
+ if ( (fMacVersionMin == ld::macVersionUnset) && (fIOSVersionMin == ld::iOSVersionUnset) && (fOutputKind != Options::kObjectFile) ) {
+ #ifdef DEFAULT_MACOSX_MIN_VERSION
+ warning("-macosx_version_min not specified, assuming " DEFAULT_MACOSX_MIN_VERSION);
+ setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION);
+ #else
+ warning("-macosx_version_min not specified, assuming 10.6");
+ fMacVersionMin = ld::mac10_6;
+ #endif
+ }
+ if ( !fMakeCompressedDyldInfo && minOS(ld::mac10_6, ld::iOS_3_1) && !fMakeCompressedDyldInfoForceOff )
+ fMakeCompressedDyldInfo = true;
+ break;
+ case CPU_TYPE_ARM:
+ if ( (fMacVersionMin == ld::macVersionUnset) && (fIOSVersionMin == ld::iOSVersionUnset) && (fOutputKind != Options::kObjectFile) ) {
+ #if defined(DEFAULT_IPHONEOS_MIN_VERSION)
+ warning("-ios_version_min not specified, assuming " DEFAULT_IPHONEOS_MIN_VERSION);
+ setIOSVersionMin(DEFAULT_IPHONEOS_MIN_VERSION);
+ #elif defined(DEFAULT_MACOSX_MIN_VERSION)
+ warning("-macosx_version_min not specified, assuming " DEFAULT_MACOSX_MIN_VERSION);
+ setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION);
+ #else
+ warning("-macosx_version_min not specified, assuming 10.6");
+ fMacVersionMin = ld::mac10_6;
+ #endif
+ }
+ if ( !fMakeCompressedDyldInfo && minOS(ld::mac10_6, ld::iOS_3_1) && !fMakeCompressedDyldInfoForceOff )
+ fMakeCompressedDyldInfo = true;
+ break;
}
+ fLinkSnapshot.recordArch(fArchitectureName);
+ return;
}
- assert(fArchitectureName != NULL);
- if ( (fMacVersionMin == ld::macVersionUnset) && (fIOSVersionMin == ld::iOSVersionUnset) && (fOutputKind != Options::kObjectFile) ) {
-#if defined(DEFAULT_IPHONEOS_MIN_VERSION)
- warning("-ios_version_min not specificed, assuming " DEFAULT_IPHONEOS_MIN_VERSION);
- setIOSVersionMin(DEFAULT_IPHONEOS_MIN_VERSION);
-#elif defined(DEFAULT_MACOSX_MIN_VERSION)
- warning("-macosx_version_min not specificed, assuming " DEFAULT_MACOSX_MIN_VERSION);
- setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION);
-#else
- warning("-macosx_version_min not specificed, assuming 10.6");
- fMacVersionMin = ld::mac10_6;
-#endif
- }
- if ( !fMakeCompressedDyldInfo && minOS(ld::mac10_6, ld::iOS_3_1) && !fMakeCompressedDyldInfoForceOff )
- fMakeCompressedDyldInfo = true;
- break;
- default:
- fArchitectureName = "unknown architecture";
- break;
}
+ fArchitectureName = "unknown architecture";
}
void Options::parseArch(const char* arch)
{
if ( arch == NULL )
throw "-arch must be followed by an architecture string";
- fArchitectureName = arch;
- if ( strcmp(arch, "i386") == 0 ) {
- fArchitecture = CPU_TYPE_I386;
- fSubArchitecture = CPU_SUBTYPE_I386_ALL;
- }
- else if ( strcmp(arch, "x86_64") == 0 ) {
- fArchitecture = CPU_TYPE_X86_64;
- fSubArchitecture = CPU_SUBTYPE_X86_64_ALL;
- }
- else if ( strcmp(arch, "arm") == 0 ) {
- fArchitecture = CPU_TYPE_ARM;
- fSubArchitecture = CPU_SUBTYPE_ARM_ALL;
- }
- else {
- for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) {
- if ( strcmp(t->subTypeName,arch) == 0 ) {
- fArchitecture = CPU_TYPE_ARM;
- fSubArchitecture = t->subType;
- fArchSupportsThumb2 = t->supportsThumb2;
- fHasPreferredSubType = true;
- return;
- }
+ for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) {
+ if ( strcmp(t->archName,arch) == 0 ) {
+ fArchitectureName = arch;
+ fArchitecture = t->cpuType;
+ fSubArchitecture = t->cpuSubType;
+ fHasPreferredSubType = t->isSubType;
+ fArchSupportsThumb2 = t->supportsThumb2;
+ return;
}
- throwf("unknown/unsupported architecture name for: -arch %s", arch);
}
+ throwf("unknown/unsupported architecture name for: -arch %s", arch);
}
bool Options::checkForFile(const char* format, const char* dir, const char* rootName, FileInfo& result) const
{
- struct stat statBuffer;
char possiblePath[strlen(dir)+strlen(rootName)+strlen(format)+8];
sprintf(possiblePath, format, dir, rootName);
- bool found = (stat(possiblePath, &statBuffer) == 0);
+ bool found = result.checkFileExists(possiblePath);
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;
+ return found;
}
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++) {
strcat(possiblePath, suffix);
}
}
- bool found = (stat(possiblePath, &statBuffer) == 0);
+ FileInfo result;
+ bool found = result.checkFileExists(possiblePath);
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;
}
}
Options::FileInfo Options::findFile(const char* path) const
{
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) ) {
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;
+ if ( result.checkFileExists(possiblePath) ) {
return result;
}
}
}
// try raw path
- if ( stat(path, &statBuffer) == 0 ) {
- result.path = strdup(path);
- result.fileLen = statBuffer.st_size;
- result.modTime = statBuffer.st_mtime;
+ if ( result.checkFileExists(path) ) {
return result;
}
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;
+ if ( result.checkFileExists(newPath) ) {
return result;
}
}
fclose(file);
}
-void Options::loadFileList(const char* fileOfPaths)
+void Options::loadFileList(const char* fileOfPaths, ld::File::Ordinal baseOrdinal)
{
FILE* file;
const char* comma = strrchr(fileOfPaths, ',');
realFileOfPaths[realFileOfPathsLen] = '\0';
file = fopen(realFileOfPaths, "r");
if ( file == NULL )
- throwf("-filelist file not found: %s\n", realFileOfPaths);
+ throwf("-filelist file '%s' could not be opened, errno=%d (%s)\n", realFileOfPaths, errno, strerror(errno));
}
}
else {
file = fopen(fileOfPaths, "r");
if ( file == NULL )
- throwf("-filelist file not found: %s\n", fileOfPaths);
+ throwf("-filelist file '%s' could not be opened, errno=%d (%s)\n", fileOfPaths, errno, strerror(errno));
}
char path[PATH_MAX];
+ ld::File::Ordinal previousOrdinal = baseOrdinal;
while ( fgets(path, PATH_MAX, file) != NULL ) {
path[PATH_MAX-1] = '\0';
char* eol = strchr(path, '\n');
strcpy(builtPath, prefix);
strcat(builtPath, "/");
strcat(builtPath, path);
- fInputFiles.push_back(findFile(builtPath));
+ if (fPipelineFifo != NULL) {
+ FileInfo info = FileInfo(builtPath);
+ info.ordinal = previousOrdinal.nextFileListOrdinal();
+ previousOrdinal = info.ordinal;
+ info.fromFileList = true;
+ fInputFiles.push_back(info);
+ } else {
+ FileInfo info = findFile(builtPath);
+ info.ordinal = previousOrdinal.nextFileListOrdinal();
+ previousOrdinal = info.ordinal;
+ info.fromFileList = true;
+ fInputFiles.push_back(info);
+ }
}
else {
- fInputFiles.push_back(findFile(path));
+ if (fPipelineFifo != NULL) {
+ FileInfo info = FileInfo(path);
+ info.ordinal = previousOrdinal.nextFileListOrdinal();
+ previousOrdinal = info.ordinal;
+ info.fromFileList = true;
+ fInputFiles.push_back(info);
+ } else {
+ FileInfo info = findFile(path);
+ info.ordinal = previousOrdinal.nextFileListOrdinal();
+ previousOrdinal = info.ordinal;
+ info.fromFileList = true;
+ fInputFiles.push_back(info);
+ }
}
}
fclose(file);
//
void Options::parse(int argc, const char* argv[])
{
+ // Store the original args in the link snapshot.
+ fLinkSnapshot.recordRawArgs(argc, argv);
+
// pass one builds search list from -L and -F options
this->buildSearchPaths(argc, argv);
const char* arg = argv[i];
if ( arg[0] == '-' ) {
+ // by default, copy one arg to the snapshot link command, and do no file copying
+ int snapshotArgIndex = i;
+ int snapshotArgCount = -1; // -1 means compute count based on change in index
+ int snapshotFileArgIndex = -1; // -1 means no data file parameter to arg
// 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') ) {
+ snapshotArgCount = 0; // stripped out of link snapshot
if (arg[2] == '\0')
++i;
// previously handled by buildSearchPaths()
fOutputKind = kKextBundle;
}
else if ( strcmp(arg, "-o") == 0 ) {
+ snapshotArgCount = 0;
fOutputFile = argv[++i];
+ fLinkSnapshot.setSnapshotName(fOutputFile);
}
else if ( strncmp(arg, "-lazy-l", 7) == 0 ) {
+ snapshotArgCount = 0;
FileInfo info = findLibrary(&arg[7], true);
info.options.fLazyLoad = true;
+ info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
fUsingLazyDylibLinking = true;
}
+ else if ( strcmp(arg, "-lto_library") == 0 ) {
+ snapshotFileArgIndex = 1;
+ fOverridePathlibLTO = argv[++i];
+ if ( fOverridePathlibLTO == NULL )
+ throw "missing argument to -lto_library";
+ }
else if ( (arg[1] == 'l') && (strncmp(arg,"-lazy_",6) !=0) ) {
- addLibrary(findLibrary(&arg[2]));
+ snapshotArgCount = 0;
+ FileInfo info = findLibrary(&arg[2]);
+ info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
+ addLibrary(info);
}
// This causes a dylib to be weakly bound at
// link time. This corresponds to weak_import.
else if ( strncmp(arg, "-weak-l", 7) == 0 ) {
+ // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now)
+ snapshotArgCount = 0;
FileInfo info = findLibrary(&arg[7]);
info.options.fWeakImport = true;
+ info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
}
// Avoid lazy binding.
else if ( strcmp(arg, "-force_load") == 0 ) {
FileInfo info = findFile(argv[++i]);
info.options.fForceLoad = true;
+ info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
}
// Library versioning.
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>";
+ snapshotFileArgIndex = 3;
parseSectionOrderFile(argv[i+1], argv[i+2], argv[i+3]);
i += 3;
}
else if ( strcmp(arg, "-order_file") == 0 ) {
+ snapshotFileArgIndex = 1;
parseOrderFile(argv[++i], false);
}
else if ( strcmp(arg, "-order_file_statistics") == 0 ) {
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>";
+ snapshotFileArgIndex = 3;
addSection(argv[i+1], argv[i+2], argv[i+3]);
i += 3;
}
|| (strcmp(arg, "-dylinker_install_name") == 0)
|| (strcmp(arg, "-install_name") == 0)) {
fDylibInstallName = argv[++i];
- if ( fDylibInstallName == NULL )
+ if ( fDylibInstallName == NULL )
throw "-install_name missing <path>";
}
// Sets the base address of the output.
}
// Same as -@ from the FSF linker.
else if ( strcmp(arg, "-filelist") == 0 ) {
+ snapshotArgCount = 0;
const char* path = argv[++i];
if ( (path == NULL) || (path[0] == '-') )
throw "-filelist missing <path>";
- loadFileList(path);
+ ld::File::Ordinal baseOrdinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
+ loadFileList(path, baseOrdinal);
}
else if ( strcmp(arg, "-keep_private_externs") == 0 ) {
fKeepPrivateExterns = true;
}
}
else if ( strcmp(arg, "-interposable_list") == 0 ) {
+ snapshotFileArgIndex = 1;
fInterposeMode = kInterposeSome;
loadExportFile(argv[++i], "-interposable_list", fInterposeList);
}
fInterposeMode = kInterposeNone;
}
else if ( strcmp(arg, "-exported_symbols_list") == 0 ) {
+ snapshotFileArgIndex = 1;
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 ) {
+ snapshotFileArgIndex = 1;
if ( fExportMode == kExportSome )
throw "can't use -unexported_symbols_list and -exported_symbols_list";
fExportMode = kDontExportSome;
fDontExportSymbols.insert(argv[++i]);
}
else if ( strcmp(arg, "-non_global_symbols_no_strip_list") == 0 ) {
+ snapshotFileArgIndex = 1;
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 ) {
+ snapshotFileArgIndex = 1;
if ( fLocalSymbolHandling == kLocalSymbolsSelectiveInclude )
throw "can't use -non_global_symbols_no_strip_list and -non_global_symbols_strip_list";
fLocalSymbolHandling = kLocalSymbolsSelectiveExclude;
}
// Similar to -weak-l but uses the absolute path name to the library.
else if ( strcmp(arg, "-weak_library") == 0 ) {
+ // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now)
+ snapshotArgCount = 0;
FileInfo info = findFile(argv[++i]);
info.options.fWeakImport = true;
+ info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
}
else if ( strcmp(arg, "-lazy_library") == 0 ) {
+ // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now)
+ snapshotArgCount = 0;
FileInfo info = findFile(argv[++i]);
info.options.fLazyLoad = true;
+ info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
fUsingLazyDylibLinking = true;
}
else if ( strcmp(arg, "-framework") == 0 ) {
- addLibrary(findFramework(argv[++i]));
+ snapshotArgCount = 0;
+ FileInfo info = findFramework(argv[++i]);
+ info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
+ addLibrary(info);
}
else if ( strcmp(arg, "-weak_framework") == 0 ) {
+ // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now)
+ snapshotArgCount = 0;
FileInfo info = findFramework(argv[++i]);
info.options.fWeakImport = true;
+ info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
}
else if ( strcmp(arg, "-lazy_framework") == 0 ) {
+ // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now)
+ snapshotArgCount = 0;
FileInfo info = findFramework(argv[++i]);
info.options.fLazyLoad = true;
+ info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
fUsingLazyDylibLinking = true;
}
// previously handled by buildSearchPaths()
}
else if ( strcmp(arg, "-undefined") == 0 ) {
- setUndefinedTreatment(argv[++i]);
+ setUndefinedTreatment(argv[++i]);
}
// Debugging output flag.
else if ( strcmp(arg, "-arch_multiple") == 0 ) {
// Warn, error or make strong a mismatch between weak
// and non-weak references.
else if ( strcmp(arg, "-weak_reference_mismatches") == 0 ) {
- setWeakReferenceMismatchTreatment(argv[++i]);
+ setWeakReferenceMismatchTreatment(argv[++i]);
}
// For a deployment target of 10.3 and earlier ld64 will
// prebind an executable with 0s in all addresses that
// This should probably be deprecated when we respect -L and -F
// when searching for libraries.
else if ( strcmp(arg, "-dylib_file") == 0 ) {
+ // ignore for snapshot because a stub dylib will be created in the snapshot
+ snapshotArgCount = 0;
addDylibOverride(argv[++i]);
}
// What to expand @executable_path to if found in dependent dylibs
}
// ??? Deprecate when we get rid of basing at build time.
else if ( strcmp(arg, "-seg_addr_table") == 0 ) {
+ snapshotFileArgIndex = 1;
const char* name = argv[++i];
if ( name == NULL )
throw "-seg_addr_table missing argument";
i += 2;
}
else if ( strcmp(arg, "-bundle_loader") == 0 ) {
+ snapshotFileArgIndex = 1;
fBundleLoader = argv[++i];
if ( (fBundleLoader == NULL) || (fBundleLoader[0] == '-') )
throw "-bundle_loader missing <path>";
FileInfo info = findFile(fBundleLoader);
+ info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
info.options.fBundleLoader = true;
fInputFiles.push_back(info);
}
// previously handled by buildSearchPaths()
}
else if ( strcmp(arg, "-syslibroot") == 0 ) {
+ snapshotArgCount = 0;
++i;
// previously handled by buildSearchPaths()
}
fUUIDMode = kUUIDRandom;
}
else if ( strcmp(arg, "-dtrace") == 0 ) {
+ snapshotFileArgIndex = 1;
const char* name = argv[++i];
if ( name == NULL )
throw "-dtrace missing argument";
fAliases.push_back(pair);
}
else if ( strcmp(arg, "-alias_list") == 0 ) {
+ snapshotFileArgIndex = 1;
parseAliasFile(argv[++i]);
}
// put this last so that it does not interfer with other options starting with 'i'
fDisablePositionIndependentExecutable = true;
}
else if ( strncmp(arg, "-reexport-l", 11) == 0 ) {
+ // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now)
+ snapshotArgCount = 0;
FileInfo info = findLibrary(&arg[11], true);
info.options.fReExport = true;
+ info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
}
else if ( strcmp(arg, "-reexport_library") == 0 ) {
+ // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now)
+ snapshotArgCount = 0;
FileInfo info = findFile(argv[++i]);
info.options.fReExport = true;
+ info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
}
else if ( strcmp(arg, "-reexport_framework") == 0 ) {
+ // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now)
+ snapshotArgCount = 0;
FileInfo info = findFramework(argv[++i]);
info.options.fReExport = true;
+ info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
}
else if ( strncmp(arg, "-upward-l", 9) == 0 ) {
+ // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now)
+ snapshotArgCount = 0;
FileInfo info = findLibrary(&arg[9], true);
info.options.fUpward = true;
+ info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
}
else if ( strcmp(arg, "-upward_library") == 0 ) {
+ // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now)
+ snapshotArgCount = 0;
FileInfo info = findFile(argv[++i]);
info.options.fUpward = true;
+ info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
}
else if ( strcmp(arg, "-upward_framework") == 0 ) {
+ // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now)
+ snapshotArgCount = 0;
FileInfo info = findFramework(argv[++i]);
info.options.fUpward = true;
+ info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
}
else if ( strcmp(arg, "-dead_strip_dylibs") == 0 ) {
fMarkDeadStrippableDylib = true;
}
else if ( strcmp(arg, "-exported_symbols_order") == 0 ) {
+ snapshotFileArgIndex = 1;
loadSymbolOrderFile(argv[++i], fExportSymbolsOrder);
}
else if ( strcmp(arg, "-no_compact_linkedit") == 0 ) {
fFunctionStartsForcedOff = true;
fFunctionStartsForcedOn = false;
}
+ else if ( strcmp(arg, "-no_data_in_code_info") == 0 ) {
+ fDataInCodeInfoLoadCommand = false;
+ }
+ else if ( strcmp(arg, "-data_in_code_info") == 0 ) {
+ fDataInCodeInfoLoadCommand = true;
+ }
else if ( strcmp(arg, "-object_path_lto") == 0 ) {
fTempLtoObjectPath = argv[++i];
if ( fTempLtoObjectPath == NULL )
fObjcCategoryMerging = false;
}
else if ( strcmp(arg, "-force_symbols_weak_list") == 0 ) {
+ snapshotFileArgIndex = 1;
loadExportFile(argv[++i], "-force_symbols_weak_list", fForceWeakSymbols);
}
else if ( strcmp(arg, "-force_symbols_not_weak_list") == 0 ) {
+ snapshotFileArgIndex = 1;
loadExportFile(argv[++i], "-force_symbols_not_weak_list", fForceNotWeakSymbols);
}
else if ( strcmp(arg, "-force_symbol_weak") == 0 ) {
fForceNotWeakSymbols.insert(symbol);
}
else if ( strcmp(arg, "-reexported_symbols_list") == 0 ) {
+ snapshotFileArgIndex = 1;
if ( fExportMode == kExportSome )
throw "can't use -exported_symbols_list and -reexported_symbols_list";
loadExportFile(argv[++i], "-reexported_symbols_list", fReExportSymbols);
}
else if ( strcmp(arg, "-page_align_data_atoms") == 0 ) {
fPageAlignDataAtoms = true;
+ }
+ else if (strcmp(arg, "-debug_snapshot") == 0) {
+ fLinkSnapshot.setSnapshotMode(Snapshot::SNAPSHOT_DEBUG);
+ fSnapshotRequested = true;
+ }
+ else if ( strcmp(arg, "-new_main") == 0 ) {
+ fEntryPointLoadCommandForceOn = true;
+ }
+ else if ( strcmp(arg, "-no_new_main") == 0 ) {
+ fEntryPointLoadCommandForceOff = true;
+ }
+ else if ( strcmp(arg, "-source_version") == 0 ) {
+ const char* vers = argv[++i];
+ if ( vers == NULL )
+ throw "-source_version missing <version>";
+ fSourceVersion = parseVersionNumber64(vers);
+ }
+ else if ( strcmp(arg, "-add_source_version") == 0 ) {
+ fSourceVersionLoadCommandForceOn = true;
+ }
+ else if ( strcmp(arg, "-no_source_version") == 0 ) {
+ fSourceVersionLoadCommandForceOff = true;
+ }
+ else if ( strcmp(arg, "-sdk_version") == 0 ) {
+ const char* vers = argv[++i];
+ if ( vers == NULL )
+ throw "-sdk_version missing <version>";
+ fSDKVersion = parseVersionNumber32(vers);
+ }
+ else if ( strcmp(arg, "-dependent_dr_info") == 0 ) {
+ fDependentDRInfoForcedOn = true;
+ }
+ else if ( strcmp(arg, "-no_dependent_dr_info") == 0 ) {
+ fDependentDRInfoForcedOff = true;
+ }
+ else if ( strcmp(arg, "-kexts_use_stubs") == 0 ) {
+ fKextsUseStubs = true;
}
else {
throwf("unknown option: %s", arg);
}
+
+ if (snapshotArgCount == -1)
+ snapshotArgCount = i-snapshotArgIndex+1;
+ if (snapshotArgCount > 0)
+ fLinkSnapshot.addSnapshotLinkArg(snapshotArgIndex, snapshotArgCount, snapshotFileArgIndex);
}
else {
FileInfo info = findFile(arg);
+ info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
if ( strcmp(&info.path[strlen(info.path)-2], ".a") == 0 )
addLibrary(info);
else
// if a -lazy option was used, implicitly link in lazydylib1.o
if ( fUsingLazyDylibLinking ) {
- addLibrary(findLibrary("lazydylib1.o"));
+ FileInfo info = findLibrary("lazydylib1.o");
+ info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)argc);
+ addLibrary(info);
}
+
+ if (fSnapshotRequested)
+ fLinkSnapshot.createSnapshot();
}
fVerbose = true;
extern const char ldVersionString[];
fprintf(stderr, "%s", ldVersionString);
+ fprintf(stderr, "configured to support archs: %s\n", ALL_SUPPORTED_ARCHS);
// if only -v specified, exit cleanly
if ( argc == 2 ) {
const char* ltoVers = lto::version();
if ( ltoVers != NULL )
- fprintf(stderr, "%s\n", ltoVers);
+ fprintf(stderr, "LTO support using: %s\n", ltoVers);
exit(0);
}
}
const char* customDyldPath = getenv("LD_DYLD_PATH");
if ( customDyldPath != NULL )
fDyldInstallPath = customDyldPath;
+
+ const char* debugArchivePath = getenv("LD_DEBUG_SNAPSHOT");
+ if (debugArchivePath != NULL) {
+ fLinkSnapshot.setSnapshotMode(Snapshot::SNAPSHOT_DEBUG);
+ if (strlen(debugArchivePath) > 0)
+ fLinkSnapshot.setSnapshotPath(debugArchivePath);
+ fSnapshotRequested = true;
+ }
+
+ const char* pipeFdString = getenv("LD_PIPELINE_FIFO");
+ if (pipeFdString != NULL) {
+ fPipelineFifo = pipeFdString;
+ }
}
// allow build system to force on -warn_commons
if ( getenv("LD_WARN_COMMONS") != NULL )
fWarnCommons = true;
+
+ // allow B&I to set default -source_version
+ if ( fSourceVersion == 0 ) {
+ const char* vers = getenv("RC_ProjectSourceVersion");
+ if ( vers != NULL )
+ fSourceVersion = parseVersionNumber64(vers);
+ }
}
case CPU_TYPE_X86_64:
if ( (fOutputKind != Options::kObjectFile) && (fOutputKind != Options::kPreload) ) {
#ifdef DEFAULT_MACOSX_MIN_VERSION
- warning("-macosx_version_min not specificed, assuming " DEFAULT_MACOSX_MIN_VERSION);
+ warning("-macosx_version_min not specified, assuming " DEFAULT_MACOSX_MIN_VERSION);
setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION);
#else
- warning("-macosx_version_min not specificed, assuming 10.6");
+ warning("-macosx_version_min not specified, assuming 10.6");
fMacVersionMin = ld::mac10_6;
#endif
}
case CPU_TYPE_ARM:
if ( (fOutputKind != Options::kObjectFile) && (fOutputKind != Options::kPreload) ) {
#if defined(DEFAULT_IPHONEOS_MIN_VERSION)
- warning("-ios_version_min not specificed, assuming " DEFAULT_IPHONEOS_MIN_VERSION);
+ warning("-ios_version_min not specified, assuming " DEFAULT_IPHONEOS_MIN_VERSION);
setIOSVersionMin(DEFAULT_IPHONEOS_MIN_VERSION);
#elif defined(DEFAULT_MACOSX_MIN_VERSION)
- warning("-macosx_version_min not specificed, assuming " DEFAULT_MACOSX_MIN_VERSION);
+ warning("-macosx_version_min not specified, assuming " DEFAULT_MACOSX_MIN_VERSION);
setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION);
#else
- warning("-macosx_version_min not specificed, assuming 10.6");
+ warning("-macosx_version_min not specified, assuming 10.6");
fMacVersionMin = ld::mac10_6;
#endif
}
}
break;
case CPU_TYPE_X86_64:
- if ( fMacVersionMin < ld::mac10_4 ) {
+ if ( (fMacVersionMin < ld::mac10_4) && (fIOSVersionMin == ld::iOSVersionUnset) ) {
//warning("-macosx_version_min should be 10.4 or later for x86_64");
fMacVersionMin = ld::mac10_4;
}
// iOS 5.0 and later use new MH_KEXT_BUNDLE type
fMakeCompressedDyldInfo = false;
fMakeCompressedDyldInfoForceOff = true;
- fAllowTextRelocs = true;
+ fAllowTextRelocs = true;
+ fKextsUseStubs = !fAllowTextRelocs;
fUndefinedTreatment = kUndefinedDynamicLookup;
break;
}
// only use compressed LINKEDIT for:
- // x86_64 and i386 on Mac OS X 10.6 or later
- // arm on iPhoneOS 3.1 or later
+ // Mac OS X 10.6 or later
+ // iOS 3.1 or later
if ( fMakeCompressedDyldInfo ) {
- switch (fArchitecture) {
- case CPU_TYPE_I386:
- if ( fIOSVersionMin != ld::iOSVersionUnset ) // simulator always uses compressed LINKEDIT
- break;
- case CPU_TYPE_X86_64:
- if ( fMacVersionMin < ld::mac10_6 )
- fMakeCompressedDyldInfo = false;
- break;
- case CPU_TYPE_ARM:
- if ( !minOS(ld::mac10_6, ld::iOS_3_1) )
- fMakeCompressedDyldInfo = false;
- break;
- default:
- fMakeCompressedDyldInfo = false;
- }
+ if ( !minOS(ld::mac10_6, ld::iOS_3_1) )
+ fMakeCompressedDyldInfo = false;
}
-
+
// only ARM enforces that cpu-sub-types must match
if ( fArchitecture != CPU_TYPE_ARM )
fAllowCpuSubtypeMismatches = true;
// set fOutputSlidable
switch ( fOutputKind ) {
case Options::kObjectFile:
- case Options::kStaticExecutable:
fOutputSlidable = false;
break;
+ case Options::kStaticExecutable:
case Options::kDynamicExecutable:
fOutputSlidable = fPositionIndependentExecutable;
break;
switch ( fOutputKind ) {
case Options::kObjectFile:
fFunctionStartsLoadCommand = false;
+ fDataInCodeInfoLoadCommand = false;
break;
case Options::kPreload:
case Options::kStaticExecutable:
case Options::kKextBundle:
+ fDataInCodeInfoLoadCommand = false;
if ( fFunctionStartsForcedOn )
fFunctionStartsLoadCommand = true;
break;
// on the command line
if ( (fArchitecture == CPU_TYPE_I386) && (fOutputKind == kDynamicExecutable) && !fDisableNonExecutableHeap)
fNonExecutableHeap = true;
+
+ // Use LC_MAIN instead of LC_UNIXTHREAD for newer OSs
+ switch ( fOutputKind ) {
+ case Options::kDynamicExecutable:
+ if ( fEntryPointLoadCommandForceOn ) {
+ fEntryPointLoadCommand = true;
+ fEntryName = "_main";
+ }
+ else if ( fEntryPointLoadCommandForceOff ) {
+ fNeedsThreadLoadCommand = true;
+ }
+ else {
+ if ( minOS(ld::mac10_8, ld::iOS_Future) ) {
+ fEntryPointLoadCommand = true;
+ fEntryName = "_main";
+ }
+ else
+ fNeedsThreadLoadCommand = true;
+ }
+ break;
+ case Options::kObjectFile:
+ case Options::kKextBundle:
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ break;
+
+ case Options::kStaticExecutable:
+ case Options::kPreload:
+ case Options::kDyld:
+ fNeedsThreadLoadCommand = true;
+ break;
+ }
+
+ // add LC_SOURCE_VERSION
+ switch ( fOutputKind ) {
+ case Options::kDynamicExecutable:
+ case Options::kKextBundle:
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ case Options::kDyld:
+ case Options::kStaticExecutable:
+ if ( fSourceVersionLoadCommandForceOn ) {
+ fSourceVersionLoadCommand = true;
+ }
+ else if ( fSourceVersionLoadCommandForceOff ) {
+ fSourceVersionLoadCommand = false;
+ }
+ else {
+ if ( minOS(ld::mac10_8, ld::iOS_Future) ) {
+ fSourceVersionLoadCommand = true;
+ }
+ else
+ fSourceVersionLoadCommand = false;
+ }
+ break;
+ case Options::kObjectFile:
+ case Options::kPreload:
+ fSourceVersionLoadCommand = false;
+ break;
+ }
+
+
+ // add LC_DYLIB_CODE_SIGN_DRS
+ switch ( fOutputKind ) {
+ case Options::kDynamicExecutable:
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ if ( fDependentDRInfoForcedOn ) {
+ fDependentDRInfo = true;
+ }
+ else if ( fDependentDRInfoForcedOff ) {
+ fDependentDRInfo = false;
+ }
+ else {
+ if ( minOS(ld::mac10_8, ld::iOS_Future) )
+ fDependentDRInfo = true;
+ else
+ fDependentDRInfo = false;
+ }
+ break;
+ case Options::kKextBundle:
+ case Options::kDyld:
+ case Options::kStaticExecutable:
+ case Options::kObjectFile:
+ case Options::kPreload:
+ fDependentDRInfo = false;
+ break;
+ }
+
+ // if -sdk_version not on command line, infer from -syslibroot
+ if ( (fSDKVersion == 0) && (fSDKPaths.size() > 0) ) {
+ const char* sdkPath = fSDKPaths.front();
+ const char* end = &sdkPath[strlen(sdkPath)-1];
+ while ( !isdigit(*end) && (end > sdkPath) )
+ --end;
+ const char* start = end-1;
+ while ( (isdigit(*start) || (*start == '.')) && (start > sdkPath))
+ --start;
+ char sdkVersionStr[32];
+ int len = end-start+1;
+ if ( len > 2 ) {
+ strlcpy(sdkVersionStr, start+1, len);
+ fSDKVersion = parseVersionNumber32(sdkVersionStr);
+ }
+ }
+
+ // if -sdk_version and -syslibroot not used, but targeting MacOSX, use current OS version
+ if ( (fSDKVersion == 0) && (fMacVersionMin != ld::macVersionUnset) ) {
+ // special case if RC_ProjectName and MACOSX_DEPLOYMENT_TARGET are both set that sdkversion=minos
+ if ( getenv("RC_ProjectName") && getenv("MACOSX_DEPLOYMENT_TARGET") ) {
+ fSDKVersion = fMacVersionMin;
+ }
+ else {
+ int mib[2] = { CTL_KERN, KERN_OSRELEASE };
+ char kernVersStr[100];
+ size_t strlen = sizeof(kernVersStr);
+ if ( sysctl(mib, 2, kernVersStr, &strlen, NULL, 0) != -1 ) {
+ uint32_t kernVers = parseVersionNumber32(kernVersStr);
+ int minor = (kernVers >> 16) - 4; // kernel major version is 4 ahead of x in 10.x
+ fSDKVersion = 0x000A0000 + (minor << 8);
+ }
+ }
+ }
+
}
void Options::checkIllegalOptionCombinations()
if ( strcmp(&lastSlash[1], subUmbrella) == 0 ) {
info.options.fReExport = true;
found = true;
+ fLinkSnapshot.recordSubUmbrella(info.path);
break;
}
}
if ( strncmp(&lastSlash[1], subLibrary, dot-lastSlash-1) == 0 ) {
info.options.fReExport = true;
found = true;
+ fLinkSnapshot.recordSubLibrary(info.path);
break;
}
}
}
}
- // check -pie is only used when building a dynamic main executable for 10.5
if ( fPositionIndependentExecutable ) {
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
+ // check -pie is only used when building a dynamic main executable for 10.5
if ( !minOS(ld::mac10_5, ld::iOS_4_2) ) {
if ( fIOSVersionMin == ld::iOSVersionUnset )
throw "-pie can only be used when targeting Mac OS X 10.5 or later";
throw "-pie can only be used when targeting iOS 4.2 or later";
}
break;
+ case Options::kStaticExecutable:
case Options::kPreload:
+ // -pie is ok with -static or -preload
break;
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
warning("-pie being ignored. It is only used when linking a main executable");
fPositionIndependentExecutable = false;
break;
- case Options::kStaticExecutable:
case Options::kObjectFile:
case Options::kDyld:
case Options::kKextBundle:
bool newLinker = false;
// build command line buffer in case ld crashes
+#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
+ CRSetCrashLogMessage(crashreporterBuffer);
+#endif
const char* srcRoot = getenv("SRCROOT");
if ( srcRoot != NULL ) {
strlcpy(crashreporterBuffer, "SRCROOT=", crashreporterBufferSize);
}
}
}
-
- // -dtrace only supported by new linker
- if( dtraceFound )
- return;
-
- if( archFound ) {
- switch ( fArchitecture ) {
- case CPU_TYPE_I386:
- if ( (staticFound || kextFound) && !newLinker ) {
- // this environment variable will disable use of ld_classic for -static links
- if ( getenv("LD_NO_CLASSIC_LINKER_STATIC") == NULL ) {
- this->gotoClassicLinker(argc, argv);
- }
- }
- break;
- }
- }
- else {
- // work around for VSPTool
- if ( staticFound ) {
- warning("using ld_classic");
- this->gotoClassicLinker(argc, argv);
- }
- }
-
}
void Options::gotoClassicLinker(int argc, const char* argv[])
fprintf(stderr, "can't exec ld_classic\n");
exit(1);
}
+
+
+// Note, returned string buffer is own by this function.
+// It should not be freed
+// It will be reused, so clients need to strdup() if they want
+// to use it long term.
+const char* Options::demangleSymbol(const char* sym) const
+{
+ // only try to demangle symbols if -demangle on command line
+ if ( !fDemangle )
+ return sym;
+
+ // only try to demangle symbols that look like C++ symbols
+ if ( strncmp(sym, "__Z", 3) != 0 )
+ return sym;
+
+ static size_t size = 1024;
+ static char* buff = (char*)malloc(size);
+ int status;
+
+ char* result = abi::__cxa_demangle(&sym[1], buff, &size, &status);
+ if ( result != NULL ) {
+ // if demangling successful, keep buffer for next demangle
+ buff = result;
+ return buff;
+ }
+ return sym;
+}
+
#include <ext/hash_map>
#include "ld.hpp"
+#include "Snapshot.h"
extern void throwf (const char* format, ...) __attribute__ ((noreturn,format(printf, 1, 2)));
extern void warning(const char* format, ...) __attribute__((format(printf, 1, 2)));
+class Snapshot;
+
class LibraryOptions
{
public:
enum LocalSymbolHandling { kLocalSymbolsAll, kLocalSymbolsNone, kLocalSymbolsSelectiveInclude, kLocalSymbolsSelectiveExclude };
enum DebugInfoStripping { kDebugInfoNone, kDebugInfoMinimal, kDebugInfoFull };
- struct FileInfo {
+ class FileInfo {
+ public:
const char* path;
uint64_t fileLen;
time_t modTime;
LibraryOptions options;
- };
+ ld::File::Ordinal ordinal;
+ bool fromFileList;
+
+ // These are used by the threaded input file parsing engine.
+ mutable int inputFileSlot; // The input file "slot" assigned to this particular file
+ bool readyToParse;
+
+ // The use pattern for FileInfo is to create one on the stack in a leaf function and return
+ // it to the calling frame by copy. Therefore the copy constructor steals the path string from
+ // the source, which dies with the stack frame.
+ FileInfo(FileInfo const &other) : path(other.path), fileLen(other.fileLen), modTime(other.modTime), options(other.options), ordinal(other.ordinal), fromFileList(other.fromFileList), inputFileSlot(-1) { ((FileInfo&)other).path = NULL; };
+
+ // Create an empty FileInfo. The path can be set implicitly by checkFileExists().
+ FileInfo() : path(NULL), fileLen(0), modTime(0), options(), fromFileList(false) {};
+
+ // Create a FileInfo for a specific path, but does not stat the file.
+ FileInfo(const char *_path) : path(strdup(_path)), fileLen(0), modTime(0), options(), fromFileList(false) {};
+
+ ~FileInfo() { if (path) ::free((void*)path); }
+
+ // Stat the file and update fileLen and modTime.
+ // If the object already has a path the p must be NULL.
+ // If the object does not have a path then p can be any candidate path, and if the file exists the object permanently remembers the path.
+ // Returns true if the file exists, false if not.
+ bool checkFileExists(const char *p=NULL);
+
+ // Returns true if a previous call to checkFileExists() succeeded.
+ // Returns false if the file does not exist of checkFileExists() has never been called.
+ bool missing() const { return modTime==0; }
+};
struct ExtraSection {
const char* segmentName;
bool keepLocalSymbol(const char* symbolName) const;
bool allowTextRelocs() const { return fAllowTextRelocs; }
bool warnAboutTextRelocs() const { return fWarnTextRelocs; }
+ bool kextsUseStubs() const { return fKextsUseStubs; }
bool usingLazyDylibLinking() const { return fUsingLazyDylibLinking; }
bool verbose() const { return fVerbose; }
bool makeEncryptable() const { return fEncryptable; }
bool objcGc() const { return fObjCGc; }
bool objcGcOnly() const { return fObjCGcOnly; }
bool canUseThreadLocalVariables() const { return fTLVSupport; }
- bool demangleSymbols() const { return fDemangle; }
bool addVersionLoadCommand() const { return fVersionLoadCommand; }
bool addFunctionStarts() const { return fFunctionStartsLoadCommand; }
+ bool addDataInCodeInfo() const { return fDataInCodeInfoLoadCommand; }
bool canReExportSymbols() const { return fCanReExportSymbols; }
const char* tempLtoObjectPath() const { return fTempLtoObjectPath; }
+ const char* overridePathlibLTO() const { return fOverridePathlibLTO; }
bool objcCategoryMerging() const { return fObjcCategoryMerging; }
bool pageAlignDataAtoms() const { return fPageAlignDataAtoms; }
bool hasWeakBitTweaks() const;
bool forceNotWeak(const char* symbolName) const;
bool forceWeakNonWildCard(const char* symbolName) const;
bool forceNotWeakNonWildcard(const char* symbolName) const;
+ Snapshot& snapshot() const { return fLinkSnapshot; }
bool errorBecauseOfWarnings() const;
-
+ bool needsThreadLoadCommand() const { return fNeedsThreadLoadCommand; }
+ bool needsEntryPointLoadCommand() const { return fEntryPointLoadCommand; }
+ bool needsSourceVersionLoadCommand() const { return fSourceVersionLoadCommand; }
+ bool needsDependentDRInfo() const { return fDependentDRInfo; }
+ uint64_t sourceVersion() const { return fSourceVersion; }
+ uint32_t sdkVersion() const { return fSDKVersion; }
+ const char* demangleSymbol(const char* sym) const;
+ bool pipelineEnabled() const { return fPipelineFifo != NULL; }
+ const char* pipelineFifo() const { return fPipelineFifo; }
+
private:
class CStringEquals
{
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);
+ void loadFileList(const char* fileOfPaths, ld::File::Ordinal baseOrdinal);
uint64_t parseAddress(const char* addr);
void loadExportFile(const char* fileOfExports, const char* option, SetWithWildcards& set);
void parseAliasFile(const char* fileOfAliases);
const char* fMapPath;
const char* fDyldInstallPath;
const char* fTempLtoObjectPath;
+ const char* fOverridePathlibLTO;
uint64_t fZeroPageSize;
uint64_t fStackSize;
uint64_t fStackAddr;
+ uint64_t fSourceVersion;
+ uint32_t fSDKVersion;
bool fExecutableStack;
bool fNonExecutableHeap;
bool fDisableNonExecutableHeap;
bool fDeadStripDylibs;
bool fAllowTextRelocs;
bool fWarnTextRelocs;
+ bool fKextsUseStubs;
bool fUsingLazyDylibLinking;
bool fEncryptable;
bool fOrderData;
bool fFunctionStartsLoadCommand;
bool fFunctionStartsForcedOn;
bool fFunctionStartsForcedOff;
+ bool fDataInCodeInfoLoadCommand;
bool fCanReExportSymbols;
bool fObjcCategoryMerging;
bool fPageAlignDataAtoms;
+ bool fNeedsThreadLoadCommand;
+ bool fEntryPointLoadCommand;
+ bool fEntryPointLoadCommandForceOn;
+ bool fEntryPointLoadCommandForceOff;
+ bool fSourceVersionLoadCommand;
+ bool fSourceVersionLoadCommandForceOn;
+ bool fSourceVersionLoadCommandForceOff;
+ bool fDependentDRInfo;
+ bool fDependentDRInfoForcedOn;
+ bool fDependentDRInfoForcedOff;
DebugInfoStripping fDebugInfoStripping;
const char* fTraceOutputFile;
ld::MacVersionMin fMacVersionMin;
std::vector<const char*> fSDKPaths;
std::vector<const char*> fDyldEnvironExtras;
bool fSaveTempFiles;
+ mutable Snapshot fLinkSnapshot;
+ bool fSnapshotRequested;
+ const char * fPipelineFifo;
};
OutputFile::OutputFile(const Options& opts)
:
hasWeakExternalSymbols(false), usesWeakExternalSymbols(false), overridesWeakExternalSymbols(false),
- _noReExportedDylibs(false), hasThreadLocalVariableDefinitions(false), pieDisabled(false),
+ _noReExportedDylibs(false), hasThreadLocalVariableDefinitions(false), pieDisabled(false), hasDataInCode(false),
headerAndLoadCommandsSection(NULL),
rebaseSection(NULL), bindingSection(NULL), weakBindingSection(NULL),
lazyBindingSection(NULL), exportSection(NULL),
splitSegInfoSection(NULL), functionStartsSection(NULL),
+ dataInCodeSection(NULL), dependentDRsSection(NULL),
symbolTableSection(NULL), stringPoolSection(NULL),
localRelocationsSection(NULL), externalRelocationsSection(NULL),
sectionRelocationsSection(NULL),
_hasSectionRelocations(opts.outputKind() == Options::kObjectFile),
_hasSplitSegInfo(opts.sharedRegionEligible()),
_hasFunctionStartsInfo(opts.addFunctionStarts()),
+ _hasDataInCodeInfo(opts.addDataInCodeInfo()),
+ _hasDependentDRInfo(opts.needsDependentDRInfo()),
_hasDynamicSymbolTable(true),
_hasLocalRelocations(!opts.makeCompressedDyldInfo()),
_hasExternalRelocations(!opts.makeCompressedDyldInfo()),
_weakBindingInfoAtom(NULL),
_exportInfoAtom(NULL),
_splitSegInfoAtom(NULL),
- _functionStartsAtom(NULL)
+ _functionStartsAtom(NULL),
+ _dataInCodeAtom(NULL),
+ _dependentDRInfoAtom(NULL)
{
}
void OutputFile::assignAtomAddresses(ld::Internal& state)
{
+ const bool log = false;
+ if ( log ) fprintf(stderr, "assignAtomAddresses()\n");
for (std::vector<ld::Internal::FinalSection*>::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) {
ld::Internal::FinalSection* sect = *sit;
+ if ( log ) fprintf(stderr, " section=%s/%s\n", sect->segmentName(), sect->sectionName());
for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
const ld::Atom* atom = *ait;
+ if ( log ) fprintf(stderr, " atom=%p, name=%s\n", atom, atom->name());
switch ( sect-> type() ) {
case ld::Section::typeImportProxies:
// want finalAddress() of all proxy atoms to be zero
_functionStartsAtom->encode();
}
+ if ( _options.addDataInCodeInfo() ) {
+ // build data-in-code info
+ assert(_dataInCodeAtom != NULL);
+ _dataInCodeAtom->encode();
+ }
+
+ if ( _options.needsDependentDRInfo() ) {
+ // build dependent dylib DR info
+ assert(_dependentDRInfoAtom != NULL);
+ _dependentDRInfoAtom->encode();
+ }
+
// build classic symbol table
assert(_symbolTableAtom != NULL);
_symbolTableAtom->encode();
}
}
- if ( log ) fprintf(stderr, " address=0x%08llX, hidden=%d, alignment=%02d, padBytes=%d, section=%s,%s\n",
- sect->address, sect->isSectionHidden(), sect->alignment, sect->alignmentPaddingBytes,
+ if ( log ) fprintf(stderr, " address=0x%08llX, size=0x%08llX, hidden=%d, alignment=%02d, padBytes=%d, section=%s,%s\n",
+ sect->address, sect->size, sect->isSectionHidden(), sect->alignment, sect->alignmentPaddingBytes,
sect->segmentName(), sect->sectionName());
// update running totals
if ( !sect->isSectionHidden() || hiddenSectionsOccupyAddressSpace )
if ( hasZeroForFileOffset(sect) ) {
// fileoff of zerofill sections is moot, but historically it is set to zero
sect->fileOffset = 0;
+
+ // <rdar://problem/10445047> align file offset with address layout
+ fileOffset += sect->alignmentPaddingBytes;
}
else {
// page align file offset at start of each segment
return (*target)->finalAddress();
case ld::Fixup::bindingsIndirectlyBound:
*target = state.indirectBindingTable[fixup->u.bindingIndex];
+ #ifndef NDEBUG
+ if ( ! (*target)->finalAddressMode() ) {
+ throwf("reference to symbol (which has not been assigned an address) %s", (*target)->name());
+ }
+ #endif
return (*target)->finalAddress();
}
throw "unexpected binding";
assert(fit->binding == ld::Fixup::bindingDirectlyBound);
accumulator = this->lazyBindingInfoOffsetForLazyPointerAddress(fit->u.target->finalAddress());
break;
+ case ld::Fixup::kindDataInCodeStartData:
+ case ld::Fixup::kindDataInCodeStartJT8:
+ case ld::Fixup::kindDataInCodeStartJT16:
+ case ld::Fixup::kindDataInCodeStartJT32:
+ case ld::Fixup::kindDataInCodeStartJTA32:
+ case ld::Fixup::kindDataInCodeEnd:
+ break;
case ld::Fixup::kindStoreTargetAddressLittleEndian32:
accumulator = addressOf(state, fit, &toTarget);
thumbTarget = targetIsThumb(state, fit);
return false;
}
-
-void OutputFile::writeOutputFile(ld::Internal& state)
+void OutputFile::writeAtoms(ld::Internal& state, uint8_t* wholeBuffer)
{
- // for UNIX conformance, error if file exists and is not writable
- if ( (access(_options.outputFilePath(), F_OK) == 0) && (access(_options.outputFilePath(), W_OK) == -1) )
- throwf("can't write output file: %s", _options.outputFilePath());
-
- int permissions = 0777;
- if ( _options.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 __options.outputFilePath() 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)
- // Lastly, only delete existing file if it is a normal file (e.g. not /dev/null).
- struct stat stat_buf;
- if ( (stat(_options.outputFilePath(), &stat_buf) != -1) && (stat_buf.st_mode & S_IFREG) )
- (void)unlink(_options.outputFilePath());
-
- // try to allocate buffer for entire output file content
- uint8_t* wholeBuffer = (uint8_t*)calloc(_fileSize, 1);
- if ( wholeBuffer == NULL )
- throwf("can't create buffer of %llu bytes for output", _fileSize);
-
- if ( _options.UUIDMode() == Options::kUUIDRandom ) {
- uint8_t bits[16];
- ::uuid_generate_random(bits);
- _headersAndLoadCommandAtom->setUUID(bits);
- }
-
// have each atom write itself
uint64_t fileOffsetOfEndOfLastAtom = 0;
uint64_t mhAddress = 0;
}
}
}
+}
+
+
+void OutputFile::computeContentUUID(ld::Internal& state, uint8_t* wholeBuffer)
+{
+ const bool log = false;
+ if ( (_options.outputKind() != Options::kObjectFile) || state.someObjectFileHasDwarf ) {
+ uint8_t digest[CC_MD5_DIGEST_LENGTH];
+ uint32_t stabsStringsOffsetStart;
+ uint32_t tabsStringsOffsetEnd;
+ uint32_t stabsOffsetStart;
+ uint32_t stabsOffsetEnd;
+ if ( _symbolTableAtom->hasStabs(stabsStringsOffsetStart, tabsStringsOffsetEnd, stabsOffsetStart, stabsOffsetEnd) ) {
+ // find two areas of file that are stabs info and should not contribute to checksum
+ uint64_t stringPoolFileOffset = 0;
+ uint64_t symbolTableFileOffset = 0;
+ for (std::vector<ld::Internal::FinalSection*>::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) {
+ ld::Internal::FinalSection* sect = *sit;
+ if ( sect->type() == ld::Section::typeLinkEdit ) {
+ if ( strcmp(sect->sectionName(), "__string_pool") == 0 )
+ stringPoolFileOffset = sect->fileOffset;
+ else if ( strcmp(sect->sectionName(), "__symbol_table") == 0 )
+ symbolTableFileOffset = sect->fileOffset;
+ }
+ }
+ uint64_t firstStabNlistFileOffset = symbolTableFileOffset + stabsOffsetStart;
+ uint64_t lastStabNlistFileOffset = symbolTableFileOffset + stabsOffsetEnd;
+ uint64_t firstStabStringFileOffset = stringPoolFileOffset + stabsStringsOffsetStart;
+ uint64_t lastStabStringFileOffset = stringPoolFileOffset + tabsStringsOffsetEnd;
+ if ( log ) fprintf(stderr, "firstStabNlistFileOffset=0x%08llX\n", firstStabNlistFileOffset);
+ if ( log ) fprintf(stderr, "lastStabNlistFileOffset=0x%08llX\n", lastStabNlistFileOffset);
+ if ( log ) fprintf(stderr, "firstStabStringFileOffset=0x%08llX\n", firstStabStringFileOffset);
+ if ( log ) fprintf(stderr, "lastStabStringFileOffset=0x%08llX\n", lastStabStringFileOffset);
+ assert(firstStabNlistFileOffset <= firstStabStringFileOffset);
+
+ CC_MD5_CTX md5state;
+ CC_MD5_Init(&md5state);
+ // checksum everything up to first stabs nlist
+ if ( log ) fprintf(stderr, "checksum 0x%08X -> 0x%08llX\n", 0, firstStabNlistFileOffset);
+ CC_MD5_Update(&md5state, &wholeBuffer[0], firstStabNlistFileOffset);
+ // checkusm everything after last stabs nlist and up to first stabs string
+ if ( log ) fprintf(stderr, "checksum 0x%08llX -> 0x%08llX\n", lastStabNlistFileOffset, firstStabStringFileOffset);
+ CC_MD5_Update(&md5state, &wholeBuffer[lastStabNlistFileOffset], firstStabStringFileOffset-lastStabNlistFileOffset);
+ // checksum everything after last stabs string to end of file
+ if ( log ) fprintf(stderr, "checksum 0x%08llX -> 0x%08llX\n", lastStabStringFileOffset, _fileSize);
+ CC_MD5_Update(&md5state, &wholeBuffer[lastStabStringFileOffset], _fileSize-lastStabStringFileOffset);
+ CC_MD5_Final(digest, &md5state);
+ if ( log ) fprintf(stderr, "uuid=%02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X\n", digest[0], digest[1], digest[2],
+ digest[3], digest[4], digest[5], digest[6], digest[7]);
+ }
+ else {
+ CC_MD5(wholeBuffer, _fileSize, digest);
+ }
+ // <rdar://problem/6723729> LC_UUID uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats
+ digest[6] = ( digest[6] & 0x0F ) | ( 3 << 4 );
+ digest[8] = ( digest[8] & 0x3F ) | 0x80;
+ // update buffer with new UUID
+ _headersAndLoadCommandAtom->setUUID(digest);
+ _headersAndLoadCommandAtom->recopyUUIDCommand();
+ }
+}
- // compute UUID
- if ( _options.UUIDMode() == Options::kUUIDContent ) {
- const bool log = false;
- if ( (_options.outputKind() != Options::kObjectFile) || state.someObjectFileHasDwarf ) {
- uint8_t digest[CC_MD5_DIGEST_LENGTH];
- uint32_t stabsStringsOffsetStart;
- uint32_t tabsStringsOffsetEnd;
- uint32_t stabsOffsetStart;
- uint32_t stabsOffsetEnd;
- if ( _symbolTableAtom->hasStabs(stabsStringsOffsetStart, tabsStringsOffsetEnd, stabsOffsetStart, stabsOffsetEnd) ) {
- // find two areas of file that are stabs info and should not contribute to checksum
- uint64_t stringPoolFileOffset = 0;
- uint64_t symbolTableFileOffset = 0;
- for (std::vector<ld::Internal::FinalSection*>::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) {
- ld::Internal::FinalSection* sect = *sit;
- if ( sect->type() == ld::Section::typeLinkEdit ) {
- if ( strcmp(sect->sectionName(), "__string_pool") == 0 )
- stringPoolFileOffset = sect->fileOffset;
- else if ( strcmp(sect->sectionName(), "__symbol_table") == 0 )
- symbolTableFileOffset = sect->fileOffset;
- }
- }
- uint64_t firstStabNlistFileOffset = symbolTableFileOffset + stabsOffsetStart;
- uint64_t lastStabNlistFileOffset = symbolTableFileOffset + stabsOffsetEnd;
- uint64_t firstStabStringFileOffset = stringPoolFileOffset + stabsStringsOffsetStart;
- uint64_t lastStabStringFileOffset = stringPoolFileOffset + tabsStringsOffsetEnd;
- if ( log ) fprintf(stderr, "firstStabNlistFileOffset=0x%08llX\n", firstStabNlistFileOffset);
- if ( log ) fprintf(stderr, "lastStabNlistFileOffset=0x%08llX\n", lastStabNlistFileOffset);
- if ( log ) fprintf(stderr, "firstStabStringFileOffset=0x%08llX\n", firstStabStringFileOffset);
- if ( log ) fprintf(stderr, "lastStabStringFileOffset=0x%08llX\n", lastStabStringFileOffset);
- assert(firstStabNlistFileOffset <= firstStabStringFileOffset);
-
- CC_MD5_CTX md5state;
- CC_MD5_Init(&md5state);
- // checksum everything up to first stabs nlist
- if ( log ) fprintf(stderr, "checksum 0x%08X -> 0x%08llX\n", 0, firstStabNlistFileOffset);
- CC_MD5_Update(&md5state, &wholeBuffer[0], firstStabNlistFileOffset);
- // checkusm everything after last stabs nlist and up to first stabs string
- if ( log ) fprintf(stderr, "checksum 0x%08llX -> 0x%08llX\n", lastStabNlistFileOffset, firstStabStringFileOffset);
- CC_MD5_Update(&md5state, &wholeBuffer[lastStabNlistFileOffset], firstStabStringFileOffset-lastStabNlistFileOffset);
- // checksum everything after last stabs string to end of file
- if ( log ) fprintf(stderr, "checksum 0x%08llX -> 0x%08llX\n", lastStabStringFileOffset, _fileSize);
- CC_MD5_Update(&md5state, &wholeBuffer[lastStabStringFileOffset], _fileSize-lastStabStringFileOffset);
- CC_MD5_Final(digest, &md5state);
- if ( log ) fprintf(stderr, "uuid=%02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X\n", digest[0], digest[1], digest[2],
- digest[3], digest[4], digest[5], digest[6], digest[7]);
- }
- else {
- CC_MD5(wholeBuffer, _fileSize, digest);
- }
- // <rdar://problem/6723729> LC_UUID uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats
- digest[6] = ( digest[6] & 0x0F ) | ( 3 << 4 );
- digest[8] = ( digest[8] & 0x3F ) | 0x80;
- // update buffer with new UUID
- _headersAndLoadCommandAtom->setUUID(digest);
- _headersAndLoadCommandAtom->recopyUUIDCommand();
+
+void OutputFile::writeOutputFile(ld::Internal& state)
+{
+ // for UNIX conformance, error if file exists and is not writable
+ if ( (access(_options.outputFilePath(), F_OK) == 0) && (access(_options.outputFilePath(), W_OK) == -1) )
+ throwf("can't write output file: %s", _options.outputFilePath());
+
+ mode_t permissions = 0777;
+ if ( _options.outputKind() == Options::kObjectFile )
+ permissions = 0666;
+ mode_t umask = ::umask(0);
+ ::umask(umask); // put back the original umask
+ permissions &= ~umask;
+ // Calling unlink first assures the file is gone so that open creates it with correct permissions
+ // It also handles the case where __options.outputFilePath() 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)
+ // Lastly, only delete existing file if it is a normal file (e.g. not /dev/null).
+ struct stat stat_buf;
+ bool outputIsRegularFile = true;
+ if ( stat(_options.outputFilePath(), &stat_buf) != -1 ) {
+ if (stat_buf.st_mode & S_IFREG) {
+ (void)unlink(_options.outputFilePath());
+ } else {
+ outputIsRegularFile = false;
+ }
+ }
+
+ int fd;
+ // Construct a temporary path of the form {outputFilePath}.ld_XXXXXX
+ const char filenameTemplate[] = ".ld_XXXXXX";
+ char tmpOutput[PATH_MAX];
+ uint8_t *wholeBuffer;
+ if (outputIsRegularFile) {
+ strcpy(tmpOutput, _options.outputFilePath());
+ // If the path is too long to add a suffix for a temporary name then
+ // just fall back to using the output path.
+ if (strlen(tmpOutput)+strlen(filenameTemplate) < PATH_MAX) {
+ strcat(tmpOutput, filenameTemplate);
+ fd = mkstemp(tmpOutput);
+ } else {
+ fd = open(tmpOutput, O_RDWR|O_CREAT, permissions);
}
+ if ( fd == -1 )
+ throwf("can't open output file for writing: %s, errno=%d", tmpOutput, errno);
+ ftruncate(fd, _fileSize);
+
+ wholeBuffer = (uint8_t *)mmap(NULL, _fileSize, PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0);
+ if ( wholeBuffer == MAP_FAILED )
+ throwf("can't create buffer of %llu bytes for output", _fileSize);
+ } else {
+ fd = open(_options.outputFilePath(), O_WRONLY);
+ if ( fd == -1 )
+ throwf("can't open output file for writing: %s, errno=%d", _options.outputFilePath(), errno);
+ // try to allocate buffer for entire output file content
+ wholeBuffer = (uint8_t*)calloc(_fileSize, 1);
+ if ( wholeBuffer == NULL )
+ throwf("can't create buffer of %llu bytes for output", _fileSize);
+ }
+
+ if ( _options.UUIDMode() == Options::kUUIDRandom ) {
+ uint8_t bits[16];
+ ::uuid_generate_random(bits);
+ _headersAndLoadCommandAtom->setUUID(bits);
}
- // write whole output file in one chunk
- int fd = open(_options.outputFilePath(), O_CREAT | O_WRONLY | O_TRUNC, permissions);
- if ( fd == -1 )
- throwf("can't open output file for writing: %s, errno=%d", _options.outputFilePath(), errno);
- if ( ::pwrite(fd, wholeBuffer, _fileSize, 0) == -1 )
- throwf("can't write to output file: %s, errno=%d", _options.outputFilePath(), errno);
- close(fd);
- free(wholeBuffer);
+ writeAtoms(state, wholeBuffer);
+
+ // compute UUID
+ if ( _options.UUIDMode() == Options::kUUIDContent )
+ computeContentUUID(state, wholeBuffer);
+
+ if (outputIsRegularFile) {
+ if ( ::chmod(tmpOutput, permissions) == -1 ) {
+ unlink(tmpOutput);
+ throwf("can't set permissions on output file: %s, errno=%d", tmpOutput, errno);
+ }
+ if ( ::rename(tmpOutput, _options.outputFilePath()) == -1 && strcmp(tmpOutput, _options.outputFilePath()) != 0) {
+ unlink(tmpOutput);
+ throwf("can't move output file in place, errno=%d", errno);
+ }
+ } else {
+ if ( ::write(fd, wholeBuffer, _fileSize) == -1 ) {
+ throwf("can't write to output file: %s, errno=%d", _options.outputFilePath(), errno);
+ }
+ }
}
struct AtomByNameSorter
continue;
}
- // <rdar://problem/7977374> Add command line options to control symbol weak-def bit on exported symbols
- if ( _options.hasWeakBitTweaks() && (atom->definition() == ld::Atom::definitionRegular) ) {
- const char* name = atom->name();
- if ( atom->scope() == ld::Atom::scopeGlobal ) {
- if ( atom->combine() == ld::Atom::combineNever ) {
- if ( _options.forceWeak(name) )
- (const_cast<ld::Atom*>(atom))->setCombine(ld::Atom::combineByName);
- }
- else if ( atom->combine() == ld::Atom::combineByName ) {
- if ( _options.forceNotWeak(name) )
- (const_cast<ld::Atom*>(atom))->setCombine(ld::Atom::combineNever);
- }
- }
- else {
- if ( _options.forceWeakNonWildCard(name) )
- warning("cannot force to be weak, non-external symbol %s", name);
- else if ( _options.forceNotWeakNonWildcard(name) )
- warning("cannot force to be not-weak, non-external symbol %s", name);
- }
- }
-
switch ( atom->scope() ) {
case ld::Atom::scopeTranslationUnit:
if ( _options.keepLocalSymbol(atom->name()) ) {
void OutputFile::addPreloadLinkEdit(ld::Internal& state)
{
switch ( _options.architecture() ) {
+#if SUPPORT_ARCH_i386
case CPU_TYPE_I386:
if ( _hasLocalRelocations ) {
_localRelocsAtom = new LocalRelocationsAtom<x86>(_options, state, *this);
stringPoolSection = state.addAtom(*_stringPoolAtom);
}
break;
+#endif
+#if SUPPORT_ARCH_x86_64
case CPU_TYPE_X86_64:
if ( _hasLocalRelocations ) {
_localRelocsAtom = new LocalRelocationsAtom<x86_64>(_options, state, *this);
stringPoolSection = state.addAtom(*_stringPoolAtom);
}
break;
+#endif
+#if SUPPORT_ARCH_arm_any
case CPU_TYPE_ARM:
if ( _hasLocalRelocations ) {
_localRelocsAtom = new LocalRelocationsAtom<arm>(_options, state, *this);
stringPoolSection = state.addAtom(*_stringPoolAtom);
}
break;
+#endif
default:
throw "architecture not supported for -preload";
}
return addPreloadLinkEdit(state);
switch ( _options.architecture() ) {
+#if SUPPORT_ARCH_i386
case CPU_TYPE_I386:
if ( _hasSectionRelocations ) {
_sectionsRelocationsAtom = new SectionRelocationsAtom<x86>(_options, state, *this);
_functionStartsAtom = new FunctionStartsAtom<x86>(_options, state, *this);
functionStartsSection = state.addAtom(*_functionStartsAtom);
}
+ if ( _hasDataInCodeInfo ) {
+ _dataInCodeAtom = new DataInCodeAtom<x86>(_options, state, *this);
+ dataInCodeSection = state.addAtom(*_dataInCodeAtom);
+ }
+ if ( _hasDependentDRInfo ) {
+ _dependentDRInfoAtom = new DependentDRAtom<x86>(_options, state, *this);
+ dependentDRsSection = state.addAtom(*_dependentDRInfoAtom);
+ }
if ( _hasSymbolTable ) {
_symbolTableAtom = new SymbolTableAtom<x86>(_options, state, *this);
symbolTableSection = state.addAtom(*_symbolTableAtom);
stringPoolSection = state.addAtom(*_stringPoolAtom);
}
break;
+#endif
+#if SUPPORT_ARCH_x86_64
case CPU_TYPE_X86_64:
if ( _hasSectionRelocations ) {
_sectionsRelocationsAtom = new SectionRelocationsAtom<x86_64>(_options, state, *this);
_functionStartsAtom = new FunctionStartsAtom<x86_64>(_options, state, *this);
functionStartsSection = state.addAtom(*_functionStartsAtom);
}
+ if ( _hasDataInCodeInfo ) {
+ _dataInCodeAtom = new DataInCodeAtom<x86_64>(_options, state, *this);
+ dataInCodeSection = state.addAtom(*_dataInCodeAtom);
+ }
+ if ( _hasDependentDRInfo ) {
+ _dependentDRInfoAtom = new DependentDRAtom<x86_64>(_options, state, *this);
+ dependentDRsSection = state.addAtom(*_dependentDRInfoAtom);
+ }
if ( _hasSymbolTable ) {
_symbolTableAtom = new SymbolTableAtom<x86_64>(_options, state, *this);
symbolTableSection = state.addAtom(*_symbolTableAtom);
stringPoolSection = state.addAtom(*_stringPoolAtom);
}
break;
+#endif
+#if SUPPORT_ARCH_arm_any
case CPU_TYPE_ARM:
if ( _hasSectionRelocations ) {
_sectionsRelocationsAtom = new SectionRelocationsAtom<arm>(_options, state, *this);
_functionStartsAtom = new FunctionStartsAtom<arm>(_options, state, *this);
functionStartsSection = state.addAtom(*_functionStartsAtom);
}
+ if ( _hasDataInCodeInfo ) {
+ _dataInCodeAtom = new DataInCodeAtom<arm>(_options, state, *this);
+ dataInCodeSection = state.addAtom(*_dataInCodeAtom);
+ }
+ if ( _hasDependentDRInfo ) {
+ _dependentDRInfoAtom = new DependentDRAtom<arm>(_options, state, *this);
+ dependentDRsSection = state.addAtom(*_dependentDRInfoAtom);
+ }
if ( _hasSymbolTable ) {
_symbolTableAtom = new SymbolTableAtom<arm>(_options, state, *this);
symbolTableSection = state.addAtom(*_symbolTableAtom);
stringPoolSection = state.addAtom(*_stringPoolAtom);
}
break;
+#endif
default:
throw "unknown architecture";
}
void OutputFile::addLoadCommands(ld::Internal& state)
{
switch ( _options.architecture() ) {
+#if SUPPORT_ARCH_x86_64
case CPU_TYPE_X86_64:
_headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom<x86_64>(_options, state, *this);
headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom);
break;
+#endif
+#if SUPPORT_ARCH_arm_any
case CPU_TYPE_ARM:
_headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom<arm>(_options, state, *this);
headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom);
break;
+#endif
+#if SUPPORT_ARCH_i386
case CPU_TYPE_I386:
_headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom<x86>(_options, state, *this);
headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom);
break;
+#endif
default:
throw "unknown architecture";
}
}
assert(minusTarget != NULL);
break;
- default:
+ case ld::Fixup::kindDataInCodeStartData:
+ case ld::Fixup::kindDataInCodeStartJT8:
+ case ld::Fixup::kindDataInCodeStartJT16:
+ case ld::Fixup::kindDataInCodeStartJT32:
+ case ld::Fixup::kindDataInCodeStartJTA32:
+ case ld::Fixup::kindDataInCodeEnd:
+ hasDataInCode = true;
+ break;
+ default:
break;
}
if ( this->isStore(fit->kind) ) {
if ( _options.warnAboutTextRelocs() )
warning("text reloc in %s to %s", atom->name(), target->name());
}
- else if ( _options.positionIndependentExecutable() && ((_options.iOSVersionMin() >= ld::iOS_4_3) || (_options.macosxVersionMin() >= ld::mac10_7)) ) {
+ else if ( _options.positionIndependentExecutable() && (_options.outputKind() == Options::kDynamicExecutable)
+ && ((_options.iOSVersionMin() >= ld::iOS_4_3) || (_options.macosxVersionMin() >= ld::mac10_7)) ) {
if ( ! this->pieDisabled ) {
warning("PIE disabled. Absolute addressing (perhaps -mdynamic-no-pic) not allowed in code signed PIE, "
"but used in %s from %s. "
return;
}
// Have direct reference to weak-global. This should be an indrect reference
+ const char* demangledName = strdup(_options.demangleSymbol(atom->name()));
warning("direct access in %s to global weak symbol %s means the weak symbol cannot be overridden at runtime. "
"This was likely caused by different translation units being compiled with different visibility settings.",
- atom->name(), target->name());
+ demangledName, _options.demangleSymbol(target->name()));
}
return;
}
return;
}
// Have direct reference to weak-global. This should be an indrect reference
+ const char* demangledName = strdup(_options.demangleSymbol(atom->name()));
warning("direct access in %s to global weak symbol %s means the weak symbol cannot be overridden at runtime. "
"This was likely caused by different translation units being compiled with different visibility settings.",
- atom->name(), target->name());
+ demangledName, _options.demangleSymbol(target->name()));
}
return;
}
uint8_t rebaseType = REBASE_TYPE_POINTER;
uint8_t type = BIND_TYPE_POINTER;
const ld::dylib::File* dylib = dynamic_cast<const ld::dylib::File*>(target->file());
- bool weak_import = ((dylib != NULL) && (fixupWithTarget->weakImport || dylib->forcedWeakLinked()));
+ bool weak_import = (fixupWithTarget->weakImport || ((dylib != NULL) && dylib->forcedWeakLinked()));
uint64_t address = atom->finalAddress() + fixupWithTarget->offsetInAtom;
uint64_t addend = targetAddend - minusTargetAddend;
sect->hasLocalRelocs = true; // so dyld knows to change permissions on __TEXT segment
rebaseType = REBASE_TYPE_TEXT_ABSOLUTE32;
}
+ if ( (addend != 0) && _options.sharedRegionEligible() ) {
+ // make sure the addend does not cause the pointer to point outside the target's segment
+ // if it does, update_dyld_shared_cache will not be able to put this dylib into the shared cache
+ uint64_t targetAddress = target->finalAddress();
+ for (std::vector<ld::Internal::FinalSection*>::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) {
+ ld::Internal::FinalSection* sct = *sit;
+ uint64_t sctEnd = (sct->address+sct->size);
+ if ( (sct->address <= targetAddress) && (targetAddress < sctEnd) ) {
+ if ( (targetAddress+addend) > sctEnd ) {
+ warning("data symbol %s from %s has pointer to %s + 0x%08llX. "
+ "That large of an addend may disable %s from being put in the dyld shared cache.",
+ atom->name(), atom->file()->path(), target->name(), addend, _options.installPath() );
+ }
+ }
+ }
+ }
_rebaseInfo.push_back(RebaseInfo(rebaseType, address));
}
if ( needsBinding ) {
return;
// non-lazy-pointer section is encoded in indirect symbol table - not using relocations
- if ( (sect->type() == ld::Section::typeNonLazyPointer) && (_options.outputKind() != Options::kKextBundle) ) {
- assert(target != NULL);
- assert(fixupWithTarget != NULL);
- return;
+ if ( sect->type() == ld::Section::typeNonLazyPointer ) {
+ // except kexts and static pie which *do* use relocations
+ switch (_options.outputKind()) {
+ case Options::kKextBundle:
+ break;
+ case Options::kStaticExecutable:
+ if ( _options.positionIndependentExecutable() )
+ break;
+ // else fall into default case
+ default:
+ assert(target != NULL);
+ assert(fixupWithTarget != NULL);
+ return;
+ }
}
// no need to rebase or bind PCRel stores
// uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]);
//}
// write table of object files
- std::map<const ld::File*, uint32_t> readerToOrdinal;
- std::map<uint32_t, const ld::File*> ordinalToReader;
+ std::map<const ld::File*, ld::File::Ordinal> readerToOrdinal;
+ std::map<ld::File::Ordinal, const ld::File*> ordinalToReader;
std::map<const ld::File*, uint32_t> readerToFileOrdinal;
for (std::vector<ld::Internal::FinalSection*>::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) {
ld::Internal::FinalSection* sect = *sit;
const ld::File* reader = atom->file();
if ( reader == NULL )
continue;
- uint32_t readerOrdinal = reader->ordinal();
- std::map<const ld::File*, uint32_t>::iterator pos = readerToOrdinal.find(reader);
+ ld::File::Ordinal readerOrdinal = reader->ordinal();
+ std::map<const ld::File*, ld::File::Ordinal>::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[NULL] = fileIndex++;
- for(std::map<uint32_t, const ld::File*>::iterator it = ordinalToReader.begin(); it != ordinalToReader.end(); ++it) {
- if ( it->first != 0 ) {
- fprintf(mapFile, "[%3u] %s\n", fileIndex, it->second->path());
- readerToFileOrdinal[it->second] = fileIndex++;
- }
+ uint32_t fileIndex = 1;
+ for(std::map<ld::File::Ordinal, const ld::File*>::iterator it = ordinalToReader.begin(); it != ordinalToReader.end(); ++it) {
+ fprintf(mapFile, "[%3u] %s\n", fileIndex, it->second->path());
+ readerToFileOrdinal[it->second] = fileIndex++;
}
// write table of sections
fprintf(mapFile, "# Sections:\n");
bool operator()(const ld::Atom* left, const ld::Atom* right) const
{
// first sort by reader
- uint32_t leftFileOrdinal = left->file()->ordinal();
- uint32_t rightFileOrdinal = right->file()->ordinal();
+ ld::File::Ordinal leftFileOrdinal = left->file()->ordinal();
+ ld::File::Ordinal rightFileOrdinal = right->file()->ordinal();
if ( leftFileOrdinal!= rightFileOrdinal)
return (leftFileOrdinal < rightFileOrdinal);
const char* newDirPath;
const char* newFilename;
//fprintf(stderr, "debug note for %s\n", atom->name());
- if ( atom->translationUnitSource(&newDirPath, &newFilename) ) {
+ // guard against dwarf info that has no directory <rdar://problem/10991352>
+ if ( atom->translationUnitSource(&newDirPath, &newFilename) && (newDirPath != NULL)) {
// 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] != '/') )
+ size_t len = strlen(newDirPath);
+ if ( (newDirPath != NULL) && (len > 1 ) && (newDirPath[len-1] != '/') )
asprintf((char**)&newDirPath, "%s/", newDirPath);
if ( filename != NULL ) {
// translation unit change, emit ending SO
bool _noReExportedDylibs;
bool hasThreadLocalVariableDefinitions;
bool pieDisabled;
+ bool hasDataInCode;
ld::Internal::FinalSection* headerAndLoadCommandsSection;
ld::Internal::FinalSection* rebaseSection;
ld::Internal::FinalSection* bindingSection;
ld::Internal::FinalSection* exportSection;
ld::Internal::FinalSection* splitSegInfoSection;
ld::Internal::FinalSection* functionStartsSection;
+ ld::Internal::FinalSection* dataInCodeSection;
+ ld::Internal::FinalSection* dependentDRsSection;
ld::Internal::FinalSection* symbolTableSection;
ld::Internal::FinalSection* stringPoolSection;
ld::Internal::FinalSection* localRelocationsSection;
};
private:
+ void writeAtoms(ld::Internal& state, uint8_t* wholeBuffer);
+ void computeContentUUID(ld::Internal& state, uint8_t* wholeBuffer);
void buildDylibOrdinalMapping(ld::Internal&);
bool hasOrdinalForInstallPath(const char* path, int* ordinal);
void addLoadCommands(ld::Internal& state);
const bool _hasSectionRelocations;
const bool _hasSplitSegInfo;
const bool _hasFunctionStartsInfo;
+ const bool _hasDataInCodeInfo;
+ const bool _hasDependentDRInfo;
bool _hasDynamicSymbolTable;
bool _hasLocalRelocations;
bool _hasExternalRelocations;
class LinkEditAtom* _exportInfoAtom;
class LinkEditAtom* _splitSegInfoAtom;
class LinkEditAtom* _functionStartsAtom;
+ class LinkEditAtom* _dataInCodeAtom;
+ class LinkEditAtom* _dependentDRInfoAtom;
};
} // namespace tool
// each input files contributes initial atoms
_atoms.reserve(1024);
_inputFiles.forEachInitialAtom(*this);
+
_completedInitialObjectFiles = true;
//_symbolTable.printStatistics();
if ( objFile->debugInfo() == ld::relocatable::File::kDebugInfoDwarf )
_internal.someObjectFileHasDwarf = true;
- // remember if any objc classes built for fix-and-continue
- if ( objFile->objcReplacementClasses() )
- _internal.hasObjcReplacementClasses = true;
-
// remember if any .o file did not have MH_SUBSECTIONS_VIA_SYMBOLS bit set
if ( ! objFile->canScatterAtoms() )
_internal.allObjectFilesScatterable = false;
}
else if ( _options.outputKind() == Options::kDynamicLibrary ) {
if ( atom.file() != NULL )
- warning("target OS does not support re-exporting symbol %s from %s\n", SymbolTable::demangle(name), atom.file()->path());
+ warning("target OS does not support re-exporting symbol %s from %s\n", _options.demangleSymbol(name), atom.file()->path());
else
- warning("target OS does not support re-exporting symbol %s\n", SymbolTable::demangle(name));
+ warning("target OS does not support re-exporting symbol %s\n", _options.demangleSymbol(name));
}
}
else {
if ( atom.file() != NULL )
- warning("cannot export hidden symbol %s from %s", SymbolTable::demangle(name), atom.file()->path());
+ warning("cannot export hidden symbol %s from %s", _options.demangleSymbol(name), atom.file()->path());
else
- warning("cannot export hidden symbol %s", SymbolTable::demangle(name));
+ warning("cannot export hidden symbol %s", _options.demangleSymbol(name));
}
}
}
(const_cast<ld::Atom*>(&atom))->setScope(ld::Atom::scopeGlobal);
}
else {
- throwf("requested re-export symbol %s is not from a dylib, but from %s\n", SymbolTable::demangle(name), atom.file()->path());
+ throwf("requested re-export symbol %s is not from a dylib, but from %s\n", _options.demangleSymbol(name), atom.file()->path());
}
}
break;
//fprintf(stderr, "demote %s to hidden\n", name);
}
if ( _options.canReExportSymbols() && _options.shouldReExport(name) ) {
- throwf("requested re-export symbol %s is not from a dylib, but from %s\n", SymbolTable::demangle(name), atom.file()->path());
+ throwf("requested re-export symbol %s is not from a dylib, but from %s\n", _options.demangleSymbol(name), atom.file()->path());
}
break;
}
// remember if any atoms are proxies that require LTO
if ( atom.contentType() == ld::Atom::typeLTOtemporary )
_haveLLVMObjs = true;
-
- // if we've already partitioned into final sections, and lto needs a symbol very late, add it
- if ( _addToFinalSection )
- _internal.addAtom(atom);
-
+
if ( _options.deadCodeStrip() ) {
// add to set of dead-strip-roots, all symbols that the compiler marks as don't strip
if ( atom.dontDeadStrip() )
// now remove all non-live atoms from _atoms
const bool log = false;
if ( log ) {
- fprintf(stderr, "deadStripOptimize() all atoms with liveness:\n");
+ fprintf(stderr, "deadStripOptimize() all %ld atoms with liveness:\n", _atoms.size());
for (std::vector<const ld::Atom*>::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) {
- fprintf(stderr, " live=%d name=%s\n", (*it)->live(), (*it)->name());
+ const ld::File* file = (*it)->file();
+ fprintf(stderr, " live=%d atom=%p name=%s from=%s\n", (*it)->live(), *it, (*it)->name(), (file ? file->path() : "<internal>"));
}
}
else {
_atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), NotLive()), _atoms.end());
}
+
+ if ( log ) {
+ fprintf(stderr, "deadStripOptimize() %ld remaining atoms\n", _atoms.size());
+ for (std::vector<const ld::Atom*>::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) {
+ fprintf(stderr, " live=%d atom=%p name=%s\n", (*it)->live(), *it, (*it)->name());
+ }
+ }
}
+// This is called when LTO is used but -dead_strip is not used.
+// Some undefines were eliminated by LTO, but others were not.
+void Resolver::remainingUndefines(std::vector<const char*>& undefs)
+{
+ StringSet undefSet;
+ // search all atoms for references that are unbound
+ for (std::vector<const ld::Atom*>::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) {
+ const ld::Atom* atom = *it;
+ for (ld::Fixup::iterator fit=atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) {
+ switch ( (ld::Fixup::TargetBinding)fit->binding ) {
+ case ld::Fixup::bindingByNameUnbound:
+ assert(0 && "should not be by-name this late");
+ undefSet.insert(fit->u.name);
+ break;
+ case ld::Fixup::bindingsIndirectlyBound:
+ if ( _internal.indirectBindingTable[fit->u.bindingIndex] == NULL ) {
+ undefSet.insert(_symbolTable.indirectName(fit->u.bindingIndex));
+ }
+ break;
+ case ld::Fixup::bindingByContentBound:
+ case ld::Fixup::bindingNone:
+ case ld::Fixup::bindingDirectlyBound:
+ break;
+ }
+ }
+ }
+ // look for any initial undefines that are still undefined
+ for (Options::UndefinesIterator uit=_options.initialUndefinesBegin(); uit != _options.initialUndefinesEnd(); ++uit) {
+ if ( ! _symbolTable.hasName(*uit) ) {
+ undefSet.insert(*uit);
+ }
+ }
+
+ // copy set to vector
+ for (StringSet::const_iterator it=undefSet.begin(); it != undefSet.end(); ++it) {
+ fprintf(stderr, "undef: %s\n", *it);
+ undefs.push_back(*it);
+ }
+}
+
void Resolver::liveUndefines(std::vector<const char*>& undefs)
{
StringSet undefSet;
++foundReferenceCount;
}
else {
- fprintf(stderr, " %s in %s\n", SymbolTable::demangle(atom->name()), pathLeafName(atom->file()->path()));
+ fprintf(stderr, " %s in %s\n", _options.demangleSymbol(atom->name()), pathLeafName(atom->file()->path()));
++foundReferenceCount;
break; // if undefined used twice in a function, only show first
}
break;
}
std::vector<const char*> unresolvableUndefines;
- // <rdar://problem/10052396> LTO many have eliminated need for some undefines
- if ( _options.deadCodeStrip() || _haveLLVMObjs )
+ if ( _options.deadCodeStrip() )
this->liveUndefines(unresolvableUndefines);
+ else if( _haveLLVMObjs )
+ this->remainingUndefines(unresolvableUndefines); // <rdar://problem/10052396> LTO may have eliminated need for some undefines
else
_symbolTable.undefines(unresolvableUndefines);
for (int i=0; i < unresolvableCount; ++i) {
const char* name = unresolvableUndefines[i];
unsigned int slot = _symbolTable.findSlotForName(name);
- fprintf(stderr, " \"%s\", referenced from:\n", SymbolTable::demangle(name));
+ fprintf(stderr, " \"%s\", referenced from:\n", _options.demangleSymbol(name));
// scan all atoms for references
bool foundAtomReference = printReferencedBy(name, slot);
// scan command line options
else if ( _internal.indirectBindingTable[slot]->definition() == ld::Atom::definitionProxy ) {
if ( makingDylib )
throwf("-init function (%s) found in linked dylib, must be in dylib being linked", symbolName);
- else
- throwf("entry point (%s) found in linked dylib, must be in executable being linked", symbolName);
}
return _internal.indirectBindingTable[slot];
}
void Resolver::removeCoalescedAwayAtoms()
{
+ const bool log = false;
+ if ( log ) {
+ fprintf(stderr, "removeCoalescedAwayAtoms() starts with %lu atoms\n", _atoms.size());
+ }
_atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), AtomCoalescedAway()), _atoms.end());
+ if ( log ) {
+ fprintf(stderr, "removeCoalescedAwayAtoms() after removing coalesced atoms, %lu remain\n", _atoms.size());
+ for (std::vector<const ld::Atom*>::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) {
+ fprintf(stderr, " atom=%p %s\n", *it, (*it)->name());
+ }
+ }
}
void Resolver::linkTimeOptimize()
lto::OptimizeOptions optOpt;
optOpt.outputFilePath = _options.outputFilePath();
optOpt.tmpObjectFilePath = _options.tempLtoObjectPath();
- optOpt.allGlobalsAReDeadStripRoots = _options.allGlobalsAreDeadStripRoots();
+ optOpt.preserveAllGlobals = _options.allGlobalsAreDeadStripRoots() || _options.hasExportRestrictList();
optOpt.verbose = _options.verbose();
optOpt.saveTemps = _options.saveTempFiles();
optOpt.pie = _options.positionIndependentExecutable();
std::vector<const ld::Atom*> newAtoms;
std::vector<const char*> additionalUndefines;
- if ( ! lto::optimize(_atoms, _internal, _inputFiles.nextInputOrdinal(), optOpt, *this, newAtoms, additionalUndefines) )
+ if ( ! lto::optimize(_atoms, _internal, optOpt, *this, newAtoms, additionalUndefines) )
return; // if nothing done
// some atoms might have been optimized way (marked coalesced), remove them
this->removeCoalescedAwayAtoms();
-
- // add new atoms into their final section
- for (std::vector<const ld::Atom*>::iterator it = newAtoms.begin(); it != newAtoms.end(); ++it) {
- _internal.addAtom(**it);
- }
- // remove temp lto section and move all of its atoms to their final section
- ld::Internal::FinalSection* tempLTOsection = NULL;
- for (std::vector<ld::Internal::FinalSection*>::iterator sit=_internal.sections.begin(); sit != _internal.sections.end(); ++sit) {
- ld::Internal::FinalSection* sect = *sit;
- if ( sect->type() == ld::Section::typeTempLTO ) {
- tempLTOsection = sect;
- // remove temp lto section from final image
- _internal.sections.erase(sit);
- break;
- }
- }
- // lto atoms now have proper section info, so add to final section
- if ( tempLTOsection != NULL ) {
- for (std::vector<const ld::Atom*>::iterator ait=tempLTOsection->atoms.begin(); ait != tempLTOsection->atoms.end(); ++ait) {
- const ld::Atom* atom = *ait;
- if ( ! atom->coalescedAway() ) {
- this->convertReferencesToIndirect(*atom);
- _internal.addAtom(*atom);
- }
- }
- }
-
+ // run through all atoms again and make sure newly codegened atoms have refernces bound
+ for (std::vector<const ld::Atom*>::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it)
+ this->convertReferencesToIndirect(**it);
+
// resolve new undefines (e.g calls to _malloc and _memcpy that llvm compiler conjures up)
- _addToFinalSection = true;
for(std::vector<const char*>::iterator uit = additionalUndefines.begin(); uit != additionalUndefines.end(); ++uit) {
const char *targetName = *uit;
// these symbols may or may not already be in linker's symbol table
_inputFiles.searchLibraries(targetName, true, true, false, *this);
}
}
- _addToFinalSection = false;
// if -dead_strip on command line
if ( _options.deadCodeStrip() ) {
}
// and re-compute dead code
this->deadStripOptimize();
-
- // remove newly dead atoms from each section
- for (std::vector<ld::Internal::FinalSection*>::iterator sit=_internal.sections.begin(); sit != _internal.sections.end(); ++sit) {
- ld::Internal::FinalSection* sect = *sit;
- sect->atoms.erase(std::remove_if(sect->atoms.begin(), sect->atoms.end(), NotLive()), sect->atoms.end());
- }
}
if ( _options.outputKind() == Options::kObjectFile ) {
// if -r mode, add proxies for new undefines (e.g. ___stack_chk_fail)
- _addToFinalSection = true;
this->resolveUndefines();
- _addToFinalSection = false;
}
else {
// last chance to check for undefines
}
+void Resolver::tweakWeakness()
+{
+ // <rdar://problem/7977374> Add command line options to control symbol weak-def bit on exported symbols
+ if ( _options.hasWeakBitTweaks() ) {
+ for (std::vector<ld::Internal::FinalSection*>::iterator sit = _internal.sections.begin(); sit != _internal.sections.end(); ++sit) {
+ ld::Internal::FinalSection* sect = *sit;
+ for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
+ const ld::Atom* atom = *ait;
+ if ( atom->definition() != ld::Atom::definitionRegular )
+ continue;
+ const char* name = atom->name();
+ if ( atom->scope() == ld::Atom::scopeGlobal ) {
+ if ( atom->combine() == ld::Atom::combineNever ) {
+ if ( _options.forceWeak(name) )
+ (const_cast<ld::Atom*>(atom))->setCombine(ld::Atom::combineByName);
+ }
+ else if ( atom->combine() == ld::Atom::combineByName ) {
+ if ( _options.forceNotWeak(name) )
+ (const_cast<ld::Atom*>(atom))->setCombine(ld::Atom::combineNever);
+ }
+ }
+ else {
+ if ( _options.forceWeakNonWildCard(name) )
+ warning("cannot force to be weak, non-external symbol %s", name);
+ else if ( _options.forceNotWeakNonWildcard(name) )
+ warning("cannot force to be not-weak, non-external symbol %s", name);
+ }
+ }
+ }
+ }
+}
+
+
void Resolver::resolve()
{
this->initializeState();
this->checkUndefines();
this->checkDylibSymbolCollisions();
this->removeCoalescedAwayAtoms();
- this->fillInInternalState();
this->linkTimeOptimize();
+ this->fillInInternalState();
+ this->tweakWeakness();
+ _symbolTable.checkDuplicateSymbols();
}
class Resolver : public ld::File::AtomHandler
{
public:
- Resolver(const Options& opts, const InputFiles& inputs, ld::Internal& state)
+ Resolver(const Options& opts, InputFiles& inputs, ld::Internal& state)
: _options(opts), _inputFiles(inputs), _internal(state),
_symbolTable(opts, state.indirectBindingTable),
- _haveLLVMObjs(false), _addToFinalSection(false),
+ _haveLLVMObjs(false),
_completedInitialObjectFiles(false) {}
void markLive(const ld::Atom& atom, WhyLiveBackChain* previous);
bool isDtraceProbe(ld::Fixup::Kind kind);
void liveUndefines(std::vector<const char*>&);
+ void remainingUndefines(std::vector<const char*>&);
bool printReferencedBy(const char* name, SymbolTable::IndirectBindingSlot slot);
-
+ void tweakWeakness();
class CStringEquals {
public:
};
const Options& _options;
- const InputFiles& _inputFiles;
+ InputFiles& _inputFiles;
ld::Internal& _internal;
std::vector<const ld::Atom*> _atoms;
std::set<const ld::Atom*> _deadStripRoots;
std::vector<const ld::Atom*> _atomsWithUnresolvedReferences;
SymbolTable _symbolTable;
bool _haveLLVMObjs;
- bool _addToFinalSection;
bool _completedInitialObjectFiles;
};
--- /dev/null
+//
+// Snapshot.cpp
+// ld64
+//
+// Created by Josh Behnke on 8/25/11.
+// Copyright (c) 2011 Apple Inc. All rights reserved.
+//
+
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <libgen.h>
+#include <time.h>
+#include <Block.h>
+
+#include "Snapshot.h"
+#include "Options.h"
+
+#include "compile_stubs.h"
+
+//#define STORE_PID_IN_SNAPSHOT 1
+
+// Well known snapshot file/directory names. These appear in the root of the snapshot.
+// They are collected together here to make managing the namespace easier.
+static const char *frameworksString = "frameworks"; // directory containing framework stubs (mach-o files)
+static const char *dylibsString = "dylibs"; // directory containing dylib stubs (mach-o files)
+static const char *archiveFilesString = "archive_files"; // directory containing .a files
+static const char *origCommandLineString = "orig_command_line"; // text file containing the original command line
+static const char *linkCommandString = "link_command"; // text file containing the snapshot equivalent command line
+static const char *dataFilesString = "data_files"; // arbitrary data files referenced on the command line
+static const char *objectsString = "objects"; // directory containing object files
+static const char *frameworkStubsString = "framework_stubs"; // directory containing framework stub info (text files)
+static const char *dylibStubsString = "dylib_stubs"; // directory containing dylib stub info (text files)
+static const char *assertFileString = "assert_info"; // text file containing assertion failure logs
+static const char *compileFileString = "compile_stubs"; // text file containing compile_stubs script
+
+Snapshot *Snapshot::globalSnapshot = NULL;
+
+Snapshot::Snapshot() : fRecordArgs(false), fRecordObjects(false), fRecordDylibSymbols(false), fRecordArchiveFiles(false), fRecordUmbrellaFiles(false), fRecordDataFiles(false), fFrameworkArgAdded(false), fSnapshotLocation(NULL), fSnapshotName(NULL), fRootDir(NULL), fFilelistFile(-1), fCopiedArchives(NULL)
+{
+ if (globalSnapshot != NULL)
+ throw "only one snapshot supported";
+ globalSnapshot = this;
+}
+
+
+Snapshot::~Snapshot()
+{
+ // Lots of things leak under the assumption the linker is about to exit.
+}
+
+
+void Snapshot::setSnapshotPath(const char *path)
+{
+ if (fRootDir == NULL) {
+ fSnapshotLocation = strdup(path);
+ }
+}
+
+
+void Snapshot::setSnapshotMode(SnapshotMode mode)
+{
+ if (fRootDir == NULL) {
+ fRecordArgs = false;
+ fRecordObjects = false;
+ fRecordDylibSymbols = false;
+ fRecordArchiveFiles = false;
+ fRecordUmbrellaFiles = false;
+ fRecordDataFiles = false;
+
+ switch (mode) {
+ case SNAPSHOT_DISABLED:
+ break;
+ case SNAPSHOT_DEBUG:
+ fRecordArgs = fRecordObjects = fRecordDylibSymbols = fRecordArchiveFiles = fRecordUmbrellaFiles = fRecordDataFiles = true;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void Snapshot::setSnapshotName(const char *path)
+{
+ if (fRootDir == NULL) {
+ const char *base = basename((char *)path);
+ time_t now = time(NULL);
+ struct tm t;
+ localtime_r(&now, &t);
+ char buf[PATH_MAX];
+ snprintf(buf, sizeof(buf)-1, "%s-%4.4d-%2.2d-%2.2d-%2.2d%2.2d%2.2d.ld-snapshot", base, t.tm_year+1900, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
+ fSnapshotName = strdup(buf);
+ }
+}
+
+
+// Construct a path string in the snapshot.
+// subdir - an optional subdirectory name
+// file - the file name
+void Snapshot::buildPath(char *buf, const char *subdir, const char *file)
+{
+ if (fRootDir == NULL)
+ throw "snapshot not created";
+
+ strcpy(buf, fRootDir);
+ strcat(buf, "/");
+ if (subdir) {
+ strcat(buf, subdir);
+ // implicitly create the subdirectory
+ mkdir(buf, S_IRUSR|S_IWUSR|S_IXUSR);
+ strcat(buf, "/");
+ }
+ if (file != NULL)
+ strcat(buf, basename((char *)file));
+}
+
+
+// Construct a unique path string in the snapshot. If a path collision is detected then uniquing
+// is accomplished by appending a counter to the path until there is no preexisting file.
+// subdir - an optional subdirectory name
+// file - the file name
+void Snapshot::buildUniquePath(char *buf, const char *subdir, const char *file)
+{
+ buildPath(buf, subdir, file);
+ struct stat st;
+ if (stat(buf, &st)==0) {
+ // make it unique
+ int counter=1;
+ char *number = strrchr(buf, 0);
+ number[0]='-';
+ number++;
+ do {
+ sprintf(number, "%d", counter++);
+ } while (stat(buf, &st) == 0);
+ }
+}
+
+
+// Copy a file to the snapshot.
+// sourcePath is the original file
+// subdir is an optional subdirectory in the snapshot
+// path is an optional out parameter containing the final uniqued path in the snapshot
+// where the file was copied
+void Snapshot::copyFileToSnapshot(const char *sourcePath, const char *subdir, char *path)
+{
+ const int copyBufSize=(1<<14); // 16kb buffer
+ static void *copyBuf = NULL;
+ if (copyBuf == NULL)
+ copyBuf = malloc(copyBufSize);
+
+ char *file=basename((char *)sourcePath);
+ char buf[PATH_MAX];
+ if (path == NULL) path = buf;
+ buildUniquePath(path, subdir, file);
+ int out_fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
+ int in_fd = open(sourcePath, O_RDONLY);
+ int len;
+ if (out_fd != -1 && in_fd != -1) {
+ do {
+ len = read(in_fd, copyBuf, copyBufSize);
+ if (len > 0) write(out_fd, copyBuf, len);
+ } while (len == copyBufSize);
+ }
+ close(in_fd);
+ close(out_fd);
+}
+
+
+// Create the snapshot root directory.
+void Snapshot::createSnapshot()
+{
+ if (fRootDir == NULL) {
+ // provide default name and location
+ if (fSnapshotLocation == NULL)
+ fSnapshotLocation = "/tmp";
+ if (fSnapshotName == NULL) {
+ setSnapshotName("ld_snapshot");
+ }
+
+ char buf[PATH_MAX];
+ fRootDir = (char *)fSnapshotLocation;
+ buildUniquePath(buf, NULL, fSnapshotName);
+ fRootDir = strdup(buf);
+ if (mkdir(fRootDir, S_IRUSR|S_IWUSR|S_IXUSR)!=0) {
+ warning("unable to create link snapshot directory: %s", fRootDir);
+ fRootDir = NULL;
+ setSnapshotMode(SNAPSHOT_DISABLED); // don't try to write anything if we can't create snapshot dir
+ }
+
+ buildPath(buf, NULL, compileFileString);
+ int compileScript = open(buf, O_WRONLY|O_CREAT|O_TRUNC, S_IXUSR|S_IRUSR|S_IWUSR);
+ write(compileScript, compile_stubs, strlen(compile_stubs));
+ close(compileScript);
+
+ SnapshotLog::iterator it;
+ for (it = fLog.begin(); it != fLog.end(); it++) {
+ void (^logItem)(void) = *it;
+ logItem();
+ Block_release(logItem);
+ }
+ fLog.erase(fLog.begin(), fLog.end());
+
+ if (fRecordArgs) {
+ writeCommandLine(fRawArgs, origCommandLineString, true);
+ writeCommandLine(fArgs);
+ }
+
+#if STORE_PID_IN_SNAPSHOT
+ char path[PATH_MAX];
+ buildUniquePath(path, NULL, pidString);
+ int pidfile = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
+ char pid_buf[32];
+ sprintf(pid_buf, "%lu\n", (long unsigned)getpid());
+ write(pidfile, pid_buf, strlen(pid_buf));
+ write(pidfile, "\n", 1);
+ close(pidfile);
+#endif
+
+ }
+}
+
+
+// Write the current command line vector to filename.
+void Snapshot::writeCommandLine(StringVector &args, const char *filename, bool includeCWD)
+{
+ if (!isLazy() && fRecordArgs) {
+ // Figure out the file name and open it.
+ if (filename == NULL)
+ filename = linkCommandString;
+ char path[PATH_MAX];
+ buildPath(path, NULL, filename);
+ int argsFile = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IXUSR|S_IRUSR|S_IWUSR);
+ FILE *argsStream = fdopen(argsFile, "w");
+
+ if (includeCWD)
+ fprintf(argsStream, "cd %s\n", getcwd(path, sizeof(path)));
+
+ // iterate to write args, quoting as needed
+ StringVector::iterator it;
+ for (it = args.begin(); it != args.end(); it++) {
+ const char *arg = *it;
+ bool needQuotes = false;
+ for (const char *c = arg; *c != 0 && !needQuotes; c++) {
+ if (isspace(*c))
+ needQuotes = true;
+ }
+ if (it != args.begin()) fprintf(argsStream, " ");
+ if (needQuotes) fprintf(argsStream, "\"");
+ fprintf(argsStream, "%s", arg);
+ if (needQuotes) fprintf(argsStream, "\"");
+ }
+ fprintf(argsStream, "\n");
+ fclose(argsStream);
+ }
+}
+
+
+// Store the command line args in the snapshot.
+void Snapshot::recordRawArgs(int argc, const char *argv[])
+{
+ // first store the original command line as-is
+ for (int i=0; i<argc; i++) {
+ fRawArgs.push_back(argv[i]);
+ }
+ fArgs.insert(fArgs.begin(), argv[0]);
+ fArgs.insert(fArgs.begin()+1, "-Z"); // don't search standard paths when running in the snapshot
+}
+
+
+// Adds one or more args to the snapshot link command.
+// argIndex is the index in the original raw args vector to start adding args
+// argCount is the count of args to copy from the raw args vector
+// fileArg is the index relative to argIndex of a file arg. The file is copied into the
+// snapshot and the path is fixed up in the snapshot link command. (skipped if fileArg==-1)
+void Snapshot::addSnapshotLinkArg(int argIndex, int argCount, int fileArg)
+{
+ if (fRootDir == NULL) {
+ fLog.push_back(Block_copy(^{ this->addSnapshotLinkArg(argIndex, argCount, fileArg); }));
+ } else {
+ char buf[PATH_MAX];
+ const char *subdir = dataFilesString;
+ for (int i=0, arg=argIndex; i<argCount && argIndex+1<(int)fRawArgs.size(); i++, arg++) {
+ if (i != fileArg) {
+ fArgs.push_back(fRawArgs[arg]);
+ } else {
+ if (fRecordDataFiles) {
+ copyFileToSnapshot(fRawArgs[arg], subdir, buf);
+ fArgs.push_back(strdup(snapshotRelativePath(buf)));
+ } else {
+ // if we don't copy the file then just record the original path
+ fArgs.push_back(strdup(fRawArgs[arg]));
+ }
+ }
+ }
+ }
+}
+
+// Record the -arch string
+void Snapshot::recordArch(const char *arch)
+{
+ // must be called after recordRawArgs()
+ if (fRawArgs.size() == 0)
+ throw "raw args not set";
+
+ // only need to store the arch explicitly if it is not mentioned on the command line
+ bool archInArgs = false;
+ StringVector::iterator it;
+ for (it = fRawArgs.begin(); it != fRawArgs.end() && !archInArgs; it++) {
+ const char *arg = *it;
+ if (strcmp(arg, "-arch") == 0)
+ archInArgs = true;
+ }
+
+ if (!archInArgs) {
+ if (fRootDir == NULL) {
+ fLog.push_back(Block_copy(^{ this->recordArch(arch); }));
+ } else {
+ char path_buf[PATH_MAX];
+ buildUniquePath(path_buf, NULL, "arch");
+ int fd=open(path_buf, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
+ write(fd, arch, strlen(arch));
+ close(fd);
+ }
+ }
+}
+
+// Record an object file in the snapshot.
+// path - the object file's path
+// fileContent - a pointer to the object file content
+// fileLength - the buffer size of fileContent
+void Snapshot::recordObjectFile(const char *path)
+{
+ if (fRootDir == NULL) {
+ fLog.push_back(Block_copy(^{ this->recordObjectFile(path); }));
+ } else {
+ if (fRecordObjects) {
+ char path_buf[PATH_MAX];
+ copyFileToSnapshot(path, objectsString, path_buf);
+
+ // lazily open the filelist file
+ if (fFilelistFile == -1) {
+ char filelist_path[PATH_MAX];
+ buildUniquePath(filelist_path, objectsString, "filelist");
+ fFilelistFile = open(filelist_path, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
+ fArgs.push_back("-filelist");
+ fArgs.push_back(strdup(snapshotRelativePath(filelist_path)));
+ writeCommandLine(fArgs);
+ }
+
+ // record the snapshot path in the filelist
+ const char *relative_path = snapshotRelativePath(path_buf);
+ write(fFilelistFile, relative_path, strlen(relative_path));
+ write(fFilelistFile, "\n", 1);
+ }
+ }
+}
+
+void Snapshot::addFrameworkArg(const char *framework)
+{
+ bool found=false;
+ for (unsigned i=0; i<fArgs.size()-1; i++) {
+ if (strcmp(fArgs[i], "-framework") == 0 && strcmp(fArgs[i+1], framework) == 0)
+ found = true;
+ }
+ if (!found) {
+ if (!fFrameworkArgAdded) {
+ fFrameworkArgAdded = true;
+ fArgs.push_back("-Fframeworks");
+ }
+ fArgs.push_back("-framework");
+ fArgs.push_back(strdup(framework));
+ writeCommandLine(fArgs);
+ }
+}
+
+void Snapshot::addDylibArg(const char *dylib)
+{
+ bool found=false;
+ for (unsigned i=0; i<fArgs.size()-1; i++) {
+ if (strcmp(fArgs[i], dylib) == 0)
+ found = true;
+ }
+ if (!found) {
+ char buf[ARG_MAX];
+ sprintf(buf, "%s/%s", dylibsString, dylib);
+ fArgs.push_back(strdup(buf));
+ writeCommandLine(fArgs);
+ }
+}
+
+// Record a dylib symbol reference in the snapshot.
+// (References are not written to the snapshot until writeStubDylibs() is called.)
+void Snapshot::recordDylibSymbol(ld::dylib::File* dylibFile, const char *name)
+{
+ if (fRootDir == NULL) {
+ fLog.push_back(Block_copy(^{ this->recordDylibSymbol(dylibFile, name); }));
+ } else {
+ if (fRecordDylibSymbols) {
+ // find the dylib in the table
+ DylibMap::iterator it;
+ const char *dylibPath = dylibFile->path();
+ it = fDylibSymbols.find(dylibPath);
+ bool isFramework = (strstr(dylibPath, "framework") != NULL);
+ int dylibFd;
+ if (it == fDylibSymbols.end()) {
+ // Didn't find a file descriptor for this dylib. Create one and add it to the dylib map.
+ char path_buf[PATH_MAX];
+ buildUniquePath(path_buf, isFramework ? frameworkStubsString : dylibStubsString, dylibPath);
+ dylibFd = open(path_buf, O_WRONLY|O_APPEND|O_CREAT, S_IRUSR|S_IWUSR);
+ fDylibSymbols.insert(std::pair<const char *, int>(dylibPath, dylibFd));
+ char *base_name = strdup(basename(path_buf));
+ if (isFramework) {
+ addFrameworkArg(base_name);
+ } else {
+ addDylibArg(base_name);
+ }
+ writeCommandLine(fArgs);
+ } else {
+ dylibFd = it->second;
+ }
+ // Record the symbol.
+
+ bool isIdentifier = (name[0] == '_');
+ for (const char *c = name; *c != 0 && isIdentifier; c++)
+ if (!isalnum(*c) && *c!='_')
+ isIdentifier = false;
+ const char *prefix = "void ";
+ const char *weakAttr = "__attribute__ ((weak)) ";
+ const char *suffix = "(void){}\n";
+ if (isIdentifier) {
+ write(dylibFd, prefix, strlen(prefix));
+ if (dylibFile->hasWeakExternals() && dylibFile->hasWeakDefinition(name))
+ write(dylibFd, weakAttr, strlen(weakAttr));
+ if (*name == '_') name++;
+ write(dylibFd, name, strlen(name));
+ write(dylibFd, suffix, strlen(suffix));
+ } else {
+ static int symbolCounter = 0;
+ char buf[64+strlen(name)];
+ sprintf(buf, "void s_%5.5d(void) __asm(\"%s\");\nvoid s_%5.5d(){}\n", symbolCounter, name, symbolCounter);
+ write(dylibFd, buf, strlen(buf));
+ symbolCounter++;
+ }
+ }
+ }
+}
+
+
+// Record a .a archive in the snapshot.
+void Snapshot::recordArchive(const char *archiveFile)
+{
+ if (fRootDir == NULL) {
+ const char *copy = strdup(archiveFile);
+ fLog.push_back(Block_copy(^{ this->recordArchive(archiveFile); ::free((void *)copy); }));
+ } else {
+ if (fRecordArchiveFiles) {
+ // lazily create a vector of .a files that have been added
+ if (fCopiedArchives == NULL) {
+ fCopiedArchives = new StringVector;
+ }
+
+ // See if we have already added this .a
+ StringVector::iterator it;
+ bool found = false;
+ for (it = fCopiedArchives->begin(); it != fCopiedArchives->end() && !found; it++) {
+ if (strcmp(archiveFile, *it) == 0)
+ found = true;
+ }
+
+ // If this is a new .a then copy it to the snapshot and add it to the snapshot link command.
+ if (!found) {
+ char path[PATH_MAX];
+ fCopiedArchives->push_back(archiveFile);
+ copyFileToSnapshot(archiveFile, archiveFilesString, path);
+ fArgs.push_back(strdup(snapshotRelativePath(path)));
+ writeCommandLine(fArgs);
+ }
+ }
+ }
+}
+
+void Snapshot::recordSubUmbrella(const char *frameworkPath)
+{
+ if (fRootDir == NULL) {
+ const char *copy = strdup(frameworkPath);
+ fLog.push_back(Block_copy(^{ this->recordSubUmbrella(copy); ::free((void *)copy); }));
+ } else {
+ if (fRecordUmbrellaFiles) {
+ const char *framework = basename((char *)frameworkPath);
+ char buf[PATH_MAX], wrapper[PATH_MAX];
+ strcpy(wrapper, frameworksString);
+ buildPath(buf, wrapper, NULL); // ensure the frameworks directory exists
+ strcat(wrapper, "/");
+ strcat(wrapper, framework);
+ strcat(wrapper, ".framework");
+ copyFileToSnapshot(frameworkPath, wrapper);
+ addFrameworkArg(framework);
+ }
+ }
+}
+
+void Snapshot::recordSubLibrary(const char *dylibPath)
+{
+ if (fRootDir == NULL) {
+ const char *copy = strdup(dylibPath);
+ fLog.push_back(Block_copy(^{ this->recordSubLibrary(copy); ::free((void *)copy); }));
+ } else {
+ if (fRecordUmbrellaFiles) {
+ copyFileToSnapshot(dylibPath, dylibsString);
+ addDylibArg(basename((char *)dylibPath));
+ }
+ }
+}
+
+void Snapshot::recordAssertionMessage(const char *fmt, ...)
+{
+ char *msg;
+ va_list args;
+ va_start(args, fmt);
+ vasprintf(&msg, fmt, args);
+ va_end(args);
+ if (msg != NULL) {
+ if (fRootDir == NULL) {
+ fLog.push_back(Block_copy(^{ this->recordAssertionMessage("%s", msg); free(msg); }));
+ } else {
+ char path[PATH_MAX];
+ buildPath(path, NULL, assertFileString);
+ int log = open(path, O_WRONLY|O_APPEND|O_CREAT, S_IRUSR|S_IWUSR);
+ write(log, msg, strlen(msg));
+ close(log);
+ free(msg);
+ }
+ }
+}
--- /dev/null
+//
+// Snapshot.h
+// ld64
+//
+// Created by Josh Behnke on 8/25/11.
+// Copyright (c) 2011 Apple Inc. All rights reserved.
+//
+
+#ifndef ld64_Snapshot_h
+#define ld64_Snapshot_h
+#include <stdint.h>
+#include <string.h>
+#include <map>
+#include <vector>
+
+#include "ld.hpp"
+
+class Options;
+class SnapshotLogItem;
+
+class Snapshot {
+
+public:
+ static Snapshot *globalSnapshot;
+
+ typedef enum {
+ SNAPSHOT_DISABLED, // nothing is recorded
+ SNAPSHOT_DEBUG, // records: .o, .dylib, .framework, .a, and other data files
+ } SnapshotMode;
+
+ Snapshot();
+ ~Snapshot();
+
+ // Control the data captured in the snapshot
+ void setSnapshotMode(SnapshotMode mode);
+
+ // Use the basename of path to construct the snapshot name.
+ // Must be called prior to createSnapshot().
+ void setSnapshotName(const char *path);
+
+ // Set the directory in which the snapshot will be created.
+ // Must be called prior to createSnapshot().
+ void setSnapshotPath(const char *path);
+
+ // Stores the linker command line in the snapshot
+ void recordRawArgs(int argc, const char *argv[]);
+
+ // Adds one or more args to the snapshot link command.
+ // argIndex is the index in the original raw args vector to start adding args
+ // argCount is the count of args to copy from the raw args vector
+ // fileArg is the index relative to argIndex of a file arg. The file is copied into the
+ // snapshot and the path is fixed up in the snapshot link command. (skipped if fileArg==-1)
+ // recordRawArgs() must be called prior to the first call to addSnapshotLinkArg()
+ void addSnapshotLinkArg(int argIndex, int argCount=1, int fileArg=-1);
+
+ // record the -arch string
+ void recordArch(const char *arch);
+
+ // Stores an object file in the snapshot, using a unique name in an "objects" subdir.
+ void recordObjectFile(const char *path);
+
+ // Records symbol names used in dylibs. Does not store anything in the snapshot.
+ void recordDylibSymbol(ld::dylib::File* dylibFile, const char *name);
+
+ // Stores an archive (.a) file in the snapshot.
+ void recordArchive(const char *archiveFile);
+
+ // Copies the framework binary into the snapshot frameworks directory.
+ void recordSubUmbrella(const char *frameworkPath);
+
+ // Copies the library binary into the snapshot dylibs directory.
+ void recordSubLibrary(const char *dylibPath);
+
+ // Records arbitrary text messages into a log file in the snapshot.
+ // Used by the assertion failure machienery.
+ void recordAssertionMessage(const char *fmt, ...);
+
+ // Create the snapshot.
+ // Until this is called the snapshot operates lazily, storing minimal data in memory.
+ // When this is called the snapshot is created and any previously recorded data is
+ // immediately copied. Any subsequent additions to the snapshot are copied immediately.
+ void createSnapshot();
+
+ // Returns the snapshot root directory.
+ const char *rootDir() { return fRootDir; }
+
+private:
+
+ friend class SnapshotArchiveFileLog;
+
+ typedef std::vector<void(^)(void)> SnapshotLog;
+
+ struct strcompclass {
+ bool operator() (const char *a, const char *b) const { return ::strcmp(a, b) < 0; }
+ };
+ typedef std::vector<const char *> StringVector;
+ typedef std::map<const char *, int, strcompclass > DylibMap;
+ typedef std::map<const char *, const char *, strcompclass> PathMap;
+
+
+ // Write the current contents of the args vector to a file in the snapshot.
+ // If filename is NULL then "link_command" is used.
+ // This is used to write both the original and the "cooked" versions of the link command
+ void writeCommandLine(StringVector &args, const char *filename=NULL, bool includeCWD=false);
+
+ // Construct a path in the snapshot.
+ // buf is a sring buffer in which the path is constructed
+ // subdir is an optional subdirectory, and file is a file name
+ // Constructs the path <snapshot_root>/<subdir>/<file> in buf
+ void buildPath(char *buf, const char *subdir, const char *file);
+
+ // Similar to buildPath(), except this ensures the returned path
+ // does not reference an existing file in the snapshot.
+ // Performs uniquing by appending a count suffex to the path (ie .../file-XX)
+ void buildUniquePath(char *buf, const char *subdir, const char *file);
+
+ // Copies an arbitrary file to the snapshot. Subdir specifies an optional subdirectory name.
+ // Uses buildUniquePath to construct a unique path. If the result path is needed by the caller
+ // then a path buffer can be supplied in buf. Otherwise an internal buffer is used.
+ void copyFileToSnapshot(const char *sourcePath, const char *subdir, char *buf=NULL);
+
+ // Convert a full path to snapshot relative by constructing an interior pointer at the right offset.
+ const char *snapshotRelativePath(const char *path) { return path+strlen(fRootDir)+1; }
+
+ // returns true if the snapshot has not been created (by createSnapshot()) yet
+ bool isLazy() { return fRootDir == NULL; }
+
+ void addFrameworkArg(const char *framework);
+ void addDylibArg(const char *dylib);
+
+ SnapshotLog fLog; // log of events that recorded data in a snapshot prior to createSnapshot()
+ bool fRecordArgs; // record command line
+ bool fRecordObjects; // record .o files
+ bool fRecordDylibSymbols; // record referenced dylib/framework symbols
+ bool fRecordArchiveFiles; // record .a files
+ bool fRecordUmbrellaFiles; // record re-exported sub frameworks/dylibs
+ bool fRecordDataFiles; // record other data files
+ bool fFrameworkArgAdded;
+
+ const char *fSnapshotLocation; // parent directory of frootDir
+ const char *fSnapshotName; // a string to use in constructing the snapshot name
+ char *fRootDir; // root directory of the snapshot
+ int fFilelistFile; // file descriptor to the open text file used for the -filelist
+
+ StringVector fRawArgs; // stores the raw command line args
+ StringVector fArgs; // stores the "cooked" command line args
+ PathMap fPathMap; // mapping of original paths->snapshot paths for copied files
+
+ DylibMap fDylibSymbols; // map of dylib names to string vector containing referenced symbol names
+ StringVector *fCopiedArchives; // vector of .a files that have been copied to the snapshot
+};
+
+#endif
#include <unistd.h>
#include <assert.h>
+#include <iostream>
+#include <sstream>
#include <string>
#include <map>
#include <set>
// HACK, I can't find a way to pass values in the compare classes (e.g. ContentFuncs)
// so use global variable to pass info.
static ld::IndirectBindingTable* _s_indirectBindingTable = NULL;
-bool SymbolTable::_s_doDemangle = false;
SymbolTable::SymbolTable(const Options& opts, std::vector<const ld::Atom*>& ibt)
- : _options(opts), _cstringTable(6151), _indirectBindingTable(ibt), _hasExternalTentativeDefinitions(false)
+ : _options(opts), _cstringTable(6151), _indirectBindingTable(ibt), _hasExternalTentativeDefinitions(false)
{
_s_indirectBindingTable = this;
- _s_doDemangle = _options.demangleSymbols();
}
}
+void SymbolTable::addDuplicateSymbol(const char *name, const ld::Atom *atom)
+{
+ // Look up or create the file list for name.
+ DuplicateSymbols::iterator symbolsIterator = _duplicateSymbols.find(name);
+ DuplicatedSymbolAtomList *atoms = NULL;
+ if (symbolsIterator != _duplicateSymbols.end()) {
+ atoms = symbolsIterator->second;
+ } else {
+ atoms = new std::vector<const ld::Atom *>;
+ _duplicateSymbols.insert(std::pair<const char *, DuplicatedSymbolAtomList *>(name, atoms));
+ }
+
+ // check if file is already in the list, add it if not
+ bool found = false;
+ for (DuplicatedSymbolAtomList::iterator it = atoms->begin(); !found && it != atoms->end(); it++)
+ if (strcmp((*it)->file()->path(), atom->file()->path()) == 0)
+ found = true;
+ if (!found)
+ atoms->push_back(atom);
+}
-bool SymbolTable::addByName(const ld::Atom& newAtom, bool ignoreDuplicates)
+void SymbolTable::checkDuplicateSymbols() const
{
- bool useNew = true;
- bool checkVisibilityMismatch = false;
- assert(newAtom.name() != NULL);
- const char* name = newAtom.name();
- IndirectBindingSlot slot = this->findSlotForName(name);
- const ld::Atom* existingAtom = _indirectBindingTable[slot];
- //fprintf(stderr, "addByName(%p) name=%s, slot=%u, existing=%p\n", &newAtom, newAtom.name(), slot, existingAtom);
- if ( existingAtom != NULL ) {
- assert(&newAtom != existingAtom);
- switch ( existingAtom->definition() ) {
- case ld::Atom::definitionRegular:
- switch ( newAtom.definition() ) {
- case ld::Atom::definitionRegular:
- if ( existingAtom->combine() == ld::Atom::combineByName ) {
- if ( newAtom.combine() == ld::Atom::combineByName ) {
- // <rdar://problem/9183821> always choose mach-o over llvm bit code, otherwise LTO may eliminate the llvm atom
- const bool existingIsLTO = (existingAtom->contentType() == ld::Atom::typeLTOtemporary);
- const bool newIsLTO = (newAtom.contentType() == ld::Atom::typeLTOtemporary);
- if ( existingIsLTO != newIsLTO ) {
- useNew = existingIsLTO;
- }
- else {
- // both weak, prefer non-auto-hide one
- if ( newAtom.autoHide() != existingAtom->autoHide() ) {
- // <rdar://problem/6783167> support auto hidden weak symbols: .weak_def_can_be_hidden
- useNew = existingAtom->autoHide();
- // don't check for visibility mismatch
- }
- else if ( newAtom.autoHide() && existingAtom->autoHide() ) {
- // both have auto-hide, so use one with greater alignment
- useNew = ( newAtom.alignment().trailingZeros() > existingAtom->alignment().trailingZeros() );
- }
- else {
- // neither auto-hide, check visibility
- if ( newAtom.scope() != existingAtom->scope() ) {
- // <rdar://problem/8304984> use more visible weak def symbol
- useNew = (newAtom.scope() == ld::Atom::scopeGlobal);
- }
- else {
- // both have same visibility, use one with greater alignment
- useNew = ( newAtom.alignment().trailingZeros() > existingAtom->alignment().trailingZeros() );
- }
- }
- }
- }
- else {
- // existing weak, new is not-weak
- useNew = true;
- }
+ bool foundDuplicate = false;
+ for (DuplicateSymbols::const_iterator symbolIt = _duplicateSymbols.begin(); symbolIt != _duplicateSymbols.end(); symbolIt++) {
+ DuplicatedSymbolAtomList *atoms = symbolIt->second;
+ bool reportDuplicate;
+ if (_options.deadCodeStrip()) {
+ // search for a live atom
+ reportDuplicate = false;
+ for (DuplicatedSymbolAtomList::iterator it = atoms->begin(); !reportDuplicate && it != atoms->end(); it++) {
+ if ((*it)->live())
+ reportDuplicate = true;
+ }
+ } else {
+ reportDuplicate = true;
+ }
+ if (reportDuplicate) {
+ foundDuplicate = true;
+ fprintf(stderr, "duplicate symbol %s in:\n", symbolIt->first);
+ for (DuplicatedSymbolAtomList::iterator atomIt = atoms->begin(); atomIt != atoms->end(); atomIt++) {
+ fprintf(stderr, " %s\n", (*atomIt)->file()->path());
+ }
+ }
+ }
+ if (foundDuplicate)
+ throwf("%d duplicate symbol%s", (int)_duplicateSymbols.size(), _duplicateSymbols.size()==1?"":"s");
+}
+
+// AtomPicker encapsulates the logic for picking which atom to use when adding an atom by name results in a collision
+class NameCollisionResolution {
+public:
+ NameCollisionResolution(const ld::Atom& a, const ld::Atom& b, bool ignoreDuplicates, const Options& options) : _atomA(a), _atomB(b), _options(options), _reportDuplicate(false), _ignoreDuplicates(ignoreDuplicates) {
+ pickAtom();
+ }
+
+ // Returns which atom to use
+ const ld::Atom& chosen() { return *_chosen; }
+ bool choseAtom(const ld::Atom& atom) { return _chosen == &atom; }
+
+ // Returns true if the two atoms should be reported as a duplicate symbol
+ bool reportDuplicate() { return _reportDuplicate; }
+
+private:
+ const ld::Atom& _atomA;
+ const ld::Atom& _atomB;
+ const Options& _options;
+ const ld::Atom* _chosen;
+ bool _reportDuplicate;
+ bool _ignoreDuplicates;
+
+ void pickAtom(const ld::Atom& atom) { _chosen = &atom; } // primitive to set which atom is picked
+ void pickAtomA() { pickAtom(_atomA); } // primitive to pick atom A
+ void pickAtomB() { pickAtom(_atomB); } // primitive to pick atom B
+
+ // use atom A if pickA, otherwise use atom B
+ void pickAOrB(bool pickA) { if (pickA) pickAtomA(); else pickAtomB(); }
+
+ void pickHigherOrdinal() {
+ pickAOrB(_atomA.file()->ordinal() < _atomB.file()->ordinal());
+ }
+
+ void pickLowerOrdinal() {
+ pickAOrB(_atomA.file()->ordinal() > _atomB.file()->ordinal());
+ }
+
+ void pickLargerSize() {
+ if (_atomA.size() == _atomB.size())
+ pickLowerOrdinal();
+ else
+ pickAOrB(_atomA.size() > _atomB.size());
+ }
+
+ void pickGreaterAlignment() {
+ pickAOrB(_atomA.alignment().trailingZeros() > _atomB.alignment().trailingZeros());
+ }
+
+ void pickBetweenRegularAtoms() {
+ if ( _atomA.combine() == ld::Atom::combineByName ) {
+ if ( _atomB.combine() == ld::Atom::combineByName ) {
+ // <rdar://problem/9183821> always choose mach-o over llvm bit code, otherwise LTO may eliminate the llvm atom
+ const bool aIsLTO = (_atomA.contentType() == ld::Atom::typeLTOtemporary);
+ const bool bIsLTO = (_atomB.contentType() == ld::Atom::typeLTOtemporary);
+ // <rdar://problem/9183821> always choose mach-o over llvm bit code, otherwise LTO may eliminate the llvm atom
+ if ( aIsLTO != bIsLTO ) {
+ pickAOrB(!aIsLTO);
+ }
+ else {
+ // both weak, prefer non-auto-hide one
+ if ( _atomA.autoHide() != _atomB.autoHide() ) {
+ // <rdar://problem/6783167> support auto hidden weak symbols: .weak_def_can_be_hidden
+ pickAOrB(!_atomA.autoHide());
+ }
+ else if ( _atomA.autoHide() && _atomB.autoHide() ) {
+ // both have auto-hide, so use one with greater alignment
+ pickGreaterAlignment();
+ }
+ else {
+ // neither auto-hide, check visibility
+ if ( _atomA.scope() != _atomB.scope() ) {
+ // <rdar://problem/8304984> use more visible weak def symbol
+ pickAOrB(_atomA.scope() == ld::Atom::scopeGlobal);
}
else {
- if ( newAtom.combine() == ld::Atom::combineByName ) {
- // existing not-weak, new is weak
- useNew = false;
- }
- else {
- // existing not-weak, new is not-weak
- if ( newAtom.section().type() == ld::Section::typeMachHeader ) {
- warning("ignoring override of built-in symbol %s from %s", newAtom.name(), existingAtom->file()->path());
- useNew = true;
- }
- else if ( existingAtom->section().type() == ld::Section::typeMachHeader ) {
- warning("ignoring override of built-in symbol %s from %s", newAtom.name(), newAtom.file()->path());
- useNew = false;
- }
- else {
- if ( ignoreDuplicates ) {
- useNew = false;
- static bool fullWarning = false;
- if ( ! fullWarning ) {
- warning("-dead_strip with lazy loaded static (library) archives "
- "has resulted in a duplicate symbol. You can change your "
- "source code to rename symbols to avoid the collision. "
- "This will be an error in a future linker.");
- fullWarning = true;
- }
- warning("duplicate symbol %s originally in %s now lazily loaded from %s",
- SymbolTable::demangle(name), existingAtom->file()->path(), newAtom.file()->path());
- }
- else {
- throwf("duplicate symbol %s in %s and %s",
- SymbolTable::demangle(name), newAtom.file()->path(), existingAtom->file()->path());
- }
- }
- }
+ // both have same visibility, use one with greater alignment
+ pickGreaterAlignment();
}
+ }
+ }
+ }
+ else {
+ pickAtomB(); // pick not-weak
+
+ }
+ }
+ else {
+ if ( _atomB.combine() == ld::Atom::combineByName ) {
+ pickAtomA(); // pick not-weak
+
+ }
+ else {
+ // both are not-weak
+ if ( _atomA.section().type() == ld::Section::typeMachHeader ) {
+ pickAtomA();
+ }
+ else if ( _atomB.section().type() == ld::Section::typeMachHeader ) {
+ pickAtomB();
+ }
+ else {
+ if ( _ignoreDuplicates ) {
+ pickLowerOrdinal();
+ }
+ else {
+ _reportDuplicate = true;
+ }
+ }
+ }
+ }
+ }
+
+ void pickCommonsMode(const ld::Atom& dylib, const ld::Atom& proxy) {
+ assert(dylib.definition() == ld::Atom::definitionTentative);
+ assert(proxy.definition() == ld::Atom::definitionProxy);
+ switch ( _options.commonsMode() ) {
+ case Options::kCommonsIgnoreDylibs:
+ if ( _options.warnCommons() )
+ warning("using common symbol %s from %s and ignoring defintion from dylib %s",
+ proxy.name(), proxy.file()->path(), dylib.file()->path());
+ pickAtom(dylib);
+ break;
+ case Options::kCommonsOverriddenByDylibs:
+ if ( _options.warnCommons() )
+ warning("replacing common symbol %s from %s with true definition from dylib %s",
+ proxy.name(), proxy.file()->path(), dylib.file()->path());
+ pickAtom(proxy);
+ break;
+ case Options::kCommonsConflictsDylibsError:
+ throwf("common symbol %s from %s conflicts with defintion from dylib %s",
+ proxy.name(), proxy.file()->path(), dylib.file()->path());
+ }
+ }
+
+ void pickProxyAtom() {
+ // both atoms are definitionProxy
+ // <rdar://problem/5137732> ld should keep looking when it finds a weak definition in a dylib
+ if ( _atomA.combine() == ld::Atom::combineByName ) {
+ pickAtomB();
+ } else if ( _atomB.combine() == ld::Atom::combineByName ) {
+ pickAtomA();
+ } else {
+ throwf("symbol %s exported from both %s and %s\n", _atomA.name(), _atomA.file()->path(), _atomB.file()->path());
+ }
+ }
+
+ void pickAtom() {
+ // First, discriminate by definition
+ switch (_atomA.definition()) {
+ case ld::Atom::definitionRegular:
+ switch (_atomB.definition()) {
+ case ld::Atom::definitionRegular:
+ pickBetweenRegularAtoms();
break;
case ld::Atom::definitionTentative:
- // ignore new tentative atom, because we already have a regular one
- useNew = false;
- checkVisibilityMismatch = true;
- if ( newAtom.size() > existingAtom->size() ) {
- warning("for symbol %s tentative definition of size %llu from %s is "
- "is smaller than the real definition of size %llu from %s",
- newAtom.name(), newAtom.size(), newAtom.file()->path(),
- existingAtom->size(), existingAtom->file()->path());
- }
+ pickAtomA();
break;
case ld::Atom::definitionAbsolute:
- throwf("duplicate symbol %s in %s and %s", name, newAtom.file()->path(), existingAtom->file()->path());
+ _reportDuplicate = true;
+ pickHigherOrdinal();
+ break;
case ld::Atom::definitionProxy:
- // ignore external atom, because we already have a one
- useNew = false;
+ pickAtomA();
break;
}
break;
case ld::Atom::definitionTentative:
- switch ( newAtom.definition() ) {
+ switch (_atomB.definition()) {
case ld::Atom::definitionRegular:
- // replace existing tentative atom with regular one
- if ( newAtom.section().type() == ld::Section::typeMachHeader ) {
- // silently replace tentative __dso_handle with real linker created symbol
- useNew = true;
- }
- else if ( existingAtom->section().type() == ld::Section::typeMachHeader ) {
- // silently replace tentative __dso_handle with real linker created symbol
- useNew = false;
- }
- else {
- checkVisibilityMismatch = true;
- if ( newAtom.size() < existingAtom->size() ) {
- warning("for symbol %s tentative definition of size %llu from %s is "
- "being replaced by a real definition of size %llu from %s",
- newAtom.name(), existingAtom->size(), existingAtom->file()->path(),
- newAtom.size(), newAtom.file()->path());
- }
- if ( newAtom.section().type() == ld::Section::typeCode ) {
- warning("for symbol %s tentative (data) defintion from %s is "
- "being replaced by code from %s", newAtom.name(), existingAtom->file()->path(),
- newAtom.file()->path());
- }
- }
+ pickAtomB();
break;
case ld::Atom::definitionTentative:
- // new and existing are both tentative definitions, use largest
- checkVisibilityMismatch = true;
- if ( newAtom.size() < existingAtom->size() ) {
- useNew = false;
- }
- else {
- if ( newAtom.alignment().trailingZeros() < existingAtom->alignment().trailingZeros() )
- warning("alignment lost in merging tentative definition %s", newAtom.name());
- }
+ pickLargerSize();
break;
case ld::Atom::definitionAbsolute:
- // replace tentative with absolute
- useNew = true;
+ pickHigherOrdinal();
break;
case ld::Atom::definitionProxy:
- // a tentative definition and a dylib definition, so commons-mode decides how to handle
- switch ( _options.commonsMode() ) {
- case Options::kCommonsIgnoreDylibs:
- if ( _options.warnCommons() )
- warning("using common symbol %s from %s and ignoring defintion from dylib %s",
- existingAtom->name(), existingAtom->file()->path(), newAtom.file()->path());
- useNew = false;
- break;
- case Options::kCommonsOverriddenByDylibs:
- if ( _options.warnCommons() )
- warning("replacing common symbol %s from %s with true definition from dylib %s",
- existingAtom->name(), existingAtom->file()->path(), newAtom.file()->path());
- break;
- case Options::kCommonsConflictsDylibsError:
- throwf("common symbol %s from %s conflicts with defintion from dylib %s",
- existingAtom->name(), existingAtom->file()->path(), newAtom.file()->path());
- }
+ pickCommonsMode(_atomA, _atomB);
break;
}
break;
case ld::Atom::definitionAbsolute:
- switch ( newAtom.definition() ) {
+ switch (_atomB.definition()) {
case ld::Atom::definitionRegular:
- throwf("duplicate symbol %s in %s and %s", name, newAtom.file()->path(), existingAtom->file()->path());
+ _reportDuplicate = true;
+ pickHigherOrdinal();
+ break;
case ld::Atom::definitionTentative:
- // ignore new tentative atom, because we already have a regular one
- useNew = false;
+ pickAtomA();
break;
case ld::Atom::definitionAbsolute:
- throwf("duplicate symbol %s in %s and %s", name, newAtom.file()->path(), existingAtom->file()->path());
+ _reportDuplicate = true;
+ pickHigherOrdinal();
+ break;
case ld::Atom::definitionProxy:
- // ignore external atom, because we already have a one
- useNew = false;
+ pickAtomA();
break;
}
break;
case ld::Atom::definitionProxy:
- switch ( newAtom.definition() ) {
+ switch (_atomB.definition()) {
case ld::Atom::definitionRegular:
- // replace external atom with regular one
- useNew = true;
+ pickAtomB();
break;
case ld::Atom::definitionTentative:
- // a tentative definition and a dylib definition, so commons-mode decides how to handle
- switch ( _options.commonsMode() ) {
- case Options::kCommonsIgnoreDylibs:
- if ( _options.warnCommons() )
- warning("using common symbol %s from %s and ignoring defintion from dylib %s",
- newAtom.name(), newAtom.file()->path(), existingAtom->file()->path());
- break;
- case Options::kCommonsOverriddenByDylibs:
- if ( _options.warnCommons() )
- warning("replacing defintion of %s from dylib %s with common symbol from %s",
- newAtom.name(), existingAtom->file()->path(), newAtom.file()->path());
- useNew = false;
- break;
- case Options::kCommonsConflictsDylibsError:
- throwf("common symbol %s from %s conflicts with defintion from dylib %s",
- newAtom.name(), newAtom.file()->path(), existingAtom->file()->path());
- }
+ pickCommonsMode(_atomB, _atomA);
break;
case ld::Atom::definitionAbsolute:
- // replace external atom with absolute one
- useNew = true;
+ pickAtomB();
break;
case ld::Atom::definitionProxy:
- // <rdar://problem/5137732> ld should keep looking when it finds a weak definition in a dylib
- if ( newAtom.combine() == ld::Atom::combineByName ) {
- useNew = false;
- }
- else {
- if ( existingAtom->combine() == ld::Atom::combineByName )
- useNew = true;
- else
- throwf("symbol %s exported from both %s and %s\n", name, newAtom.file()->path(), existingAtom->file()->path());
- }
+ pickProxyAtom();
break;
}
break;
- }
+ }
}
- if ( (existingAtom != NULL) && checkVisibilityMismatch && (newAtom.scope() != existingAtom->scope()) ) {
- warning("%s has different visibility (%s) in %s and (%s) in %s",
- SymbolTable::demangle(newAtom.name()), (newAtom.scope() == 1 ? "hidden" : "default"), newAtom.file()->path(), (existingAtom->scope() == 1 ? "hidden" : "default"), existingAtom->file()->path());
+};
+
+bool SymbolTable::addByName(const ld::Atom& newAtom, bool ignoreDuplicates)
+{
+ bool useNew = true;
+ assert(newAtom.name() != NULL);
+ const char* name = newAtom.name();
+ IndirectBindingSlot slot = this->findSlotForName(name);
+ const ld::Atom* existingAtom = _indirectBindingTable[slot];
+ //fprintf(stderr, "addByName(%p) name=%s, slot=%u, existing=%p\n", &newAtom, newAtom.name(), slot, existingAtom);
+ if ( existingAtom != NULL ) {
+ assert(&newAtom != existingAtom);
+ NameCollisionResolution picker(newAtom, *existingAtom, ignoreDuplicates, _options);
+ if (picker.reportDuplicate()) {
+ addDuplicateSymbol(name, existingAtom);
+ addDuplicateSymbol(name, &newAtom);
+ }
+ useNew = picker.choseAtom(newAtom);
}
if ( useNew ) {
_indirectBindingTable[slot] = &newAtom;
if ( existingAtom != NULL ) {
markCoalescedAway(existingAtom);
-// if ( fOwner.fInitialLoadsDone ) {
-// //fprintf(stderr, "existing %p %s overridden by %p\n", existingAtom, existingAtom->name(), &newAtom);
-// fOwner.fAtomsOverriddenByLateLoads.insert(existingAtom);
-// }
}
if ( newAtom.scope() == ld::Atom::scopeGlobal ) {
if ( newAtom.definition() == ld::Atom::definitionTentative ) {
}
+
+struct StrcmpSorter {
+ bool operator() (const char* i,const char* j) {
+ if (i==NULL)
+ return true;
+ if (j==NULL)
+ return false;
+ return strcmp(i, j)<0;}
+};
+
void SymbolTable::undefines(std::vector<const char*>& undefs)
{
// return all names in _byNameTable that have no associated atom
undefs.push_back(it->first);
}
// sort so that undefines are in a stable order (not dependent on hashing functions)
- std::sort(undefs.begin(), undefs.end());
+ struct StrcmpSorter strcmpSorter;
+ std::sort(undefs.begin(), undefs.end(), strcmpSorter);
}
return _indirectBindingTable[slot];
}
-extern "C" char* __cxa_demangle (const char* mangled_name,
- char* buf,
- size_t* n,
- int* status);
-
-const char* SymbolTable::demangle(const char* sym)
-{
- // only try to demangle symbols if -demangle on command line
- if ( !_s_doDemangle )
- return sym;
-
- // only try to demangle symbols that look like C++ symbols
- if ( strncmp(sym, "__Z", 3) != 0 )
- return sym;
-
- static size_t size = 1024;
- static char* buff = (char*)malloc(size);
- int status;
-
- char* result = __cxa_demangle(&sym[1], buff, &size, &status);
- if ( result != NULL ) {
- // if demangling succesful, keep buffer for next demangle
- buff = result;
- return buff;
- }
- return sym;
-}
-
-
void SymbolTable::printStatistics()
{
// fprintf(stderr, "cstring table size: %lu, bucket count: %lu, hash func called %u times\n",
typedef std::map<IndirectBindingSlot, const char*> SlotToName;
typedef __gnu_cxx::hash_map<const char*, CStringToSlot*, __gnu_cxx::hash<const char*>, CStringEquals> NameToMap;
+
+ typedef std::vector<const ld::Atom *> DuplicatedSymbolAtomList;
+ typedef std::map<const char *, DuplicatedSymbolAtomList * > DuplicateSymbols;
public:
byNameIterator begin() { return byNameIterator(_byNameTable.begin(),_indirectBindingTable); }
byNameIterator end() { return byNameIterator(_byNameTable.end(),_indirectBindingTable); }
void printStatistics();
- static const char* demangle(const char* sym);
// from ld::IndirectBindingTable
virtual const char* indirectName(IndirectBindingSlot slot) const;
virtual const ld::Atom* indirectAtom(IndirectBindingSlot slot) const;
+
+ // Prints the duplicated symbols to stderr and throws. Only valid to call if hasDuplicateSymbols() returns true.
+ void checkDuplicateSymbols() const;
+
private:
bool addByName(const ld::Atom& atom, bool ignoreDuplicates);
bool addByContent(const ld::Atom& atom);
bool addByReferences(const ld::Atom& atom);
void markCoalescedAway(const ld::Atom* atom);
+
+ // Tracks duplicated symbols. Each call adds file to the list of files defining symbol.
+ // The file list is uniqued per symbol, so calling multiple times for the same symbol/file pair is permitted.
+ void addDuplicateSymbol(const char *symbol, const ld::Atom* atom);
const Options& _options;
NameToSlot _byNameTable;
std::vector<const ld::Atom*>& _indirectBindingTable;
bool _hasExternalTentativeDefinitions;
- static bool _s_doDemangle;
+ DuplicateSymbols _duplicateSymbols;
};
--- /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@
+ */
+
+
+//
+// blob - generic extensible binary blob frame
+//
+#include "blob.h"
+
+namespace Security {
+
+
+//
+// Content access and validation calls
+//
+char *BlobCore::stringAt(Offset offset)
+{
+ char *s = at<char>(offset);
+ if (offset < this->length() && memchr(s, 0, this->length() - offset))
+ return s;
+ else
+ return NULL;
+}
+
+const char *BlobCore::stringAt(Offset offset) const
+{
+ const char *s = at<const char>(offset);
+ if (offset < this->length() && memchr(s, 0, this->length() - offset))
+ return s;
+ else
+ return NULL;
+}
+
+
+//
+// Read a blob from a standard file stream.
+// Reads in one pass, so it's suitable for transmission over pipes and networks.
+// The blob is allocated with malloc(3).
+// On error, sets errno and returns NULL; in which case the input stream may
+// be partially consumed.
+//
+BlobCore *BlobCore::readBlob(int fd, size_t offset, uint32_t magic, size_t minSize, size_t maxSize)
+{
+ BlobCore header;
+ if (::pread(fd, &header, sizeof(header), offset) == sizeof(header))
+ if (header.validateBlob(magic, minSize, maxSize))
+ if (BlobCore *blob = (BlobCore *)malloc(header.length())) {
+ memcpy(blob, &header, sizeof(header));
+ size_t remainder = header.length() - sizeof(header);
+ if (::pread(fd, blob+1, remainder, offset + sizeof(header)) == ssize_t(remainder))
+ return blob;
+ free(blob);
+ errno = EINVAL;
+ }
+ return NULL;
+}
+
+BlobCore *BlobCore::readBlob(int fd, uint32_t magic, size_t minSize, size_t maxSize)
+{
+ BlobCore header;
+ if (::read(fd, &header, sizeof(header)) == sizeof(header))
+ if (header.validateBlob(magic, minSize, maxSize))
+ if (BlobCore *blob = (BlobCore *)malloc(header.length())) {
+ memcpy(blob, &header, sizeof(header));
+ size_t remainder = header.length() - sizeof(header);
+ if (::read(fd, blob+1, remainder) == ssize_t(remainder))
+ return blob;
+ free(blob);
+ errno = EINVAL;
+ }
+ return NULL;
+}
+
+BlobCore *BlobCore::readBlob(std::FILE *file, uint32_t magic, size_t minSize, size_t maxSize)
+{
+ BlobCore header;
+ if (::fread(&header, sizeof(header), 1, file) == 1)
+ if (header.validateBlob(magic, minSize, maxSize))
+ if (BlobCore *blob = (BlobCore *)malloc(header.length())) {
+ memcpy(blob, &header, sizeof(header));
+ if (::fread(blob+1, header.length() - sizeof(header), 1, file) == 1)
+ return blob;
+ free(blob);
+ errno = EINVAL;
+ }
+ return NULL;
+}
+
+
+//
+// BlobWrappers
+//
+BlobWrapper *BlobWrapper::alloc(size_t length, Magic magic /* = _magic */)
+{
+ size_t wrapLength = length + sizeof(BlobCore);
+ BlobWrapper *w = (BlobWrapper *)malloc(wrapLength);
+ w->BlobCore::initialize(magic, wrapLength);
+ return w;
+}
+
+BlobWrapper *BlobWrapper::alloc(const void *data, size_t length, Magic magic /* = _magic */)
+{
+ BlobWrapper *w = alloc(length, magic);
+ memcpy(w->data(), data, w->length());
+ return w;
+}
+
+
+} // Security
--- /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@
+ */
+
+//
+// blob - generic extensible binary blob frame
+//
+// To define a new type of binary blob:
+// class MyBlob : public Blob<MyBlob, magic number> { ... }
+// Pick a unique magic number (32-bit). Blobs are understood to be a MyBlob
+// header possibly followed by more data as a contiguous memory area. Length
+// is overall (including the header), so a fixed-size blob would have length
+// sizeof(MyBlob). Both length and magic are stored in NBO.
+//
+// You are highly encouraged to follow these rules:
+// Store all integers in NBO, including offsets.
+// Use internal offsets to "point to" dynamically-sized elements past the
+// header (using the at<Type>(offset) method for access).
+// Don't use pointers in your blob.
+// If you follow those rules, your blobs will be fully relocatable, byte-order
+// independent, and generally spreading happiness around your code.
+//
+#ifndef _H_BLOB
+#define _H_BLOB
+
+#include "endian.h"
+#include "memutils.h"
+#include <errno.h>
+
+namespace Security {
+
+enum {
+ // CodeDirectory slot numbers, used to index the EmbeddedSignatureBlob (from codedirectory.h, internal)
+ cdRequirementsSlot = 2 // embedded signature: internal requirements
+};
+
+enum {
+ // Code Signing magic blob types (from <Security/CSCommonPriv.h>)
+ kSecCodeMagicRequirement = 0xfade0c00, /* single requirement */
+ kSecCodeMagicRequirementSet = 0xfade0c01, /* requirement set */
+ kSecCodeMagicEmbeddedSignature = 0xfade0cc0, /* single-architecture embedded signature */
+
+ kSecCodeMagicDRList = 0xfade0c05
+};
+
+enum {
+ // from CSCommon.h
+ kSecDesignatedRequirementType = 3 /* designated requirement */
+};
+
+//
+// All blobs in memory have this form.
+// You can have polymorphic memory blobs (C style) using different magics numbers.
+//
+class BlobCore {
+public:
+ typedef uint32_t Offset;
+ typedef uint32_t Magic;
+
+ Magic magic() const { return mMagic; }
+ size_t length() const { return mLength; }
+
+ void initialize(Magic mag, size_t len = 0)
+ { mMagic = mag; mLength = len; }
+
+ bool validateBlob(Magic magic, size_t minSize = 0, size_t maxSize = 0) const;
+
+ template <class T, class Offset>
+ T *at(Offset offset)
+ { return LowLevelMemoryUtilities::increment<T>(this, offset); }
+
+ template <class T, class Offset>
+ const T *at(Offset offset) const
+ { return LowLevelMemoryUtilities::increment<const T>(this, offset); }
+
+ template <class Offset1, class Offset2>
+ bool contains(Offset1 offset, Offset2 size) const
+ { return offset >= 0 && size_t(offset) >= sizeof(BlobCore) && (size_t(offset) + size) <= this->length(); }
+
+ template <class Base, class Offset>
+ bool contains(Base *ptr, Offset size) const
+ { return contains(LowLevelMemoryUtilities::difference(ptr, this), size); }
+
+ char *stringAt(Offset offset);
+ const char *stringAt(Offset offset) const;
+
+ void *data() { return this; }
+ const void *data() const { return this; }
+ void length(size_t size) { mLength = size; }
+
+ template <class BlobType>
+ bool is() const { return magic() == BlobType::typeMagic; }
+
+ static BlobCore *readBlob(std::FILE *file) { return readBlob(file, 0, 0, 0); }
+ static BlobCore *readBlob(int fd) { return readBlob(fd, 0, 0, 0); }
+
+protected:
+ static BlobCore *readBlob(std::FILE *file, uint32_t magic, size_t minSize, size_t maxSize); // streaming
+ static BlobCore *readBlob(int fd, uint32_t magic, size_t minSize, size_t maxSize); // streaming
+ static BlobCore *readBlob(int fd, size_t offset, uint32_t magic, size_t minSize, size_t maxSize); // pread(2)@offset
+
+protected:
+ Endian<uint32_t> mMagic;
+ Endian<uint32_t> mLength;
+};
+
+
+// basic validation helper
+inline bool BlobCore::validateBlob(Magic mag, size_t minSize /* = 0 */, size_t maxSize /* = 0 */) const
+{
+ uint32_t len = this->mLength;
+ if (mag && (mag != this->mMagic)) {
+ errno = EINVAL;
+ return false;
+ }
+ if (minSize ? (len < minSize) : (len < sizeof(BlobCore))) {
+ errno = EINVAL;
+ return false;
+ }
+ if (maxSize && len > maxSize) {
+ errno = ENOMEM;
+ return false;
+ }
+ return true;
+}
+
+
+//
+// Typed Blobs (BlobCores that know their real type and magic)
+//
+template <class BlobType, uint32_t _magic>
+class Blob: public BlobCore {
+public:
+ void initialize(size_t size = 0) { BlobCore::initialize(_magic, size); }
+
+ static const Magic typeMagic = _magic;
+
+ bool validateBlob() const
+ { return BlobCore::validateBlob(_magic, sizeof(BlobType)); }
+
+ bool validateBlob(size_t extLength) const
+ { return validateBlob() && mLength == extLength; }
+
+ static BlobType *specific(BlobCore *blob, bool unalloc = false)
+ {
+ if (BlobType *p = static_cast<BlobType *>(blob)) {
+ if (p->validateBlob())
+ return p;
+ if (unalloc)
+ ::free(p);
+ }
+ return NULL;
+ }
+
+ static const BlobType *specific(const BlobCore *blob)
+ {
+ const BlobType *p = static_cast<const BlobType *>(blob);
+ if (p && p->validateBlob())
+ return p;
+ return NULL;
+ }
+
+ BlobType *clone() const
+ { assert(validateBlob()); return specific(this->BlobCore::clone()); }
+
+ static BlobType *readBlob(int fd)
+ { return specific(BlobCore::readBlob(fd, _magic, sizeof(BlobType), 0), true); }
+
+ static BlobType *readBlob(int fd, size_t offset, size_t maxSize = 0)
+ { return specific(BlobCore::readBlob(fd, offset, _magic, sizeof(BlobType), maxSize), true); }
+
+ static BlobType *readBlob(std::FILE *file)
+ { return specific(BlobCore::readBlob(file, _magic, sizeof(BlobType), 0), true); }
+};
+
+
+//
+// A generic blob wrapped around arbitrary (flat) binary data.
+// This can be used to "regularize" plain binary data, so it can be handled
+// as a genuine Blob (e.g. for insertion into a SuperBlob).
+//
+class BlobWrapper : public Blob<BlobWrapper, 0xfade0b01> {
+public:
+ static BlobWrapper *alloc(size_t length, Magic magic = BlobWrapper::typeMagic);
+ static BlobWrapper *alloc(const void *data, size_t length, Magic magic = BlobWrapper::typeMagic);
+
+ unsigned char dataArea[0];
+
+ // override data/length to point to the payload (only)
+ void *data() { return dataArea; }
+ const void *data() const { return dataArea; }
+ size_t length() const { return BlobCore::length() - sizeof(BlobCore); }
+};
+
+
+} // Security
+
+#endif //_H_BLOB
--- /dev/null
+/*
+ * Copyright (c) 2002-2004 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@
+ */
+
+
+/*
+ * cssm utilities
+ */
+#ifndef _H_ENDIAN
+#define _H_ENDIAN
+
+#include <machine/endian.h>
+#include <libkern/OSByteOrder.h>
+//#include <security_utilities/utilities.h>
+#include "memutils.h"
+
+namespace Security {
+
+
+//
+// Encode/decode operations by type, overloaded.
+// You can use these functions directly, but consider using
+// the higher-level constructs below instead.
+//
+#ifdef __LP64__
+static inline unsigned long h2n(unsigned long v) { return OSSwapHostToBigInt64(v); }
+static inline unsigned long n2h(unsigned long v) { return OSSwapBigToHostInt64(v); }
+static inline unsigned long flip(unsigned long v) { return OSSwapInt64(v); }
+static inline signed long h2n(signed long v) { return OSSwapHostToBigInt64(v); }
+static inline signed long n2h(signed long v) { return OSSwapBigToHostInt64(v); }
+static inline signed long flip(signed long v) { return OSSwapInt64(v); }
+#else
+static inline unsigned long h2n(unsigned long v) { return htonl(v); }
+static inline unsigned long n2h(unsigned long v) { return ntohl(v); }
+static inline unsigned long flip(unsigned long v) { return OSSwapInt32(v); }
+static inline signed long h2n(signed long v) { return htonl(v); }
+static inline signed long n2h(signed long v) { return ntohl(v); }
+static inline signed long flip(signed long v) { return OSSwapInt32(v); }
+#endif
+
+static inline unsigned long long h2n(unsigned long long v) { return OSSwapHostToBigInt64(v); }
+static inline unsigned long long n2h(unsigned long long v) { return OSSwapBigToHostInt64(v); }
+static inline unsigned long long flip(unsigned long long v) { return OSSwapInt64(v); }
+static inline long long h2n(long long v) { return OSSwapHostToBigInt64(v); }
+static inline long long n2h(long long v) { return OSSwapBigToHostInt64(v); }
+static inline long long flip(long long v) { return OSSwapInt64(v); }
+
+static inline unsigned int h2n(unsigned int v) { return htonl(v); }
+static inline unsigned int n2h(unsigned int v) { return ntohl(v); }
+static inline unsigned int flip(unsigned int v) { return OSSwapInt32(v); }
+static inline signed int h2n(int v) { return htonl(v); }
+static inline signed int n2h(int v) { return ntohl(v); }
+static inline signed int flip(int v) { return OSSwapInt32(v); }
+
+static inline unsigned short h2n(unsigned short v) { return htons(v); }
+static inline unsigned short n2h(unsigned short v) { return ntohs(v); }
+static inline unsigned short flip(unsigned short v) { return OSSwapInt16(v); }
+static inline signed short h2n(signed short v) { return htons(v); }
+static inline signed short n2h(signed short v) { return ntohs(v); }
+static inline signed short flip(signed short v) { return OSSwapInt16(v); }
+
+static inline unsigned char h2n(unsigned char v) { return v; }
+static inline unsigned char n2h(unsigned char v) { return v; }
+static inline unsigned char flip(unsigned char v) { return v; }
+static inline signed char h2n(signed char v) { return v; }
+static inline signed char n2h(signed char v) { return v; }
+static inline signed char flip(signed char v) { return v; }
+
+
+//
+// Flip pointers
+//
+template <class Base>
+static inline Base *h2n(Base *p) { return (Base *)h2n(uintptr_t(p)); }
+
+template <class Base>
+static inline Base *n2h(Base *p) { return (Base *)n2h(uintptr_t(p)); }
+
+
+//
+// In-place fix operations
+//
+template <class Type>
+static inline void h2ni(Type &v) { v = h2n(v); }
+
+template <class Type>
+static inline void n2hi(Type &v) { v = n2h(v); }
+
+//
+// Endian<SomeType> keeps NBO values in memory and converts
+// during loads and stores. This presumes that you are using
+// memory blocks thare are read/written/mapped as amorphous byte
+// streams, but want to be byte-order clean using them.
+//
+// The generic definition uses h2n/n2h to flip bytes. Feel free
+// to declare specializations of Endian<T> as appropriate.
+//
+// Note well that the address of an Endian<T> is not an address-of-T,
+// and there is no conversion available.
+//
+template <class Type>
+class Endian {
+public:
+ typedef Type Value;
+ Endian() : mValue(Type(0)) { }
+ Endian(Value v) : mValue(h2n(v)) { }
+
+ operator Value () const { return n2h(mValue); }
+ Endian &operator = (Value v) { mValue = h2n(v); return *this; }
+
+private:
+ Value mValue;
+};
+
+
+} // end namespace Security
+
+
+#endif //_H_ENDIAN
--- /dev/null
+/*
+ * Copyright (c) 2000-2004 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@
+ */
+
+
+//
+// memutils - memory-related low-level utilities for easier living
+//
+#ifndef _H_MEMUTILS
+#define _H_MEMUTILS
+
+//#include <security_utilities/utilities.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <algorithm>
+
+
+//
+// Encapsulate these very sharp tools in a separate (ugly-named) namespace
+//
+namespace Security {
+namespace LowLevelMemoryUtilities {
+
+
+//
+// The default system alignment.
+//
+static const size_t systemAlignment = 4;
+
+
+//
+// Get the local alignment for a type, as used by the acting compiler.
+//
+template <class T>
+inline size_t alignof() { struct { char c; T t; } s; return sizeof(s) - sizeof(T); }
+
+
+//
+// Get the local offset of a field in a (struct or class) type, as layed out
+// by the acting compiler.
+// NB: "offsetof" is a standard-defined macro. Don't use that.
+//
+template <class Type, class Field>
+inline size_t fieldOffsetOf(Field (Type::*field))
+{
+ Type *object = 0; // we don't REALLY need this, but it's easier to read
+ return uintptr_t(&(object->*field)) - uintptr_t(object);
+}
+
+
+//
+// Round up a size or pointer to an alignment boundary.
+// Alignment must be a power of two; default is default alignment.
+//
+inline size_t alignUp(size_t size, size_t alignment = systemAlignment)
+{
+ return ((size - 1) & ~(alignment - 1)) + alignment;
+}
+
+inline void *alignUp(void *p, size_t alignment = systemAlignment)
+{
+ return reinterpret_cast<void *>(alignUp(uintptr_t(p), alignment));
+}
+
+inline const void *alignUp(const void *p, size_t alignment = systemAlignment)
+{
+ return reinterpret_cast<const void *>(alignUp(uintptr_t(p), alignment));
+}
+
+template <class T>
+inline const T *increment(const void *p, ptrdiff_t offset)
+{ return reinterpret_cast<const T *>(uintptr_t(p) + offset); }
+
+template <class T>
+inline T *increment(void *p, ptrdiff_t offset)
+{ return reinterpret_cast<T *>(uintptr_t(p) + offset); }
+
+inline const void *increment(const void *p, ptrdiff_t offset)
+{ return increment<const void>(p, offset); }
+
+inline void *increment(void *p, ptrdiff_t offset)
+{ return increment<void>(p, offset); }
+
+template <class T>
+inline const T *increment(const void *p, ptrdiff_t offset, size_t alignment)
+{ return increment<const T>(alignUp(p, alignment), offset); }
+
+template <class T>
+inline T *increment(void *p, ptrdiff_t offset, size_t alignment)
+{ return increment<T>(alignUp(p, alignment), offset); }
+
+inline const void *increment(const void *p, ptrdiff_t offset, size_t alignment)
+{ return increment<const void>(p, offset, alignment); }
+
+inline void *increment(void *p, ptrdiff_t offset, size_t alignment)
+{ return increment<void>(p, offset, alignment); }
+
+inline ptrdiff_t difference(const void *p1, const void *p2)
+{ return uintptr_t(p1) - uintptr_t(p2); }
+
+
+} // end namespace LowLevelMemoryUtilities
+} // end namespace Security
+
+#endif //_H_MEMUTILS
--- /dev/null
+//
+// SuperBlob - a typed bag of Blobs
+//
+#ifndef _H_SUPERBLOB
+#define _H_SUPERBLOB
+
+#include "blob.h"
+#include <assert.h>
+#include <utility>
+#include <map>
+
+using namespace std;
+
+namespace Security {
+
+
+//
+// A SuperBlob is a Blob that contains multiple sub-Blobs of varying type.
+// The SuperBlob is contiguous and contains a directory of its sub-blobs.
+// A Maker is included.
+//
+// SuperBlobCore lets you define your own SuperBlob type. To just use a generic
+// SuperBlob, use SuperBlob<> below.
+//
+template <class _BlobType, uint32_t _magic, class _Type>
+class SuperBlobCore: public Blob<_BlobType, _magic> {
+public:
+ class Maker; friend class Maker;
+
+ typedef _Type Type;
+
+ // echoes from parent BlobCore (the C++ type system is too restrictive here)
+ typedef BlobCore::Offset Offset;
+ template <class BlobType> BlobType *at(Offset offset) { return BlobCore::at<BlobType>(offset); }
+ template <class BlobType> const BlobType *at(Offset offset) const { return BlobCore::at<BlobType>(offset); }
+
+ void setup(size_t size, unsigned cnt)
+ { this->initialize(size); this->mCount = cnt; }
+
+ struct Index {
+ Endian<Type> type; // type of sub-Blob
+ Endian<Offset> offset; // starting offset
+ };
+
+ bool validateBlob(size_t maxSize = 0) const;
+
+ unsigned count() const { return mCount; }
+
+ // access by index number
+ Type type(unsigned n) const { assert(n < mCount); return mIndex[n].type; }
+ const BlobCore *blob(unsigned n) const { assert(n < mCount); Offset off=mIndex[n].offset; return off ? at<const BlobCore>(off) : NULL; }
+
+ template <class BlobType>
+ const BlobType *blob(unsigned n) const { return BlobType::specific(blob(n)); }
+
+ // access by index type (assumes unique types)
+ const BlobCore *find(Type type) const;
+ template <class BlobType>
+ const BlobType *find(Type t) const { return BlobType::specific(find(t)); }
+
+private:
+ Endian<uint32_t> mCount; // number of sub-Blobs following
+ Index mIndex[0]; // <count> IndexSlot structures
+ // followed by sub-Blobs, packed and ordered in an undefined way
+};
+
+
+template <class _BlobType, uint32_t _magic, class _Type>
+inline bool SuperBlobCore<_BlobType, _magic, _Type>::validateBlob(size_t maxSize /* = 0 */) const
+{
+ unsigned cnt = mCount;
+ size_t ixLimit = sizeof(SuperBlobCore) + cnt * sizeof(Index); // end of index vector
+ if (!BlobCore::validateBlob(_magic, ixLimit, maxSize))
+ return false;
+
+ for (const Index *ix = mIndex + cnt - 1; ix >= mIndex; ix--) {
+ Offset offset = ix->offset;
+ if ( offset == 0 )
+ continue; // offset==0 means unused entry
+ if (offset < ixLimit // offset not too small
+ || offset + sizeof(BlobCore) > this->length() // fits Blob header (including length field)
+ || offset + at<const BlobCore>(offset)->length() > this->length()) // fits entire blob
+ return false;
+ }
+ return true;
+}
+
+
+//
+// A generic SuperBlob ready for use. You still need to specify a magic number.
+//
+template <uint32_t _magic, class _Type = uint32_t>
+class SuperBlob : public SuperBlobCore<SuperBlob<_magic, _Type>, _magic, _Type> {
+};
+
+
+template <class _BlobType, uint32_t _magic, class _Type>
+const BlobCore *SuperBlobCore<_BlobType, _magic, _Type>::find(Type t) const
+{
+ for (unsigned slot = 0; slot < mCount; slot++) {
+ if (mIndex[slot].type == t) {
+ uint32_t off = mIndex[slot].offset;
+ if ( off == 0 )
+ return NULL;
+ else
+ return at<const BlobCore>(off);
+ }
+ }
+ return NULL; // not found
+}
+
+
+//
+// A SuperBlob::Maker simply assembles multiple Blobs into a single, indexed
+// super-blob. Just add() sub-Blobs by type and call build() to get
+// the result, malloc'ed. A Maker is not reusable.
+// Maker can repeatedly make SuperBlobs from the same (cached) inputs.
+// It can also tell you how big its output will be, given established contents
+// plus (optional) additional sizes of blobs yet to come.
+//
+template <class _BlobType, uint32_t _magic, class _Type>
+class SuperBlobCore<_BlobType, _magic, _Type>::Maker {
+public:
+ Maker() { }
+
+ Maker(const Maker &src)
+ {
+ for (typename BlobMap::iterator it = mPieces.begin(); it != mPieces.end(); ++it)
+ mPieces.insert(make_pair(it->first, it->second->clone()));
+ }
+
+ ~Maker()
+ {
+ for (typename BlobMap::iterator it = mPieces.begin(); it != mPieces.end(); ++it)
+ ::free(it->second);
+ }
+
+ void add(Type type, BlobCore *blob); // takes ownership of blob
+ void add(const _BlobType *blobs); // copies all blobs
+ void add(const Maker &maker); // ditto
+
+ size_t size(size_t size1 = 0, ...) const; // size with optional additional blob sizes
+ _BlobType *make() const; // create (malloc) and return SuperBlob
+ _BlobType *operator () () const { return make(); }
+
+private:
+ typedef std::map<Type, BlobCore *> BlobMap;
+ BlobMap mPieces;
+};
+
+
+//
+// Add a Blob to a SuperBlob::Maker.
+// This takes ownership of the blob, which must have been malloc'ed.
+// Any previous value set for this Type will be freed immediately.
+//
+template <class _BlobType, uint32_t _magic, class _Type>
+void SuperBlobCore<_BlobType, _magic, _Type>::Maker::add(Type type, BlobCore *blob)
+{
+ pair<typename BlobMap::iterator, bool> r = mPieces.insert(make_pair(type, blob));
+ if (!r.second) { // already there
+ //secdebug("superblob", "Maker %p replaces type=%d", this, type);
+ ::free(r.first->second);
+ r.first->second = blob;
+ }
+}
+
+template <class _BlobType, uint32_t _magic, class _Type>
+void SuperBlobCore<_BlobType, _magic, _Type>::Maker::add(const _BlobType *blobs)
+{
+ for (uint32_t ix = 0; ix < blobs->mCount; ix++)
+ this->add(blobs->mIndex[ix].type, blobs->blob(ix)->clone());
+}
+
+template <class _BlobType, uint32_t _magic, class _Type>
+void SuperBlobCore<_BlobType, _magic, _Type>::Maker::add(const Maker &maker)
+{
+ for (typename BlobMap::const_iterator it = maker.mPieces.begin(); it != maker.mPieces.end(); ++it)
+ this->add(it->first, it->second->clone());
+}
+
+
+//
+// Calculate the size the new SuperBlob would have, given the contents of the Maker
+// so far, plus additional blobs with the sizes given.
+//
+template <class _BlobType, uint32_t _magic, class _Type>
+size_t SuperBlobCore<_BlobType, _magic, _Type>::Maker::size(size_t size1, ...) const
+{
+ // count established blobs
+ unsigned count = mPieces.size();
+ size_t total = 0;
+ for (typename BlobMap::const_iterator it = mPieces.begin(); it != mPieces.end(); ++it) {
+ if ( it->second != NULL )
+ total += (it->second->length() + 3) & (-4); // 4-byte align each element
+ }
+
+ // add preview blob sizes to calculation (if any)
+ if (size1) {
+ va_list args;
+ va_start(args, size1);
+ do {
+ count++;
+ total += size1;
+ size1 = va_arg(args, size_t);
+ } while (size1);
+ va_end(args);
+ }
+
+ return sizeof(SuperBlobCore) + count * sizeof(Index) + total;
+}
+
+
+//
+// Finish SuperBlob construction and return the new, malloc'ed, SuperBlob.
+// This can be done repeatedly.
+//
+template <class _BlobType, uint32_t _magic, class _Type>
+_BlobType *SuperBlobCore<_BlobType, _magic, _Type>::Maker::make() const
+{
+ Offset pc = sizeof(SuperBlobCore) + mPieces.size() * sizeof(Index);
+ Offset total = size();
+ _BlobType *result = (_BlobType *)calloc(1, total);
+ if (!result)
+ throw ENOMEM;
+ result->setup(total, mPieces.size());
+ unsigned n = 0;
+ for (typename BlobMap::const_iterator it = mPieces.begin(); it != mPieces.end(); ++it) {
+ result->mIndex[n].type = it->first;
+ BlobCore* b = it->second;
+ if ( b != NULL ) {
+ result->mIndex[n].offset = pc;
+ memcpy(result->template at<unsigned char>(pc), b, b->length());
+ pc += ((b->length() + 3) & (-4)); // 4-byte align each element
+ }
+ else {
+ result->mIndex[n].offset = 0;
+ }
+ n++;
+ }
+ //secdebug("superblob", "Maker %p assembles %ld blob(s) into %p (size=%d)",
+ // this, mPieces.size(), result, total);
+ return result;
+}
+
+
+} // Security
+
+#endif //_H_SUPERBLOB
#include "InputFiles.h"
#include "Resolver.h"
#include "OutputFile.h"
+#include "Snapshot.h"
#include "passes/stubs/make_stubs.h"
#include "passes/dtrace_dof.h"
}
}
+
+
+static const char* sOverridePathlibLTO = NULL;
+
+//
+// This is magic glue that overrides the default behaviour
+// of lazydylib1.o which is used to lazily load libLTO.dylib.
+//
+extern "C" const char* dyld_lazy_dylib_path_fix(const char* path);
+const char* dyld_lazy_dylib_path_fix(const char* path)
+{
+ if ( sOverridePathlibLTO != NULL )
+ return sOverridePathlibLTO;
+ else
+ return path;
+}
+
+
+
int main(int argc, const char* argv[])
{
const char* archName = NULL;
Options options(argc, argv);
InternalState state(options);
+ // allow libLTO to be overridden by command line -lto_library
+ sOverridePathlibLTO = options.overridePathlibLTO();
+
// gather vm stats
if ( options.printStatistics() )
getVMInfo(statistics.vmStart);
statistics.startResolver = mach_absolute_time();
ld::tool::Resolver resolver(options, inputFiles, state);
resolver.resolve();
-
+
// add dylibs used
statistics.startDylibs = mach_absolute_time();
inputFiles.dylibs(state);
// implement assert() function to print out a backtrace before aborting
void __assert_rtn(const char* func, const char* file, int line, const char* failedexpr)
{
- fprintf(stderr, "Assertion failed: (%s), function %s, file %s, line %d.\n", failedexpr, func, file, line);
+ Snapshot *snapshot = Snapshot::globalSnapshot;
+
+ snapshot->setSnapshotMode(Snapshot::SNAPSHOT_DEBUG);
+ snapshot->createSnapshot();
+ snapshot->recordAssertionMessage("Assertion failed: (%s), function %s, file %s, line %d.\n", failedexpr, func, file, line);
void* callStack[128];
int depth = ::backtrace(callStack, 128);
}
long offset = (uintptr_t)callStack[i] - (uintptr_t)info.dli_saddr;
fprintf(stderr, "%d %p %s + %ld\n", i, callStack[i], symboName, offset);
+ snapshot->recordAssertionMessage("%d %p %s + %ld\n", i, callStack[i], symboName, offset);
}
+ fprintf(stderr, "A linker snapshot was created at:\n\t%s\n", snapshot->rootDir());
+ fprintf(stderr, "ld: Assertion failed: (%s), function %s, file %s, line %d.\n", failedexpr, func, file, line);
exit(1);
}
#endif
virtual void doFile(const class File&) = 0;
};
- File(const char* pth, time_t modTime, uint32_t ord)
- : _path(pth), _modTime(modTime), _ordinal(ord) { }
+ //
+ // ld::File::Ordinal
+ //
+ // Codifies the rules of ordering input files for symbol precedence. These are:
+ // - Input files listed on the command line are ordered according to their index in the argument list.
+ // - Input files listed in a file list are ordered first at the index of the file list argument, then
+ // by index in the file list
+ // - Input files extracted from archives are ordered using the ordinal of the archive itself plus the
+ // index of the object file within the archive
+ // - Indirect dylibs are ordered after all input files derived from the command line, in the order that
+ // they are discovered.
+ // - The LTO object file is last.
+ //
+ class Ordinal
+ {
+ private:
+ // The actual numeric ordinal. Lower values have higher precedence and a zero value is invalid.
+ // The 64 bit ordinal is broken into 4 16 bit chunks. The high 16 bits are a "partition" that
+ // is used to distinguish major ordinal groups: command line, indirect dylib, LTO.
+ // The remaining chunks are used according to the partition (see below).
+ uint64_t _ordinal;
+
+ Ordinal (uint64_t ordinal) : _ordinal(ordinal) {}
+
+ enum { ArgListPartition=0, IndirectDylibPartition=1, LTOPartition = 2, InvalidParition=0xffff };
+ Ordinal(uint16_t partition, uint16_t majorIndex, uint16_t minorIndex, uint16_t counter) {
+ _ordinal = ((uint64_t)partition<<48) | ((uint64_t)majorIndex<<32) | ((uint64_t)minorIndex<<16) | ((uint64_t)counter<<0);
+ }
+
+ const uint16_t partition() const { return (_ordinal>>48)&0xffff; }
+ const uint16_t majorIndex() const { return (_ordinal>>32)&0xffff; }
+ const uint16_t minorIndex() const { return (_ordinal>>16)&0xffff; }
+ const uint16_t counter() const { return (_ordinal>>00)&0xffff; }
+
+ const Ordinal nextMajorIndex() const { assert(majorIndex() < 0xffff); return Ordinal(_ordinal+((uint64_t)1<<32)); }
+ const Ordinal nextMinorIndex() const { assert(minorIndex() < 0xffff); return Ordinal(_ordinal+((uint64_t)1<<16)); }
+ const Ordinal nextCounter() const { assert(counter() < 0xffff); return Ordinal(_ordinal+((uint64_t)1<<0)); }
+
+ public:
+ Ordinal() : _ordinal(0) {};
+
+ static const Ordinal NullOrdinal() { return Ordinal((uint64_t)0); }
+
+ const bool validOrdinal() const { return _ordinal != 0; }
+
+ bool operator ==(const Ordinal& rhs) const { return _ordinal == rhs._ordinal; }
+ bool operator !=(const Ordinal& rhs) const { return _ordinal != rhs._ordinal; }
+ bool operator < (const Ordinal& rhs) const { return _ordinal < rhs._ordinal; }
+ bool operator > (const Ordinal& rhs) const { return _ordinal > rhs._ordinal; }
+
+ // For ordinals derived from the command line args the partition is ArgListPartition
+ // The majorIndex is the arg index that pulls in the file, file list, or archive.
+ // The minorIndex is used for files pulled in by a file list and the value is the index of the file in the file list.
+ // The counter is used for .a files and the value is the index of the object in the archive.
+ // Thus, an object pulled in from a .a that was listed in a file list could use all three fields.
+ static const Ordinal makeArgOrdinal(uint16_t argIndex) { return Ordinal(ArgListPartition, argIndex, 0, 0); };
+ const Ordinal nextFileListOrdinal() const { return nextMinorIndex(); }
+ const Ordinal archiveOrdinalWithMemberIndex(uint16_t index) const { return Ordinal(ArgListPartition, majorIndex(), minorIndex(), index); }
+
+ // For indirect libraries the partition is IndirectDylibPartition and the counter is used or order the libraries.
+ static const ld::File::Ordinal indirectDylibBase() { return Ordinal(IndirectDylibPartition, 0, 0, 0); }
+ const Ordinal nextIndirectDylibOrdinal() const { return nextCounter(); }
+
+ // For the LTO mach-o the partition is LTOPartition. As there is only one LTO file no other fields are needed.
+ static const ld::File::Ordinal LTOOrdinal() { return Ordinal(LTOPartition, 0, 0, 0); }
+ };
+
+ typedef enum { Reloc, Dylib, Archive, Other } Type;
+
+ File(const char* pth, time_t modTime, Ordinal ord, Type type)
+ : _path(pth), _modTime(modTime), _ordinal(ord), _type(type) { }
virtual ~File() {}
const char* path() const { return _path; }
time_t modificationTime() const{ return _modTime; }
- uint32_t ordinal() const { return _ordinal; }
+ Ordinal ordinal() const { return _ordinal; }
virtual bool forEachAtom(AtomHandler&) const = 0;
virtual bool justInTimeforEachAtom(const char* name, AtomHandler&) const = 0;
virtual ObjcConstraint objCConstraint() const { return objcConstraintNone; }
virtual uint32_t cpuSubType() const { return 0; }
virtual uint32_t subFileCount() const { return 1; }
+ bool fileExists() const { return _modTime != 0; }
+ Type type() const { return _type; }
private:
const char* _path;
time_t _modTime;
- uint32_t _ordinal;
+ const Ordinal _ordinal;
+ const Type _type;
};
// minumum OS versions
//
enum MacVersionMin { macVersionUnset=0, mac10_4=0x000A0400, mac10_5=0x000A0500,
- mac10_6=0x000A0600, mac10_7=0x000A0700 };
+ mac10_6=0x000A0600, mac10_7=0x000A0700, mac10_8=0x000A0800,
+ mac10_Future=0x10000000 };
enum IOSVersionMin { iOSVersionUnset=0, iOS_2_0=0x00020000, iOS_3_1=0x00030100,
- iOS_4_2=0x00040200, iOS_4_3=0x00040300, iOS_5_0=0x00050000 };
+ iOS_4_2=0x00040200, iOS_4_3=0x00040300, iOS_5_0=0x00050000,
+ iOS_Future=0x10000000};
namespace relocatable {
//
//
// Abstract base class for object files the linker processes.
//
- // objcReplacementClasses() is reflects if the file was compiled for fix-and-continue
- //
// debugInfo() returns if the object file contains debugger information (stabs or dwarf).
//
// stabs() lazily creates a vector of Stab objects for each atom
const char* string;
};
- File(const char* pth, time_t modTime, uint32_t ord)
- : ld::File(pth, modTime, ord) { }
+ File(const char* pth, time_t modTime, Ordinal ord)
+ : ld::File(pth, modTime, ord, Reloc) { }
virtual ~File() {}
- virtual bool objcReplacementClasses() const = 0;
virtual DebugInfoKind debugInfo() const = 0;
virtual const char* debugInfoPath() const { return path(); }
virtual time_t debugInfoModificationTime() const { return modificationTime(); }
virtual File* findDylib(const char* installPath, const char* fromPath) = 0;
};
- File(const char* pth, time_t modTime, uint32_t ord)
- : ld::File(pth, modTime, ord), _dylibInstallPath(NULL),
+ File(const char* pth, time_t modTime, Ordinal ord)
+ : ld::File(pth, modTime, ord, Dylib), _dylibInstallPath(NULL),
_dylibTimeStamp(0), _dylibCurrentVersion(0), _dylibCompatibilityVersion(0),
_explicitlyLinked(false), _implicitlyLinked(false),
_lazyLoadedDylib(false), _forcedWeakLinked(false), _reExported(false),
bool explicitlyLinked() const { return _explicitlyLinked; }
void setImplicitlyLinked() { _implicitlyLinked = true; }
bool implicitlyLinked() const { return _implicitlyLinked; }
+
// attributes of how dylib will be used when linked
void setWillBeLazyLoadedDylb() { _lazyLoadedDylib = true; }
bool willBeLazyLoadedDylib() const { return _lazyLoadedDylib; }
virtual bool hasWeakDefinition(const char* name) const = 0;
virtual bool hasPublicInstallName() const = 0;
virtual bool allSymbolsAreWeakImported() const = 0;
+ virtual const void* codeSignatureDR() const = 0;
protected:
const char* _dylibInstallPath;
uint32_t _dylibTimeStamp;
class File : public ld::File
{
public:
- File(const char* pth, time_t modTime, uint32_t ord)
- : ld::File(pth, modTime, ord) { }
+ File(const char* pth, time_t modTime, Ordinal ord)
+ : ld::File(pth, modTime, ord, Archive) { }
virtual ~File() {}
virtual bool justInTimeDataOnlyforEachAtom(const char* name, AtomHandler&) const = 0;
};
kindStoreThumbDtraceCallSiteNop, kindStoreThumbDtraceIsEnableSiteClear,
// lazy binding
kindLazyTarget, kindSetLazyOffset,
+ // data-in-code markers
+ kindDataInCodeStartData, kindDataInCodeStartJT8, kindDataInCodeStartJT16,
+ kindDataInCodeStartJT32, kindDataInCodeStartJTA32, kindDataInCodeEnd,
// pointer store combinations
kindStoreTargetAddressLittleEndian32, // kindSetTargetAddress + kindStoreLittleEndian32
kindStoreTargetAddressLittleEndian64, // kindSetTargetAddress + kindStoreLittleEndian64
void setSectionStartAddress(uint64_t a) { assert(_mode == modeSectionOffset); _address += a; _mode = modeFinalAddress; }
uint64_t sectionOffset() const { assert(_mode == modeSectionOffset); return _address; }
uint64_t finalAddress() const { assert(_mode == modeFinalAddress); return _address; }
-
+#ifndef NDEBUG
+ bool finalAddressMode() const { return (_mode == modeFinalAddress); }
+#endif
virtual const File* file() const = 0;
virtual bool translationUnitSource(const char** dir, const char** name) const = 0;
virtual const char* name() const = 0;
virtual bool canCoalesceWith(const Atom& rhs, const class IndirectBindingTable&) const { return false; }
virtual Fixup::iterator fixupsBegin() const { return NULL; }
virtual Fixup::iterator fixupsEnd() const { return NULL; }
+ bool hasFixupsOfKind(Fixup::Kind kind) const {
+ for (ld::Fixup::iterator fit = fixupsBegin(), end=fixupsEnd(); fit != end; ++fit) {
+ if ( fit->kind == kind ) return true;
+ }
+ return false;
+ }
+
virtual UnwindInfo::iterator beginUnwind() const { return NULL; }
virtual UnwindInfo::iterator endUnwind() const { return NULL; }
virtual LineInfo::iterator beginLineInfo() const { return NULL; }
objcObjectConstraint(ld::File::objcConstraintNone),
objcDylibConstraint(ld::File::objcConstraintNone),
cpuSubType(0),
- allObjectFilesScatterable(true), hasObjcReplacementClasses(false),
+ allObjectFilesScatterable(true),
someObjectFileHasDwarf(false), usingHugeSections(false) { }
std::vector<FinalSection*> sections;
ld::File::ObjcConstraint objcDylibConstraint;
uint32_t cpuSubType;
bool allObjectFilesScatterable;
- bool hasObjcReplacementClasses;
bool someObjectFileHasDwarf;
bool usingHugeSections;
};
return File<A>::validFile(fileContent, fileLength, opts); }
static File<A>* parse(const uint8_t* fileContent, uint64_t fileLength,
const char* path, time_t mTime,
- uint32_t ordinal, const ParserOptions& opts) {
+ ld::File::Ordinal ordinal, const ParserOptions& opts) {
return new File<A>(fileContent, fileLength, path, mTime,
ordinal, opts);
}
const mach_o::relocatable::ParserOptions& opts);
File(const uint8_t* fileContent, uint64_t fileLength,
const char* pth, time_t modTime,
- uint32_t ord, const ParserOptions& opts);
+ ld::File::Ordinal ord, const ParserOptions& opts);
virtual ~File() {}
// overrides of ld::File
class Entry : ar_hdr
{
public:
- const char* name() const;
+ void getName(char *, int) const;
time_t modificationTime() const;
const uint8_t* content() const;
uint32_t contentSize() const;
};
+ struct MemberState { ld::relocatable::File* file; const Entry *entry; bool logged; bool loaded; uint16_t index;};
+ bool loadMember(MemberState& state, ld::File::AtomHandler& handler, const char *format, ...) const;
+
class CStringEquals
{
public:
typedef typename A::P P;
typedef typename A::P::E E;
- struct MemberState { ld::relocatable::File* file; bool logged; bool loaded; };
-
typedef std::map<const class Entry*, MemberState> MemberToStateMap;
const struct ranlib* ranlibHashSearch(const char* name) const;
}
template <typename A>
-const char* File<A>::Entry::name() const
+void File<A>::Entry::getName(char *buf, int bufsz) 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;
+ assert(bufsz >= len+1);
+ strncpy(buf, ((char*)this)+sizeof(ar_hdr), len);
+ buf[len] = '\0';
}
else {
- static char shortName[20];
- strncpy(shortName, this->ar_name, 16);
- shortName[16] = '\0';
- char* space = strchr(shortName, ' ');
+ assert(bufsz >= 16+1);
+ strncpy(buf, this->ar_name, 16);
+ buf[16] = '\0';
+ char* space = strchr(buf, ' ');
if ( space != NULL )
*space = '\0';
- return shortName;
}
}
const Entry* const start = (Entry*)&fileContent[8];
const Entry* const end = (Entry*)&fileContent[fileLength];
for (const Entry* p=start; p < end; p = p->next()) {
- const char* memberName = p->name();
+ char memberName[256];
+ p->getName(memberName, sizeof(memberName));
// skip option table-of-content member
if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) )
continue;
template <typename A>
File<A>::File(const uint8_t fileContent[], uint64_t fileLength, const char* pth, time_t modTime,
- uint32_t ord, const ParserOptions& opts)
+ ld::File::Ordinal ord, const ParserOptions& opts)
: ld::archive::File(strdup(pth), modTime, ord),
_archiveFileContent(fileContent), _archiveFilelength(fileLength),
_tableOfContents(NULL), _tableOfContentCount(0), _tableOfContentStrings(NULL),
if ( !_forceLoadAll ) {
const Entry* const firstMember = (Entry*)&_archiveFileContent[8];
- if ( (strcmp(firstMember->name(), SYMDEF_SORTED) == 0) || (strcmp(firstMember->name(), SYMDEF) == 0) ) {
+ char memberName[256];
+ firstMember->getName(memberName, sizeof(memberName));
+ if ( (strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0) ) {
const uint8_t* contents = firstMember->content();
uint32_t ranlibArrayLen = E::get32(*((uint32_t*)contents));
_tableOfContents = (const struct ranlib*)&contents[4];
}
else {
// i386 uses ObjC1 ABI which has .objc_category* global symbols
- return false;
+ // <rdar://problem/11342022> strip -S on i386 pulls out .objc_category_name symbols from static frameworks
+ return mach_o::relocatable::hasObjC1Categories(member->content());
}
}
template <typename A>
typename File<A>::MemberState& File<A>::makeObjectFileForMember(const Entry* member) const
{
+ uint16_t memberIndex = 0;
// in case member was instantiated earlier but not needed yet
typename MemberToStateMap::iterator pos = _instantiatedEntries.find(member);
- if ( pos != _instantiatedEntries.end() )
- return pos->second;
-
- const char* memberName = member->name();
+ if ( pos == _instantiatedEntries.end() ) {
+ // Have to find the index of this member
+ const Entry* start;
+ uint16_t index;
+ if (_instantiatedEntries.size() == 0) {
+ start = (Entry*)&_archiveFileContent[8];
+ index = 1;
+ } else {
+ MemberState &lastKnown = _instantiatedEntries.rbegin()->second;
+ start = lastKnown.entry->next();
+ index = lastKnown.index+1;
+ }
+ for (const Entry* p=start; p <= member; p = p->next(), index++) {
+ MemberState state = {NULL, p, false, false, index};
+ _instantiatedEntries[p] = state;
+ if (member == p) {
+ memberIndex = index;
+ }
+ }
+ } else {
+ MemberState& state = pos->second;
+ if (state.file)
+ return state;
+ memberIndex = state.index;
+ }
+ assert(memberIndex != 0);
+ char memberName[256];
+ member->getName(memberName, sizeof(memberName));
char memberPath[strlen(this->path()) + strlen(memberName)+4];
strcpy(memberPath, this->path());
strcat(memberPath, "(");
if ( (member->content() + member->contentSize()) > (_archiveFileContent+_archiveFilelength) )
throwf("corrupt archive, member contents extends past end of file");
const char* mPath = strdup(memberPath);
- // offset the ordinals in this mach-o .o file, so that atoms layout in same order as in archive
- uint32_t memberIndex = ((uint8_t*)member - _archiveFileContent)/sizeof(ar_hdr);
// see if member is mach-o file
+ ld::File::Ordinal ordinal = this->ordinal().archiveOrdinalWithMemberIndex(memberIndex);
ld::relocatable::File* result = mach_o::relocatable::parse(member->content(), member->contentSize(),
mPath, member->modificationTime(),
- this->ordinal() + memberIndex, _objOpts);
+ ordinal, _objOpts);
if ( result != NULL ) {
- MemberState state = {result, false, false};
+ MemberState state = {result, member, false, false, memberIndex};
_instantiatedEntries[member] = state;
return _instantiatedEntries[member];
}
// see if member is llvm bitcode file
result = lto::parse(member->content(), member->contentSize(),
- mPath, member->modificationTime(), this->ordinal() + memberIndex,
+ mPath, member->modificationTime(),
_objOpts.architecture, _objOpts.subType, _logAllFiles);
if ( result != NULL ) {
- MemberState state = {result, false, false};
+ MemberState state = {result, member, false, false, memberIndex};
_instantiatedEntries[member] = state;
return _instantiatedEntries[member];
}
}
+template <typename A>
+bool File<A>::loadMember(MemberState& state, ld::File::AtomHandler& handler, const char *format, ...) const
+{
+ bool didSomething = false;
+ if (!state.loaded) {
+ if ( _verboseLoad && !state.logged ) {
+ va_list list;
+ va_start(list, format);
+ vprintf(format, list);
+ va_end(list);
+ state.logged = true;
+ }
+ state.loaded = true;
+ didSomething = state.file->forEachAtom(handler);
+ }
+ return didSomething;
+}
+
+
template <typename A>
bool File<A>::forEachAtom(ld::File::AtomHandler& handler) const
{
const Entry* const start = (Entry*)&_archiveFileContent[8];
const Entry* const end = (Entry*)&_archiveFileContent[_archiveFilelength];
for (const Entry* p=start; p < end; p = p->next()) {
- const char* memberName = p->name();
+ char memberName[256];
+ p->getName(memberName, sizeof(memberName));
if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) )
continue;
MemberState& state = this->makeObjectFileForMember(p);
- if ( _verboseLoad ) {
- if ( _forceLoadThis )
- printf("-force_load forced load of %s(%s)\n", this->path(), memberName);
- else
- printf("-all_load forced load of %s(%s)\n", this->path(), memberName);
- state.logged = true;
- }
- didSome |= state.file->forEachAtom(handler);
- state.loaded = true;
+ didSome |= loadMember(state, handler, "%s forced load of %s(%s)\n", _forceLoadThis ? "-force_load" : "-all_load", this->path(), memberName);
}
}
else if ( _forceLoadObjC ) {
if ( (strncmp(it->first, ".objc_c", 7) == 0) || (strncmp(it->first, "_OBJC_CLASS_$_", 14) == 0) ) {
const Entry* member = (Entry*)&_archiveFileContent[E::get32(it->second->ran_off)];
MemberState& state = this->makeObjectFileForMember(member);
- if ( _verboseLoad && !state.logged ) {
- printf("-ObjC forced load of %s(%s)\n", this->path(), member->name());
- state.logged = true;
- }
- if ( ! state.loaded ) {
- didSome |= state.file->forEachAtom(handler);
- state.loaded = true;
- }
+ char memberName[256];
+ member->getName(memberName, sizeof(memberName));
+ didSome |= loadMember(state, handler, "-ObjC forced load of %s(%s)\n", this->path(), memberName);
}
}
// ObjC2 has no symbols in .o files with categories but not classes, look deeper for those
const Entry* const start = (Entry*)&_archiveFileContent[8];
const Entry* const end = (Entry*)&_archiveFileContent[_archiveFilelength];
for (const Entry* member=start; member < end; member = member->next()) {
- // only look at files not already instantiated
- if ( _instantiatedEntries.count(member) == 0 ) {
- //fprintf(stderr, "checking member %s\n", member->name());
+ char mname[256];
+ member->getName(mname, sizeof(mname));
+ // skip table-of-content member
+ if ( (member==start) && ((strcmp(mname, SYMDEF_SORTED) == 0) || (strcmp(mname, SYMDEF) == 0)) )
+ continue;
+ MemberState& state = this->makeObjectFileForMember(member);
+ // only look at files not already loaded
+ if ( ! state.loaded ) {
if ( this->memberHasObjCCategories(member) ) {
MemberState& state = this->makeObjectFileForMember(member);
- if ( _verboseLoad && !state.logged ) {
- printf("-ObjC forced load of %s(%s)\n", this->path(), member->name());
- state.logged = true;
- }
- if ( ! state.loaded ) {
- didSome |= state.file->forEachAtom(handler);
- state.loaded = true;
- }
+ char memberName[256];
+ member->getName(memberName, sizeof(memberName));
+ didSome |= loadMember(state, handler, "-ObjC forced load of %s(%s)\n", this->path(), memberName);
}
}
}
if ( result != NULL ) {
const Entry* member = (Entry*)&_archiveFileContent[E::get32(result->ran_off)];
MemberState& state = this->makeObjectFileForMember(member);
- // only call handler for each member once
- if ( ! state.loaded && !state.logged ) {
- if ( _verboseLoad ) {
- printf("%s forced load of %s(%s)\n", name, this->path(), member->name());
- state.logged = true;
- }
- state.loaded = true;
- return state.file->forEachAtom(handler);
- }
+ char memberName[256];
+ member->getName(memberName, sizeof(memberName));
+ return loadMember(state, handler, "%s forced load of %s(%s)\n", name, this->path(), memberName);
}
//fprintf(stderr, "%s NOT found in archive %s\n", name, this->path());
return false;
CheckIsDataSymbolHandler checker(name);
state.file->forEachAtom(checker);
if ( checker.symbolIsDataDefinition() ) {
- if ( _verboseLoad && !state.logged ) {
- printf("%s forced load of %s(%s)\n", name, this->path(), member->name());
- state.logged = true;
- }
- state.loaded = true;
- return state.file->forEachAtom(handler);
+ char memberName[256];
+ member->getName(memberName, sizeof(memberName));
+ return loadMember(state, handler, "%s forced load of %s(%s)\n", name, this->path(), memberName);
}
}
}
// main function used by linker to instantiate archive files
//
ld::archive::File* parse(const uint8_t* fileContent, uint64_t fileLength,
- const char* path, time_t modTime, uint32_t ordinal, const ParserOptions& opts)
+ const char* path, time_t modTime, ld::File::Ordinal ordinal, const ParserOptions& opts)
{
switch ( opts.objOpts.architecture ) {
+#if SUPPORT_ARCH_x86_64
case CPU_TYPE_X86_64:
if ( archive::Parser<x86_64>::validFile(fileContent, fileLength, opts.objOpts) )
return archive::Parser<x86_64>::parse(fileContent, fileLength, path, modTime, ordinal, opts);
break;
+#endif
+#if SUPPORT_ARCH_i386
case CPU_TYPE_I386:
if ( archive::Parser<x86>::validFile(fileContent, fileLength, opts.objOpts) )
return archive::Parser<x86>::parse(fileContent, fileLength, path, modTime, ordinal, opts);
break;
+#endif
+#if SUPPORT_ARCH_arm_any
case CPU_TYPE_ARM:
if ( archive::Parser<arm>::validFile(fileContent, fileLength, opts.objOpts) )
return archive::Parser<arm>::parse(fileContent, fileLength, path, modTime, ordinal, opts);
break;
+#endif
}
return NULL;
}
};
extern ld::archive::File* parse(const uint8_t* fileContent, uint64_t fileLength,
- const char* path, time_t modTime, uint32_t ordinal, const ParserOptions& opts);
+ const char* path, time_t modTime, ld::File::Ordinal ordinal, const ParserOptions& opts);
} // namespace archive
{
public:
File(const char* path, time_t mTime, const uint8_t* content,
- uint32_t contentLength, uint32_t ordinal, cpu_type_t arch);
+ uint32_t contentLength, cpu_type_t arch);
virtual ~File();
// overrides of ld::File
virtual uint32_t cpuSubType() const { return _cpuSubType; }
// overrides of ld::relocatable::File
- virtual bool objcReplacementClasses() const { return false; }
virtual DebugInfoKind debugInfo() const { return _debugInfo; }
virtual const char* debugInfoPath() const { return _debugInfoPath; }
virtual time_t debugInfoModificationTime() const
static bool validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch);
static const char* fileKind(const uint8_t* fileContent, uint64_t fileLength);
static File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path,
- time_t modTime, uint32_t ordinal, cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles);
+ time_t modTime, cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles);
static bool libLTOisLoaded() { return (::lto_get_version() != NULL); }
static bool optimize( const std::vector<const ld::Atom*>& allAtoms,
ld::Internal& state,
- uint32_t nextInputOrdinal,
const OptimizeOptions& options,
ld::File::AtomHandler& handler,
std::vector<const ld::Atom*>& newAtoms,
private:
static const char* tripletPrefixForArch(cpu_type_t arch);
- static ld::relocatable::File* parseMachOFile(const uint8_t* p, size_t len, uint32_t nextInputOrdinal, const OptimizeOptions& options);
+ static ld::relocatable::File* parseMachOFile(const uint8_t* p, size_t len, const OptimizeOptions& options);
class CStringEquals
{
bool Parser::validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch)
{
- switch (architecture) {
- case CPU_TYPE_I386:
- return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, "i386-");
- case CPU_TYPE_X86_64:
- return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, "x86_64-");
- case CPU_TYPE_ARM:
- for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) {
- if ( subarch == t->subType )
- return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, t->llvmTriplePrefix);
+ for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) {
+ if ( (architecture == t->cpuType) && (!(t->isSubType) || (subarch == t->cpuSubType)) ) {
+ bool result = ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, t->llvmTriplePrefix);
+ if ( !result ) {
+ // <rdar://problem/8434487> LTO only supports thumbv7 not armv7
+ if ( t->llvmTriplePrefixAlt[0] != '\0' ) {
+ result = ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, t->llvmTriplePrefixAlt);
+ }
}
- break;
+ return result;
+ }
}
return false;
}
const char* Parser::fileKind(const uint8_t* p, uint64_t fileLength)
{
if ( (p[0] == 0xDE) && (p[1] == 0xC0) && (p[2] == 0x17) && (p[3] == 0x0B) ) {
- uint32_t arch = LittleEndian::get32(*((uint32_t*)(&p[16])));
- switch (arch) {
- case CPU_TYPE_I386:
- return "i386";
- case CPU_TYPE_X86_64:
- return "x86_64";
- case CPU_TYPE_ARM:
- for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) {
+ cpu_type_t arch = LittleEndian::get32(*((uint32_t*)(&p[16])));
+ for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) {
+ if ( arch == t->cpuType ) {
+ if ( t->isSubType ) {
if ( ::lto_module_is_object_file_in_memory_for_target(p, fileLength, t->llvmTriplePrefix) )
- return t->subTypeName;
+ return t->archName;
+ }
+ else {
+ return t->archName;
}
- return "arm";
+ }
}
return "unknown bitcode architecture";
}
}
File* Parser::parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime,
- uint32_t ordinal, cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles)
+ cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles)
{
- File* f = new File(path, modTime, fileContent, fileLength, ordinal, architecture);
+ File* f = new File(path, modTime, fileContent, fileLength, architecture);
_s_files.push_back(f);
if ( logAllFiles )
printf("%s\n", path);
}
-ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, uint32_t nextInputOrdinal, const OptimizeOptions& options)
+ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, const OptimizeOptions& options)
{
mach_o::relocatable::ParserOptions objOpts;
objOpts.architecture = options.arch;
modTime = statBuffer.st_mtime;
}
- ld::relocatable::File* result = mach_o::relocatable::parse(p, len, path, modTime, nextInputOrdinal, objOpts);
+ ld::relocatable::File* result = mach_o::relocatable::parse(p, len, path, modTime, ld::File::Ordinal::LTOOrdinal(), objOpts);
if ( result != NULL )
return result;
throw "LLVM LTO, file is not of required architecture";
-File::File(const char* pth, time_t mTime, const uint8_t* content, uint32_t contentLength, uint32_t ord, cpu_type_t arch)
- : ld::relocatable::File(pth,mTime,ord), _architecture(arch), _internalAtom(*this),
+File::File(const char* pth, time_t mTime, const uint8_t* content, uint32_t contentLength, cpu_type_t arch)
+ : ld::relocatable::File(pth,mTime,ld::File::Ordinal::LTOOrdinal()), _architecture(arch), _internalAtom(*this),
_atomArray(NULL), _atomArrayCount(0), _module(NULL), _debugInfoPath(pth),
_section("__TEXT_", "__tmp_lto", ld::Section::typeTempLTO),
_fixupToInternal(0, ld::Fixup::k1of1, ld::Fixup::kindNone, &_internalAtom),
uint8_t alignment = (attr & LTO_SYMBOL_ALIGNMENT_MASK);
// make Atom using placement new operator
new (&_atomArray[_atomArrayCount++]) Atom(*this, name, scope, def, combine, alignment, autohide);
- if ( scope == ld::Atom::scopeLinkageUnit )
+ if ( scope != ld::Atom::scopeTranslationUnit )
_internalAtom.addReference(name);
if ( log ) fprintf(stderr, "\t0x%08X %s\n", attr, name);
}
bool Parser::optimize( const std::vector<const ld::Atom*>& allAtoms,
ld::Internal& state,
- uint32_t nextInputOrdinal,
const OptimizeOptions& options,
ld::File::AtomHandler& handler,
std::vector<const ld::Atom*>& newAtoms,
// 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->scope() == ld::Atom::scopeGlobal) ) {
+ if ( (atom->scope() == ld::Atom::scopeGlobal) && options.preserveAllGlobals ) {
if ( logMustPreserve ) fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because global symbol\n", name);
::lto_codegen_add_must_preserve_symbol(generator, name);
}
}
// parse generated mach-o file into a MachOReader
- ld::relocatable::File* machoFile = parseMachOFile(machOFile, machOFileLen, nextInputOrdinal, options);
+ ld::relocatable::File* machoFile = parseMachOFile(machOFile, machOFileLen, options);
// sync generated mach-o atoms with existing atoms ld knows about
if ( logAtomsBeforeSync ) {
}
+class Mutex {
+ static pthread_mutex_t lto_lock;
+public:
+ Mutex() { pthread_mutex_lock(<o_lock); }
+ ~Mutex() { pthread_mutex_unlock(<o_lock); }
+};
+pthread_mutex_t Mutex::lto_lock = PTHREAD_MUTEX_INITIALIZER;
//
// Used by archive reader to see if member is an llvm bitcode file
//
bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch)
{
+ Mutex lock;
return Parser::validFile(fileContent, fileLength, architecture, subarch);
}
// main function used by linker to instantiate ld::Files
//
ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength,
- const char* path, time_t modTime, uint32_t ordinal,
+ const char* path, time_t modTime,
cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles)
{
+ Mutex lock;
if ( Parser::validFile(fileContent, fileLength, architecture, subarch) )
- return Parser::parse(fileContent, fileLength, path, modTime, ordinal, architecture, subarch, logAllFiles);
+ return Parser::parse(fileContent, fileLength, path, modTime, architecture, subarch, logAllFiles);
else
return NULL;
}
//
const char* version()
{
+ Mutex lock;
return ::lto_get_version();
}
//
bool libLTOisLoaded()
{
+ Mutex lock;
return (::lto_get_version() != NULL);
}
//
const char* archName(const uint8_t* fileContent, uint64_t fileLength)
{
+ Mutex lock;
return Parser::fileKind(fileContent, fileLength);
}
//
bool optimize( const std::vector<const ld::Atom*>& allAtoms,
ld::Internal& state,
- uint32_t nextInputOrdinal,
const OptimizeOptions& options,
ld::File::AtomHandler& handler,
std::vector<const ld::Atom*>& newAtoms,
std::vector<const char*>& additionalUndefines)
{
- return Parser::optimize(allAtoms, state, nextInputOrdinal, options, handler, newAtoms, additionalUndefines);
+ Mutex lock;
+ return Parser::optimize(allAtoms, state, options, handler, newAtoms, additionalUndefines);
}
extern bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch);
extern ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength,
- const char* path, time_t modTime, uint32_t ordinal,
+ const char* path, time_t modTime,
cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles);
struct OptimizeOptions {
const char* outputFilePath;
const char* tmpObjectFilePath;
- bool allGlobalsAReDeadStripRoots;
+ bool preserveAllGlobals;
bool verbose;
bool saveTemps;
bool pie;
extern bool optimize( const std::vector<const ld::Atom*>& allAtoms,
ld::Internal& state,
- uint32_t nextInputOrdinal,
const OptimizeOptions& options,
ld::File::AtomHandler& handler,
std::vector<const ld::Atom*>& newAtoms,
#include "MachOFileAbstraction.hpp"
#include "MachOTrie.hpp"
#include "macho_dylib_file.h"
-
+#include "../code-sign-blobs/superblob.h"
namespace mach_o {
namespace dylib {
public:
static bool validFile(const uint8_t* fileContent, bool executableOrDylib);
File(const uint8_t* fileContent, uint64_t fileLength, const char* path,
- time_t mTime, uint32_t ordinal, bool linkingFlatNamespace,
+ time_t mTime, ld::File::Ordinal ordinal, bool linkingFlatNamespace,
bool linkingMainExecutable, bool hoistImplicitPublicDylibs,
ld::MacVersionMin macMin, ld::IOSVersionMin iPhoneMin, bool addVers,
bool logAllFiles, const char* installPath, bool indirectDylib);
virtual bool hasPublicInstallName() const{ return _hasPublicInstallName; }
virtual bool hasWeakDefinition(const char* name) const;
virtual bool allSymbolsAreWeakImported() const;
+ virtual const void* codeSignatureDR() const { return _codeSignatureDR; }
protected:
NameSet _ignoreExports;
const char* _parentUmbrella;
ImportAtom<A>* _importAtom;
+ const void* _codeSignatureDR;
bool _noRexports;
bool _hasWeakExports;
bool _deadStrippable;
template <typename A> const char* File<A>::objCInfoSectionName() { return "__image_info"; }
template <typename A>
-File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, time_t mTime, uint32_t ord,
+File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, time_t mTime, ld::File::Ordinal ord,
bool linkingFlatNamespace, bool linkingMainExecutable, bool hoistImplicitPublicDylibs,
ld::MacVersionMin macMin, ld::IOSVersionMin iOSMin, bool addVers,
bool logAllFiles, const char* targetInstallPath, bool indirectDylib)
_objcContraint(ld::File::objcConstraintNone),
_importProxySection("__TEXT", "__import", ld::Section::typeImportProxies, true),
_flatDummySection("__LINKEDIT", "__flat_dummy", ld::Section::typeLinkEdit, true),
- _parentUmbrella(NULL), _importAtom(NULL), _noRexports(false), _hasWeakExports(false),
+ _parentUmbrella(NULL), _importAtom(NULL), _codeSignatureDR(NULL),
+ _noRexports(false), _hasWeakExports(false),
_deadStrippable(false), _hasPublicInstallName(false),
_providedAtom(false), _explictReExportFound(false)
{
// pass 1: get pointers, and see if this dylib uses compressed LINKEDIT format
const macho_dysymtab_command<P>* dynamicInfo = NULL;
const macho_dyld_info_command<P>* dyldInfo = NULL;
+ const macho_linkedit_data_command<P>* codeSignature = NULL;
const macho_nlist<P>* symbolTable = NULL;
const char* strings = NULL;
bool compressedLinkEdit = false;
symtab = (macho_symtab_command<P>*)cmd;
symbolTable = (const macho_nlist<P>*)((char*)header + symtab->symoff());
strings = (char*)header + symtab->stroff();
+ if ( (symtab->stroff() + symtab->strsize()) > fileLength )
+ throwf("mach-o string pool extends beyond end of file in %s", pth);
break;
case LC_DYSYMTAB:
dynamicInfo = (macho_dysymtab_command<P>*)cmd;
if ( _addVersionLoadCommand && !indirectDylib && (_macVersionMin != ld::macVersionUnset) )
warning("building for MacOSX, but linking against dylib built for iOS: %s", pth);
break;
+ case LC_CODE_SIGNATURE:
+ codeSignature = (macho_linkedit_data_command<P>* )cmd;
+ break;
case macho_segment_command<P>::CMD:
// check for Objective-C info
if ( strcmp(((macho_segment_command<P>*)cmd)->segname(), objCInfoSegmentName()) == 0 ) {
_importAtom = new ImportAtom<A>(*this, importNames);
}
+ // if the dylib is code signed, look for its Designated Requirement
+ if ( codeSignature != NULL ) {
+ const Security::BlobCore* overallSignature = (Security::BlobCore*)((char*)header + codeSignature->dataoff());
+ typedef Security::SuperBlob<Security::kSecCodeMagicEmbeddedSignature> EmbeddedSignatureBlob;
+ typedef Security::SuperBlob<Security::kSecCodeMagicRequirementSet> InternalRequirementsBlob;
+ const EmbeddedSignatureBlob* signature = EmbeddedSignatureBlob::specific(overallSignature);
+ if ( signature->validateBlob(codeSignature->datasize()) ) {
+ const InternalRequirementsBlob* ireq = signature->find<InternalRequirementsBlob>(Security::cdRequirementsSlot);
+ if ( (ireq != NULL) && ireq->validateBlob() ) {
+ const Security::BlobCore* dr = ireq->find(Security::kSecDesignatedRequirementType);
+ if ( (dr != NULL) && dr->validateBlob(Security::kSecCodeMagicRequirement) ) {
+ // <rdar://problem/10968461> make copy because mapped file is about to be unmapped
+ _codeSignatureDR = ::malloc(dr->length());
+ ::memcpy((void*)_codeSignatureDR, dr, dr->length());
+ }
+ }
+ }
+ }
+
// build hash table
if ( dyldInfo != NULL )
buildExportHashTableFromExportInfo(dyldInfo, fileContent);
this->addSymbol(symName, weakDef, false, 0);
return;
}
+ else if ( strncmp(symAction, "install_name$", 13) == 0 ) {
+ _dylibInstallPath = symName;
+ return;
+ }
else {
warning("bad symbol action: %s in dylib %s", name, this->path());
}
static bool validFile(const uint8_t* fileContent, bool executableOrDyliborBundle);
static ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength,
const char* path, time_t mTime,
- uint32_t ordinal, const Options& opts, bool indirectDylib) {
- return new File<A>(fileContent, fileLength, path, mTime,
+ ld::File::Ordinal ordinal, const Options& opts, bool indirectDylib) {
+ return new File<A>(fileContent, fileLength, path, mTime,
ordinal, opts.flatNamespace(),
opts.linkingMainExecutable(),
opts.implicitlyLinkIndirectPublicDylibs(),
// main function used by linker to instantiate ld::Files
//
ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength,
- const char* path, time_t modTime, const Options& opts, uint32_t ordinal,
+ const char* path, time_t modTime, const Options& opts, ld::File::Ordinal ordinal,
bool bundleLoader, bool indirectDylib)
{
switch ( opts.architecture() ) {
+#if SUPPORT_ARCH_x86_64
case CPU_TYPE_X86_64:
if ( Parser<x86_64>::validFile(fileContent, bundleLoader) )
return Parser<x86_64>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
break;
+#endif
+#if SUPPORT_ARCH_i386
case CPU_TYPE_I386:
if ( Parser<x86>::validFile(fileContent, bundleLoader) )
return Parser<x86>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
break;
+#endif
+#if SUPPORT_ARCH_arm_any
case CPU_TYPE_ARM:
if ( Parser<arm>::validFile(fileContent, bundleLoader) )
return Parser<arm>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
break;
+#endif
}
return NULL;
}
namespace dylib {
extern ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path,
- time_t modTime, const Options& opts, uint32_t ordinal,
+ time_t modTime, const Options& opts, ld::File::Ordinal ordinal,
bool bundleLoader, bool indirectDylib);
} // namespace dylib
class File : public ld::relocatable::File
{
public:
- File(const char* p, time_t mTime, const uint8_t* content, uint32_t ord) :
+ File(const char* p, time_t mTime, const uint8_t* content, ld::File::Ordinal ord) :
ld::relocatable::File(p,mTime,ord), _fileContent(content),
_sectionsArray(NULL), _atomsArray(NULL),
_sectionsArrayCount(0), _atomsArrayCount(0),
_dwarfDebugLineSect(NULL), _dwarfDebugStringSect(NULL),
_objConstraint(ld::File::objcConstraintNone),
_cpuSubType(0),
- _ojcReplacmentClass(false), _canScatterAtoms(false) {}
+ _canScatterAtoms(false) {}
virtual ~File();
// overrides of ld::File
{ return false; }
// overrides of ld::relocatable::File
- virtual bool objcReplacementClasses() const { return _ojcReplacmentClass; }
virtual ObjcConstraint objCConstraint() const { return _objConstraint; }
virtual uint32_t cpuSubType() const { return _cpuSubType; }
virtual DebugInfoKind debugInfo() const { return _debugInfoKind; }
const macho_section<P>* _dwarfDebugStringSect;
ld::File::ObjcConstraint _objConstraint;
uint32_t _cpuSubType;
- bool _ojcReplacmentClass;
bool _canScatterAtoms;
};
cpu_subtype_t subtype=0);
static const char* fileKind(const uint8_t* fileContent);
static bool hasObjC2Categories(const uint8_t* fileContent);
+ static bool hasObjC1Categories(const uint8_t* fileContent);
static ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength,
- const char* path, time_t modTime, uint32_t ordinal,
+ const char* path, time_t modTime, ld::File::Ordinal ordinal,
const ParserOptions& opts) {
Parser p(fileContent, fileLength, path, modTime,
ordinal, opts.convertUnwindInfo);
void addDtraceExtraInfos(const SourceLocation& src, const char* provider);
const char* scanSymbolTableForAddress(uint64_t addr);
bool convertUnwindInfo() { return _convertUnwindInfo; }
+ bool hasDataInCodeLabels() { return _hasDataInCodeLabels; }
void addFixups(const SourceLocation& src, ld::Fixup::Kind kind, const TargetDesc& target);
Parser(const uint8_t* fileContent, uint64_t fileLength,
const char* path, time_t modTime,
- uint32_t ordinal, bool convertUnwindInfo);
+ ld::File::Ordinal ordinal, bool convertUnwindInfo);
ld::relocatable::File* parse(const ParserOptions& opts);
uint8_t loadCommandSizeMask();
bool parseLoadCommands();
uint32_t _fileLength;
const char* _path;
time_t _modTime;
- uint32_t _ordinal;
+ ld::File::Ordinal _ordinal;
// filled in by parseLoadCommands()
File<A>* _file;
bool _AppleObjc; // FSF has objc that uses different data layout
bool _overlappingSymbols;
bool _convertUnwindInfo;
+ bool _hasDataInCodeLabels;
unsigned int _stubsSectionNum;
const macho_section<P>* _stubsMachOSection;
std::vector<const char*> _dtraceProviderInfo;
template <typename A>
Parser<A>::Parser(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime,
- uint32_t ordinal, bool convertDUI)
+ ld::File::Ordinal ordinal, bool convertDUI)
: _fileContent(fileContent), _fileLength(fileLength), _path(path), _modTime(modTime),
_ordinal(ordinal), _file(NULL),
_symbols(NULL), _symbolCount(0), _strings(NULL), _stringsSize(0),
_EHFrameSection(NULL), _compactUnwindSection(NULL), _absoluteSection(NULL),
_tentativeDefinitionCount(0), _absoluteSymbolCount(0),
_symbolsInSections(0), _hasLongBranchStubs(false), _AppleObjc(false),
- _overlappingSymbols(false), _convertUnwindInfo(convertDUI),
+ _overlappingSymbols(false), _convertUnwindInfo(convertDUI), _hasDataInCodeLabels(false),
_stubsSectionNum(0), _stubsMachOSection(NULL)
{
}
return NULL;
if ( header->cputype() != CPU_TYPE_ARM )
return NULL;
- for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) {
- if ( t->subType == (cpu_subtype_t)header->cpusubtype() ) {
- return t->subTypeName;
+ for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) {
+ if ( (t->cpuType == CPU_TYPE_ARM) && ((cpu_subtype_t)header->cpusubtype() == t->cpuSubType) ) {
+ return t->archName;
}
}
return "arm???";
return false;
}
+
+template <typename A>
+bool Parser<A>::hasObjC1Categories(const uint8_t* fileContent)
+{
+ 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());
+ 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>* segment = (macho_segment_command<P>*)cmd;
+ const macho_section<P>* sectionsStart = (macho_section<P>*)((char*)segment + sizeof(macho_segment_command<P>));
+ for (uint32_t si=0; si < segment->nsects(); ++si) {
+ const macho_section<P>* sect = §ionsStart[si];
+ if ( (sect->size() > 0)
+ && (strcmp(sect->sectname(), "__category") == 0)
+ && (strcmp(sect->segname(), "__OBJC") == 0) ) {
+ return true;
+ }
+ }
+ }
+ cmd = (const macho_load_command<P>*)(((char*)cmd)+cmd->cmdsize());
+ if ( cmd > cmdsEnd )
+ throwf("malformed mach-o file, load command #%d is outside size of load commands", i);
+ }
+ return false;
+}
+
template <typename A>
int Parser<A>::pointerSorter(const void* l, const void* r)
{
_tentativeDefinitionCount = 0;
_absoluteSymbolCount = 0;
_symbolsInSections = 0;
+ _hasDataInCodeLabels = false;
for (uint32_t i=0; i < this->_symbolCount; ++i) {
const macho_nlist<P>& sym = symbolFromIndex(i);
// ignore stabs
continue;
// 'L' labels do not denote atom breaks
- if ( symbolName[0] == 'L' )
+ if ( symbolName[0] == 'L' ) {
+ // <rdar://problem/9218847> Formalize data in code with L$start$ labels
+ if ( strncmp(symbolName, "L$start$", 8) == 0 )
+ _hasDataInCodeLabels = true;
continue;
-
+ }
// how many def syms in each section
if ( sym.n_sect() > _machOSectionsCount )
throw "bad n_sect in symbol table";
_file->_objConstraint = ld::File::objcConstraintRetainReleaseOrGC;
else
_file->_objConstraint = ld::File::objcConstraintRetainRelease;
- if ( (flags & 1) == 1 )
- _file->_ojcReplacmentClass = true;
if ( sect->size() > 8 ) {
warning("section %s/%s has unexpectedly large size %llu in %s",
sect->segname(), Section<A>::makeSectionName(sect), sect->size(), _file->path());
+#if SUPPORT_ARCH_arm_any
template <>
bool Section<arm>::addRelocFixup(class Parser<arm>& parser, const macho_relocation_info<P>* reloc)
{
}
return result;
}
+#endif
}
}
+ // <rdar://problem/9218847> track data-in-code
+ if ( parser.hasDataInCodeLabels() && (this->type() == ld::Section::typeCode) ) {
+ for (uint32_t i=0; i < parser.symbolCount(); ++i) {
+ const macho_nlist<P>& sym = parser.symbolFromIndex(i);
+ // ignore stabs
+ if ( (sym.n_type() & N_STAB) != 0 )
+ continue;
+ // ignore non-definitions
+ if ( (sym.n_type() & N_TYPE) != N_SECT )
+ continue;
+
+ // 'L' labels do not denote atom breaks
+ const char* symbolName = parser.nameFromSymbol(sym);
+ if ( symbolName[0] == 'L' ) {
+ if ( strncmp(symbolName, "L$start$", 8) == 0 ) {
+ ld::Fixup::Kind kind = ld::Fixup::kindNone;
+ if ( strncmp(&symbolName[8], "data$", 5) == 0 )
+ kind = ld::Fixup::kindDataInCodeStartData;
+ else if ( strncmp(&symbolName[8], "code$", 5) == 0 )
+ kind = ld::Fixup::kindDataInCodeEnd;
+ else if ( strncmp(&symbolName[8], "jt8$", 4) == 0 )
+ kind = ld::Fixup::kindDataInCodeStartJT8;
+ else if ( strncmp(&symbolName[8], "jt16$", 4) == 0 )
+ kind = ld::Fixup::kindDataInCodeStartJT16;
+ else if ( strncmp(&symbolName[8], "jt32$", 4) == 0 )
+ kind = ld::Fixup::kindDataInCodeStartJT32;
+ else if ( strncmp(&symbolName[8], "jta32$", 4) == 0 )
+ kind = ld::Fixup::kindDataInCodeStartJTA32;
+ else
+ warning("unknown L$start$ label %s in file %s", symbolName, this->file().path());
+ if ( kind != ld::Fixup::kindNone ) {
+ Atom<A>* inAtom = parser.findAtomByAddress(sym.n_value());
+ typename Parser<A>::SourceLocation src(inAtom, sym.n_value() - inAtom->objectAddress());
+ parser.addFixup(src, ld::Fixup::k1of1, kind);
+ }
+ }
+ }
+ }
+ }
+
// add follow-on fixups for aliases
if ( _hasAliases ) {
for(Atom<A>* p = _beginAtoms; p < _endAtoms; ++p) {
// main function used by linker to instantiate ld::Files
//
ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength,
- const char* path, time_t modTime, uint32_t ordinal, const ParserOptions& opts)
+ const char* path, time_t modTime, ld::File::Ordinal ordinal, const ParserOptions& opts)
{
switch ( opts.architecture ) {
+#if SUPPORT_ARCH_x86_64
case CPU_TYPE_X86_64:
if ( mach_o::relocatable::Parser<x86_64>::validFile(fileContent) )
return mach_o::relocatable::Parser<x86_64>::parse(fileContent, fileLength, path, modTime, ordinal, opts);
break;
+#endif
+#if SUPPORT_ARCH_i386
case CPU_TYPE_I386:
if ( mach_o::relocatable::Parser<x86>::validFile(fileContent) )
return mach_o::relocatable::Parser<x86>::parse(fileContent, fileLength, path, modTime, ordinal, opts);
break;
+#endif
+#if SUPPORT_ARCH_arm_any
case CPU_TYPE_ARM:
if ( mach_o::relocatable::Parser<arm>::validFile(fileContent, opts.objSubtypeMustMatch, opts.subType) )
return mach_o::relocatable::Parser<arm>::parse(fileContent, fileLength, path, modTime, ordinal, opts);
break;
+#endif
}
return NULL;
}
return false;
}
+//
+// Used by archive reader when -ObjC option is specified
+//
+bool hasObjC1Categories(const uint8_t* fileContent)
+{
+ if ( mach_o::relocatable::Parser<x86>::validFile(fileContent, false, 0) ) {
+ return mach_o::relocatable::Parser<x86>::hasObjC1Categories(fileContent);
+ }
+ return false;
+}
+
} // namespace relocatable
};
extern ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength,
- const char* path, time_t modTime, uint32_t ordinal,
+ const char* path, time_t modTime, ld::File::Ordinal ordinal,
const ParserOptions& opts);
extern bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, const ParserOptions& opts);
extern bool hasObjC2Categories(const uint8_t* fileContent);
+extern bool hasObjC1Categories(const uint8_t* fileContent);
+
extern const char* archName(const uint8_t* fileContent);
} // namespace relocatable
{
public:
File(const char* segmentName, const char* sectionName, const char* pth,
- const uint8_t fileContent[], uint64_t fileLength, uint32_t ord,
+ const uint8_t fileContent[], uint64_t fileLength,
const char* symbolName="sect_create")
- : ld::File(pth, 0, ord),
+ : ld::File(pth, 0, ld::File::Ordinal::NullOrdinal(), Other),
_atom(*this, symbolName, fileContent, fileLength),
_section(segmentName, sectionName, ld::Section::typeUnclassified) { }
virtual ~File() { }
// main function used by linker for -sectcreate
//
ld::File* parse(const char* segmentName, const char* sectionName, const char* path,
- const uint8_t fileContent[], uint64_t fileLength, uint32_t ordinal,
+ const uint8_t fileContent[], uint64_t fileLength,
const char* symbolName)
{
- return new File(segmentName, sectionName, path, fileContent, fileLength, ordinal, symbolName);
+ return new File(segmentName, sectionName, path, fileContent, fileLength, symbolName);
}
namespace opaque_section {
extern ld::File* parse(const char* segmentName, const char* sectionName, const char* path,
- const uint8_t fileContent[], uint64_t fileLength, uint32_t ordinal,
+ const uint8_t fileContent[], uint64_t fileLength,
const char* symbolName="opaque_section");
return;
if (_s_log) fprintf(stderr, "ld: __text section size=%llu, might need branch islands\n", totalTextSize);
- // figure out how many regions of branch islands will be needed
- const uint32_t kBetweenRegions = maxDistanceBetweenIslands(opts, hasThumbBranches); // place regions of islands every 14MB in __text section
- const int kIslandRegionsCount = totalTextSize / kBetweenRegions;
+ // Figure out how many regions of branch islands will be needed, and their locations.
+ // Construct a vector containing the atoms after which branch islands will be inserted,
+ // taking into account follow on fixups. No atom run without an island can exceed kBetweenRegions.
+ const uint64_t kBetweenRegions = maxDistanceBetweenIslands(opts, hasThumbBranches); // place regions of islands every 14MB in __text section
+ std::vector<const ld::Atom*> branchIslandInsertionPoints; // atoms in the atom list after which branch islands will be inserted
+ uint64_t previousIslandEndAddr = 0;
+ const ld::Atom *insertionPoint;
+ branchIslandInsertionPoints.reserve(totalTextSize/kBetweenRegions*2);
+ for (std::vector<const ld::Atom*>::iterator it=textSection->atoms.begin(); it != textSection->atoms.end(); it++) {
+ const ld::Atom* atom = *it;
+ // if we move past the next atom, will the run length exceed kBetweenRegions?
+ if ( atom->sectionOffset() + atom->size() - previousIslandEndAddr > kBetweenRegions ) {
+ // yes. Add the last known good location (atom) for inserting a branch island.
+ if ( insertionPoint == NULL )
+ throwf("Unable to insert branch island. No insertion point available.");
+ branchIslandInsertionPoints.push_back(insertionPoint);
+ previousIslandEndAddr = insertionPoint->sectionOffset()+insertionPoint->size();
+ insertionPoint = NULL;
+ }
+ // Can we insert an island after this atom? If so then keep track of it.
+ if ( !atom->hasFixupsOfKind(ld::Fixup::kindNoneFollowOn) )
+ insertionPoint = atom;
+ }
+ // add one more island after the last atom
+ if (insertionPoint != NULL)
+ branchIslandInsertionPoints.push_back(insertionPoint);
+ const int kIslandRegionsCount = branchIslandInsertionPoints.size();
+ if (_s_log) {
+ fprintf(stderr, "ld: will use %u branch island regions\n", kIslandRegionsCount);
+ for (std::vector<const ld::Atom*>::iterator it = branchIslandInsertionPoints.begin(); it != branchIslandInsertionPoints.end(); ++it) {
+ const ld::Atom* atom = *it;
+ const ld::File *file = atom->file();
+ fprintf(stderr, "ld: branch island will be inserted at 0x%llx after %s", atom->sectionOffset()+atom->size(), atom->name());
+ if (file) fprintf(stderr, " (%s)", atom->file()->path());
+ fprintf(stderr, "\n");
+ }
+ }
+
+
typedef std::map<TargetAndOffset,const ld::Atom*, TargetAndOffsetComparor> AtomToIsland;
AtomToIsland* regionsMap[kIslandRegionsCount];
std::vector<const ld::Atom*>* regionsIslands[kIslandRegionsCount];
regionsIslands[i] = new std::vector<const ld::Atom*>();
}
unsigned int islandCount = 0;
- if (_s_log) fprintf(stderr, "ld: will use %u branch island regions\n", kIslandRegionsCount);
// create islands for branches in __text that are out of range
for (std::vector<const ld::Atom*>::iterator ait=textSection->atoms.begin(); ait != textSection->atoms.end(); ++ait) {
if ( _s_log ) fprintf(stderr, "ld: %u branch islands required in %u regions\n", islandCount, kIslandRegionsCount);
std::vector<const ld::Atom*> newAtomList;
newAtomList.reserve(textSection->atoms.size()+islandCount);
- uint64_t islandRegionAddr = kBetweenRegions;;
- int regionIndex = 0;
- for (std::vector<const ld::Atom*>::iterator it=textSection->atoms.begin(); it != textSection->atoms.end(); it++) {
- const ld::Atom* atom = *it;
- if ( (atom->sectionOffset()+atom->size()) > islandRegionAddr ) {
- std::vector<const ld::Atom*>* regionIslands = regionsIslands[regionIndex];
- for (std::vector<const ld::Atom*>::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) {
- const ld::Atom* islandAtom = *rit;
- newAtomList.push_back(islandAtom);
- if ( _s_log ) fprintf(stderr, "inserting island %s into __text section\n", islandAtom->name());
- }
- ++regionIndex;
- islandRegionAddr += kBetweenRegions;
+
+ uint64_t regionIndex = 0;
+ for (std::vector<const ld::Atom*>::iterator ait=textSection->atoms.begin(); ait != textSection->atoms.end(); ait++) {
+ newAtomList.push_back(*ait);
+ // copy over atoms until we find an island insertion point
+ // Note that the last insertion point is the last atom, so this loop never moves the iterator to atoms.end().
+ while (*ait != branchIslandInsertionPoints[regionIndex]) {
+ ait++;
+ newAtomList.push_back(*ait);
}
- newAtomList.push_back(atom);
- }
- // put any remaining islands at end of __text section
- if ( regionIndex < kIslandRegionsCount ) {
+
+ // insert the branch island atoms after the insertion point atom
std::vector<const ld::Atom*>* regionIslands = regionsIslands[regionIndex];
for (std::vector<const ld::Atom*>::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) {
const ld::Atom* islandAtom = *rit;
newAtomList.push_back(islandAtom);
if ( _s_log ) fprintf(stderr, "inserting island %s into __text section\n", islandAtom->name());
}
+ regionIndex++;
}
// swap in new list of atoms for __text section
textSection->atoms.clear();
if ( pos == thumbToAtomMap.end() ) {
if ( opts.archSupportsThumb2() ) {
// <rdar://problem/9116044> make long-branch style shims for arm kexts
- if ( makingKextBundle )
+ if ( makingKextBundle && opts.allowTextRelocs() )
shim = new NoPICThumb2ToArmShimAtom(target, *sect);
else
shim = new Thumb2ToArmShimAtom(target, *sect);
std::map<const Atom*, const Atom*>::iterator pos = atomToThumbMap.find(target);
if ( pos == atomToThumbMap.end() ) {
// <rdar://problem/9116044> make long-branch style shims for arm kexts
- if ( makingKextBundle )
+ if ( makingKextBundle && opts.allowTextRelocs() )
shim = new NoPICARMtoThumbShimAtom(target, *sect);
else
shim = new ARMtoThumbShimAtom(target, *sect);
// create atom that contains the whole compact unwind table
switch ( opts.architecture() ) {
+#if SUPPORT_ARCH_x86_64
case CPU_TYPE_X86_64:
state.addAtom(*new UnwindInfoAtom<x86_64>(entries, ehFrameSize));
break;
+#endif
+#if SUPPORT_ARCH_i386
case CPU_TYPE_I386:
state.addAtom(*new UnwindInfoAtom<x86>(entries, ehFrameSize));
break;
+#endif
default:
assert(0 && "no compact unwind for arch");
}
uint32_t startOffset, uint32_t endOffset, uint32_t cui)
{
switch ( opts.architecture() ) {
+#if SUPPORT_ARCH_x86_64
case CPU_TYPE_X86_64:
state.addAtom(*new CompactUnwindAtom<x86_64>(state, atom, startOffset, endOffset-startOffset, cui));
break;
+#endif
+#if SUPPORT_ARCH_i386
case CPU_TYPE_I386:
state.addAtom(*new CompactUnwindAtom<x86>(state, atom, startOffset, endOffset-startOffset, cui));
break;
+#endif
}
}
{
public:
File(const char* segmentName, const char* sectionName, const char* pth,
- const uint8_t fileContent[], uint64_t fileLength, uint32_t ord,
+ const uint8_t fileContent[], uint64_t fileLength, Ordinal ord,
const char* symbolName="dof")
- : ld::File(pth, 0, ord),
+ : ld::File(pth, 0, ord, Other),
_atom(*this, symbolName, fileContent, fileLength),
_section(segmentName, sectionName, ld::Section::typeDtraceDOF) { }
virtual ~File() {}
sectionNamesUsed.insert(sectionName);
char symbolName[strlen(providerName)+64];
sprintf(symbolName, "__dtrace_dof_for_provider_%s", providerName);
- File* f = new File("__TEXT", sectionName, "dtrace", p, dofSectionSize, 0, symbolName);
+ File* f = new File("__TEXT", sectionName, "dtrace", p, dofSectionSize, ld::File::Ordinal::NullOrdinal(), symbolName);
if ( log ) {
fprintf(stderr, "libdtrace created DOF of size %ld\n", dofSectionSize);
}
uint32_t flags;
};
-#define OBJC_IMAGE_IS_REPLACEMENT (1<<0)
#define OBJC_IMAGE_SUPPORTS_GC (1<<1)
#define OBJC_IMAGE_REQUIRES_GC (1<<2)
#define OBJC_IMAGE_OPTIMIZED_BY_DYLD (1<<3)
class ObjCImageInfoAtom : public ld::Atom {
public:
ObjCImageInfoAtom(ld::File::ObjcConstraint objcConstraint,
- bool compaction, bool objcReplacementClasses, bool abi2);
+ bool compaction, bool abi2);
virtual const ld::File* file() const { return NULL; }
virtual bool translationUnitSource(const char** dir, const char**) const
template <typename A>
ObjCImageInfoAtom<A>::ObjCImageInfoAtom(ld::File::ObjcConstraint objcConstraint, bool compaction,
- bool objcReplacementClasses, bool abi2)
+ bool abi2)
: ld::Atom(abi2 ? _s_sectionABI2 : _s_sectionABI1, ld::Atom::definitionRegular, ld::Atom::combineNever,
ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified,
symbolTableNotIn, false, false, false, ld::Atom::Alignment(2))
{
uint32_t value = 0;
- if ( objcReplacementClasses )
- value = OBJC_IMAGE_IS_REPLACEMENT;
switch ( objcConstraint ) {
case ld::File::objcConstraintNone:
case ld::File::objcConstraintRetainRelease:
virtual void setScope(Scope) { }
virtual void copyRawContent(uint8_t buffer[]) const {
bzero(buffer, size());
- A::P::E::set32(*((uint32_t*)(&buffer[0])), 24);
+ A::P::E::set32(*((uint32_t*)(&buffer[0])), 3*sizeof(pint_t)); // entry size
A::P::E::set32(*((uint32_t*)(&buffer[4])), _methodCount);
}
virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixups[0]; }
const std::set<const ld::Atom*>& _dead;
};
+ struct AtomSorter
+ {
+ bool operator()(const Atom* left, const Atom* right)
+ {
+ // sort by file ordinal, then object address, then zero size, then symbol name
+ // only file based atoms are supported (file() != NULL)
+ if (left==right) return false;
+ const File *leftf = left->file();
+ const File *rightf = right->file();
+
+ if (leftf == rightf) {
+ if (left->objectAddress() != right->objectAddress()) {
+ return left->objectAddress() < right->objectAddress();
+ } else {
+ // for atoms in the same file with the same address, zero sized
+ // atoms must sort before nonzero sized atoms
+ if ((left->size() == 0 && right->size() > 0) || (left->size() > 0 && right->size() == 0))
+ return left->size() < right->size();
+ return strcmp(left->name(), right->name());
+ }
+ }
+ return (leftf->ordinal() < rightf->ordinal());
+ }
+ };
+
+ static void sortAtomVector(std::vector<const Atom*> &atoms) {
+ std::sort(atoms.begin(), atoms.end(), AtomSorter());
+ }
+
template <typename A>
void OptimizeCategories<A>::doit(const Options& opts, ld::Internal& state)
{
// build map of all classes in this image that have categories on them
typedef std::map<const ld::Atom*, std::vector<const ld::Atom*>*> CatMap;
CatMap classToCategories;
+ std::vector<const ld::Atom*> classOrder;
std::set<const ld::Atom*> deadAtoms;
ld::Internal::FinalSection* methodListSection = NULL;
for (std::vector<ld::Internal::FinalSection*>::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) {
continue;
}
assert(categoryAtom != NULL);
- assert(categoryAtom->size() == Category<A>::size());
+ assert(categoryAtom->size() >= Category<A>::size());
// ignore categories also in __objc_nlcatlist
if ( nlcatListAtoms.count(categoryAtom) != 0 )
continue;
CatMap::iterator pos = classToCategories.find(categoryOnClassAtom);
if ( pos == classToCategories.end() ) {
classToCategories[categoryOnClassAtom] = new std::vector<const ld::Atom*>();
+ classOrder.push_back(categoryOnClassAtom);
}
classToCategories[categoryOnClassAtom]->push_back(categoryAtom);
// mark category atom and catlist atom as dead
// if found some categories
if ( classToCategories.size() != 0 ) {
assert(methodListSection != NULL);
+ sortAtomVector(classOrder);
// alter each class definition to have new method list which includes all category methods
- for (CatMap::iterator it=classToCategories.begin(); it != classToCategories.end(); ++it) {
- const ld::Atom* classAtom = it->first;
- const std::vector<const ld::Atom*>* categories = it->second;
+ for (std::vector<const ld::Atom*>::iterator it = classOrder.begin(); it != classOrder.end(); it++) {
+ const ld::Atom* classAtom = *it;
+ const std::vector<const ld::Atom*>* categories = classToCategories[classAtom];
assert(categories->size() != 0);
// if any category adds instance methods, generate new merged method list, and replace
if ( OptimizeCategories<A>::hasInstanceMethods(state, categories) ) {
// add image info atom
switch ( opts.architecture() ) {
+#if SUPPORT_ARCH_x86_64
case CPU_TYPE_X86_64:
state.addAtom(*new ObjCImageInfoAtom<x86_64>(state.objcObjectConstraint, compaction,
- state.hasObjcReplacementClasses, true));
+ true));
break;
+#endif
+#if SUPPORT_ARCH_i386
case CPU_TYPE_I386:
state.addAtom(*new ObjCImageInfoAtom<x86>(state.objcObjectConstraint, compaction,
- state.hasObjcReplacementClasses, opts.objCABIVersion2POverride() ? true : false));
+ opts.objCABIVersion2POverride() ? true : false));
break;
+#endif
case CPU_TYPE_ARM:
state.addAtom(*new ObjCImageInfoAtom<arm>(state.objcObjectConstraint, compaction,
- state.hasObjcReplacementClasses, true));
+ true));
break;
default:
assert(0 && "unknown objc arch");
if ( opts.objcCategoryMerging() ) {
// optimize classes defined in this linkage unit by merging in categories also in this linkage unit
switch ( opts.architecture() ) {
+#if SUPPORT_ARCH_x86_64
case CPU_TYPE_X86_64:
OptimizeCategories<x86_64>::doit(opts, state);
break;
+#endif
+#if SUPPORT_ARCH_i386
case CPU_TYPE_I386:
// disable optimization until fully tested
- //if ( opts.objCABIVersion2POverride() )
- // OptimizeCategories<x86>::doit(opts, state);
+ if ( opts.objCABIVersion2POverride() )
+ OptimizeCategories<x86>::doit(opts, state);
break;
+#endif
+#if SUPPORT_ARCH_arm_any
case CPU_TYPE_ARM:
// disable optimization until fully tested
- //OptimizeCategories<arm>::doit(opts, state);
+ OptimizeCategories<arm>::doit(opts, state);
break;
+#endif
default:
assert(0 && "unknown objc arch");
}
// sort by .o order
const ld::File* leftFile = left->file();
const ld::File* rightFile = right->file();
- uint32_t leftFileOrdinal = (leftFile != NULL) ? leftFile->ordinal() : 0;
- uint32_t rightFileOrdinal = (rightFile != NULL) ? rightFile->ordinal() : 0;
+ // <rdar://problem/10830126> properly sort if on file is NULL and the other is not
+ ld::File::Ordinal leftFileOrdinal = (leftFile != NULL) ? leftFile->ordinal() : ld::File::Ordinal::NullOrdinal();
+ ld::File::Ordinal rightFileOrdinal = (rightFile != NULL) ? rightFile->ordinal() : ld::File::Ordinal::NullOrdinal();
if ( leftFileOrdinal != rightFileOrdinal )
return leftFileOrdinal< rightFileOrdinal;
ld::Section LazyPointerAtom::_s_sectionClose("__DATA", "__lazy_symbol", ld::Section::typeLazyPointerClose);
+class NonLazyPointerAtom : public ld::Atom {
+public:
+ NonLazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo)
+ : ld::Atom(_s_section, ld::Atom::definitionRegular,
+ ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeLazyPointer,
+ symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)),
+ _stubTo(stubTo),
+ _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, &stubTo) {
+ pass.addAtom(*this);
+ }
+
+ virtual const ld::File* file() const { return _stubTo.file(); }
+ virtual bool translationUnitSource(const char** dir, const char** nm) const
+ { return false; }
+ virtual const char* name() const { return _stubTo.name(); }
+ virtual uint64_t size() const { return 4; }
+ virtual uint64_t objectAddress() const { return 0; }
+ virtual void copyRawContent(uint8_t buffer[]) const { }
+ virtual void setScope(Scope) { }
+ virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; }
+ virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup1)[1]; }
+
+private:
+ const ld::Atom& _stubTo;
+ ld::Fixup _fixup1;
+
+ static ld::Section _s_section;
+ static ld::Section _s_sectionClose;
+};
+
+ld::Section NonLazyPointerAtom::_s_section("__DATA", "__nl_symbol_ptr", ld::Section::typeNonLazyPointer);
+
+
+class StubPICKextAtom : public ld::Atom {
+public:
+ StubPICKextAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo)
+ : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
+ ld::Atom::scopeLinkageUnit, ld::Atom::typeStub,
+ symbolTableIn, false, true, false, ld::Atom::Alignment(2)),
+ _stubTo(stubTo),
+ _nonLazyPointer(pass, stubTo),
+ _fixup1(0, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, &_nonLazyPointer),
+ _fixup2(0, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this),
+ _fixup3(0, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 12),
+ _fixup4(0, ld::Fixup::k4of4, ld::Fixup::kindStoreThumbLow16),
+ _fixup5(4, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, &_nonLazyPointer),
+ _fixup6(4, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this),
+ _fixup7(4, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 12),
+ _fixup8(4, ld::Fixup::k4of4, ld::Fixup::kindStoreThumbHigh16) {
+ pass.addAtom(*this);
+ asprintf((char**)&_name, "%s.stub", _stubTo.name());
+ }
+
+ virtual const ld::File* file() const { return _stubTo.file(); }
+ virtual bool translationUnitSource(const char** dir, const char** nm) const
+ { return false; }
+ virtual const char* name() const { return _name; }
+ virtual uint64_t size() const { return 16; }
+ virtual uint64_t objectAddress() const { return 0; }
+ virtual void copyRawContent(uint8_t buffer[]) const {
+ OSWriteLittleInt32(&buffer[ 0], 0, 0x0c00f240); // movw ip, #lo(nlp - L1)
+ OSWriteLittleInt32(&buffer[ 4], 0, 0x0c00f2c0); // movt ip, #hi(nlp - L1)
+ OSWriteLittleInt16(&buffer[ 8], 0, 0x44fc); // add ip, pc
+ OSWriteLittleInt32(&buffer[10], 0, 0xc000f8dc); // ldr.w ip, [ip]
+ OSWriteLittleInt16(&buffer[14], 0, 0x4760); // bx ip
+ }
+ virtual void setScope(Scope) { }
+ virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; }
+ virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup8)[1]; }
+
+private:
+ const ld::Atom& _stubTo;
+ const char* _name;
+ NonLazyPointerAtom _nonLazyPointer;
+ ld::Fixup _fixup1;
+ ld::Fixup _fixup2;
+ ld::Fixup _fixup3;
+ ld::Fixup _fixup4;
+ ld::Fixup _fixup5;
+ ld::Fixup _fixup6;
+ ld::Fixup _fixup7;
+ ld::Fixup _fixup8;
+
+ static ld::Section _s_section;
+};
+
+ld::Section StubPICKextAtom::_s_section("__TEXT", "__stub", ld::Section::typeCode);
+
+
class StubPICAtom : public ld::Atom {
public:
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2009 Apple Inc. All rights reserved.
+ * Copyright (c) 2009-2012 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
+
+class NonLazyPointerAtom : public ld::Atom {
+public:
+ NonLazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo)
+ : ld::Atom(_s_section, ld::Atom::definitionRegular,
+ ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer,
+ symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)),
+ _stubTo(stubTo),
+ _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, &stubTo) {
+ pass.addAtom(*this);
+ }
+
+ virtual const ld::File* file() const { return _stubTo.file(); }
+ virtual bool translationUnitSource(const char** dir, const char** nm) const
+ { return false; }
+ virtual const char* name() const { return _stubTo.name(); }
+ virtual uint64_t size() const { return 8; }
+ virtual uint64_t objectAddress() const { return 0; }
+ virtual void copyRawContent(uint8_t buffer[]) const { }
+ virtual void setScope(Scope) { }
+ virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; }
+ virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup1)[1]; }
+
+private:
+ const ld::Atom& _stubTo;
+ ld::Fixup _fixup1;
+
+ static ld::Section _s_section;
+};
+
+ld::Section NonLazyPointerAtom::_s_section("__DATA", "__got", ld::Section::typeNonLazyPointer);
+
+
+
+class KextStubAtom : public ld::Atom {
+public:
+ KextStubAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo)
+ : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
+ ld::Atom::scopeLinkageUnit, ld::Atom::typeStub,
+ symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)),
+ _stubTo(stubTo),
+ _nonLazyPointer(pass, stubTo),
+ _fixup(2, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86PCRel32, &_nonLazyPointer) { pass.addAtom(*this); }
+
+ virtual const ld::File* file() const { return _stubTo.file(); }
+ virtual bool translationUnitSource(const char** dir, const char** ) const
+ { return false; }
+ virtual const char* name() const { return _stubTo.name(); }
+ virtual uint64_t size() const { return 6; }
+ virtual uint64_t objectAddress() const { return 0; }
+ virtual void copyRawContent(uint8_t buffer[]) const {
+ buffer[0] = 0xFF; // jmp *foo$non_lazy_pointer(%rip)
+ buffer[1] = 0x25;
+ buffer[2] = 0x00;
+ buffer[3] = 0x00;
+ buffer[4] = 0x00;
+ buffer[5] = 0x00;
+ }
+ virtual void setScope(Scope) { }
+ virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup; }
+ virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; }
+
+private:
+ const ld::Atom& _stubTo;
+ NonLazyPointerAtom _nonLazyPointer;
+ mutable ld::Fixup _fixup;
+
+ static ld::Section _s_section;
+};
+
+ld::Section KextStubAtom::_s_section("__TEXT", "__stubs", ld::Section::typeStub);
+
+
} // namespace x86_64
#include "Options.h"
#include "ld.hpp"
+#include "MachOFileAbstraction.hpp"
#include "make_stubs.h"
case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32:
case ld::Fixup::kindStoreTargetAddressARMBranch24:
case ld::Fixup::kindStoreTargetAddressThumbBranch22:
+ assert(target != NULL);
// create stub if target is in a dylib
if ( target->definition() == ld::Atom::definitionProxy )
return target;
}
switch ( _architecture ) {
+#if SUPPORT_ARCH_i386
case CPU_TYPE_I386:
if ( usingCompressedLINKEDIT() && !forLazyDylib )
return new ld::passes::stubs::x86::StubAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport);
else
return new ld::passes::stubs::x86::classic::StubAtom(*this, target, forLazyDylib, weakImport);
break;
+#endif
+#if SUPPORT_ARCH_x86_64
case CPU_TYPE_X86_64:
- if ( usingCompressedLINKEDIT() && !forLazyDylib )
+ if ( (_options.outputKind() == Options::kKextBundle) && _options.kextsUseStubs() )
+ return new ld::passes::stubs::x86_64::KextStubAtom(*this, target);
+ else if ( usingCompressedLINKEDIT() && !forLazyDylib )
return new ld::passes::stubs::x86_64::StubAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport);
else
return new ld::passes::stubs::x86_64::classic::StubAtom(*this, target, forLazyDylib, weakImport);
break;
+#endif
+#if SUPPORT_ARCH_arm_any
case CPU_TYPE_ARM:
- if ( usingCompressedLINKEDIT() && !forLazyDylib ) {
+ if ( (_options.outputKind() == Options::kKextBundle) && _options.kextsUseStubs() ) {
+ // if text relocs are not allows in kext bundles, then linker must create a stub
+ return new ld::passes::stubs::arm::StubPICKextAtom(*this, target);
+ }
+ else if ( usingCompressedLINKEDIT() && !forLazyDylib ) {
if ( (_stubCount < 900) && !_mightBeInSharedRegion && !_largeText )
return new ld::passes::stubs::arm::StubCloseAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport);
else if ( _pic )
return new ld::passes::stubs::arm::classic::StubNoPICAtom(*this, target, forLazyDylib, weakImport);
}
break;
+#endif
}
throw "unsupported arch for stub";
}
case Options::kObjectFile:
// these kinds don't use stubs and can have resolver functions
return;
- case Options::kKextBundle:
case Options::kStaticExecutable:
case Options::kPreload:
case Options::kDyld:
case Options::kDynamicLibrary:
// uses stubs and can have resolver functions
break;
+ case Options::kKextBundle:
+ verifyNoResolverFunctions(state);
+ // if kext don't use stubs, don't do this pass
+ if ( !_options.kextsUseStubs() )
+ return;
+ break;
case Options::kDynamicExecutable:
case Options::kDynamicBundle:
// these kinds do use stubs and cannot have resolver functions
}
}
}
+
+ const bool needStubForMain = _options.needsEntryPointLoadCommand()
+ && (state.entryPoint != NULL)
+ && (state.entryPoint->definition() == ld::Atom::definitionProxy);
+ if ( needStubForMain ) {
+ // _main not found in any .o files. Currently have proxy to dylib
+ // Add to map, so that a stub will be made
+ stubFor[state.entryPoint] = NULL;
+ }
// short circuit if no stubs needed
_internal = &state;
return;
// <rdar://problem/8553283> lazily check for helper
- if ( !_options.makeCompressedDyldInfo() && (state.classicBindingHelper == NULL) )
+ if ( !_options.makeCompressedDyldInfo() && (state.classicBindingHelper == NULL) && (_options.outputKind() != Options::kKextBundle) )
throw "symbol dyld_stub_binding_helper not found, normally in crt1.o/dylib1.o/bundle1.o";
// disable arm close stubs in some cases
}
}
+ // switch entry point from proxy to stub
+ if ( needStubForMain ) {
+ const ld::Atom* mainStub = stubFor[state.entryPoint];
+ assert(mainStub != NULL);
+ state.entryPoint = mainStub;
+ }
+
// sort new atoms so links are consistent
for (std::vector<ld::Internal::FinalSection*>::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) {
ld::Internal::FinalSection* sect = *sit;
break;
case ld::Fixup::kindSetLazyOffset:
printf("offset of lazy binding info for %s", referenceTargetAtomName(ref));
- break;
+ break;
+ case ld::Fixup::kindDataInCodeStartData:
+ printf("start of data in code");
+ break;
+ case ld::Fixup::kindDataInCodeStartJT8:
+ printf("start of jump table 8 data in code");
+ break;
+ case ld::Fixup::kindDataInCodeStartJT16:
+ printf("start of jump table 16 data in code");
+ break;
+ case ld::Fixup::kindDataInCodeStartJT32:
+ printf("start of jump table 32 data in code");
+ break;
+ case ld::Fixup::kindDataInCodeStartJTA32:
+ printf("start of jump table absolute 32 data in code");
+ break;
+ case ld::Fixup::kindDataInCodeEnd:
+ printf("end of data in code");
+ break;
case ld::Fixup::kindStoreTargetAddressLittleEndian32:
printf("store 32-bit little endian address of %s", referenceTargetAtomName(ref));
break;
}
}
- ld::relocatable::File* objResult = mach_o::relocatable::parse(p, fileLen, path, stat_buf.st_mtime, 0, objOpts);
+ ld::relocatable::File* objResult = mach_o::relocatable::parse(p, fileLen, path, stat_buf.st_mtime, ld::File::Ordinal::NullOrdinal(), objOpts);
if ( objResult != NULL )
return objResult;
// see if it is an llvm object file
- objResult = lto::parse(p, fileLen, path, stat_buf.st_mtime, 0, sPreferredArch, sPreferredSubArch, false);
+ objResult = lto::parse(p, fileLen, path, stat_buf.st_mtime, sPreferredArch, sPreferredSubArch, false);
if ( objResult != NULL )
return objResult;
sShowLineInfo = false;
}
else if ( strcmp(arg, "-arch") == 0 ) {
- const char* arch = ++i<argc? argv[i]: "";
- if ( strcmp(arch, "i386") == 0 )
- sPreferredArch = CPU_TYPE_I386;
- else if ( strcmp(arch, "x86_64") == 0 )
- sPreferredArch = CPU_TYPE_X86_64;
- else {
- bool found = false;
- for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) {
- if ( strcmp(t->subTypeName,arch) == 0 ) {
- sPreferredArch = CPU_TYPE_ARM;
- sPreferredSubArch = t->subType;
- found = true;
- break;
- }
+ const char* archName = argv[++i];
+ if ( archName == NULL )
+ throw "-arch missing architecture name";
+ bool found = false;
+ for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) {
+ if ( strcmp(t->archName,archName) == 0 ) {
+ sPreferredArch = t->cpuType;
+ if ( t->isSubType )
+ sPreferredSubArch = t->cpuSubType;
+ found = true;
}
- if ( !found )
- throwf("unknown architecture %s", arch);
}
+ if ( !found )
+ throwf("unknown architecture %s", archName);
}
else if ( strcmp(arg, "-only") == 0 ) {
sMatchName = ++i<argc? argv[i]: NULL;
#include "MachOFileAbstraction.hpp"
#include "Architectures.hpp"
#include "MachOTrie.hpp"
+#include "../ld/code-sign-blobs/superblob.h"
static bool printRebase = false;
static bool printBind = false;
static bool printSharedRegion = false;
static bool printFunctionStarts = false;
static bool printDylibs = false;
+static bool printDRs = false;
+static bool printDataCode = false;
static cpu_type_t sPreferredArch = 0;
static cpu_type_t sPreferredSubArch = 0;
void printSharedRegionInfo();
void printFunctionStartsInfo();
void printDylibsInfo();
+ void printDRInfo();
+ void printDataInCode();
void printFunctionStartLine(uint64_t addr);
const uint8_t* printSharedRegionInfoForEachULEB128Address(const uint8_t* p, uint8_t kind);
pint_t relocBase();
const macho_dyld_info_command<P>* fInfo;
const macho_linkedit_data_command<P>* fSharedRegionInfo;
const macho_linkedit_data_command<P>* fFunctionStartsInfo;
+ const macho_linkedit_data_command<P>* fDataInCode;
+ const macho_linkedit_data_command<P>* fDRInfo;
uint64_t fBaseAddress;
const macho_dysymtab_command<P>* fDynamicSymbolTable;
const macho_segment_command<P>* fFirstSegment;
switch (header->filetype()) {
case MH_EXECUTE:
case MH_DYLIB:
+ case MH_DYLIB_STUB:
case MH_BUNDLE:
case MH_DYLINKER:
return true;
switch (header->filetype()) {
case MH_EXECUTE:
case MH_DYLIB:
+ case MH_DYLIB_STUB:
case MH_BUNDLE:
case MH_DYLINKER:
return true;
switch (header->filetype()) {
case MH_EXECUTE:
case MH_DYLIB:
+ case MH_DYLIB_STUB:
case MH_BUNDLE:
case MH_DYLINKER:
return true;
switch (header->filetype()) {
case MH_EXECUTE:
case MH_DYLIB:
+ case MH_DYLIB_STUB:
case MH_BUNDLE:
case MH_DYLINKER:
return true;
return false;
}
+#if SUPPORT_ARCH_arm_any
template <>
bool DyldInfoPrinter<arm>::validFile(const uint8_t* fileContent)
{
switch (header->filetype()) {
case MH_EXECUTE:
case MH_DYLIB:
+ case MH_DYLIB_STUB:
case MH_BUNDLE:
case MH_DYLINKER:
return true;
}
return false;
}
+#endif
template <typename A>
DyldInfoPrinter<A>::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path, bool printArch)
: fHeader(NULL), fLength(fileLength),
fStrings(NULL), fStringsEnd(NULL), fSymbols(NULL), fSymbolCount(0), fInfo(NULL),
- fSharedRegionInfo(NULL), fFunctionStartsInfo(NULL),
+ fSharedRegionInfo(NULL), fFunctionStartsInfo(NULL), fDataInCode(NULL), fDRInfo(NULL),
fBaseAddress(0), fDynamicSymbolTable(NULL), fFirstSegment(NULL), fFirstWritableSegment(NULL),
fWriteableSegmentWithAddrOver4G(false)
{
case LC_FUNCTION_STARTS:
fFunctionStartsInfo = (macho_linkedit_data_command<P>*)cmd;
break;
+ case LC_DATA_IN_CODE:
+ fDataInCode = (macho_linkedit_data_command<P>*)cmd;
+ break;
+ case LC_DYLIB_CODE_SIGN_DRS:
+ fDRInfo = (macho_linkedit_data_command<P>*)cmd;
+ break;
}
cmd = (const macho_load_command<P>*)endOfCmd;
}
if ( printArch ) {
- switch ( fHeader->cputype() ) {
- case CPU_TYPE_I386:
- printf("for arch i386:\n");
- break;
- case CPU_TYPE_X86_64:
- printf("for arch x86_64:\n");
- break;
- case CPU_TYPE_POWERPC:
- printf("for arch ppc:\n");
- break;
- case CPU_TYPE_ARM:
- for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) {
- if ( (cpu_subtype_t)fHeader->cpusubtype() == t->subType) {
- printf("for arch %s:\n", t->subTypeName);
- break;
- }
- }
+ for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) {
+ if ( (cpu_type_t)fHeader->cputype() == t->cpuType ) {
+ if ( t->isSubType && ((cpu_subtype_t)fHeader->cpusubtype() != t->cpuSubType) )
+ continue;
+ printf("for arch %s:\n", t->archName);
+ }
}
}
-
if ( printRebase ) {
if ( fInfo != NULL )
printRebaseInfo();
printFunctionStartsInfo();
if ( printDylibs )
printDylibsInfo();
+ if ( printDRs )
+ printDRInfo();
+ if ( printDataCode )
+ printDataInCode();
}
static uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end)
if (p == end)
throwf("malformed sleb128");
byte = *p++;
- result |= ((byte & 0x7f) << bit);
+ result |= (((int64_t)(byte & 0x7f)) << bit);
bit += 7;
} while (byte & 0x80);
// sign extend negative numbers
}
}
+#if SUPPORT_ARCH_arm_any
template <>
void DyldInfoPrinter<arm>::printFunctionStartLine(uint64_t addr)
{
else
printf("0x%0llX %s\n", addr, symbolNameForAddress(addr));
}
+#endif
template <typename A>
void DyldInfoPrinter<A>::printFunctionStartLine(uint64_t addr)
}
}
+template <typename A>
+void DyldInfoPrinter<A>::printDRInfo()
+{
+ if ( fDRInfo == NULL ) {
+ printf("no Designated Requirements info\n");
+ }
+ else {
+ printf("dylibs DRs\n");
+ const uint8_t* start = ((uint8_t*)fHeader + fDRInfo->dataoff());
+ //const uint8_t* end = ((uint8_t*)fHeader + fDRInfo->dataoff() + fDRInfo->datasize());
+ typedef Security::SuperBlob<Security::kSecCodeMagicDRList> DRListSuperBlob;
+ typedef Security::SuperBlob<Security::kSecCodeMagicRequirementSet> InternalRequirementsSetBlob;
+ const DRListSuperBlob* topBlob = (DRListSuperBlob*)start;
+ if ( topBlob->validateBlob(fDRInfo->datasize()) ) {
+ if ( topBlob->count() == fDylibLoadCommands.size() ) {
+ for(unsigned i=0; i < topBlob->count(); ++i) {
+ printf(" %-20s ", fDylibs[i]);
+ const Security::BlobCore* item = topBlob->find(i);
+ if ( item != NULL ) {
+ const uint8_t* itemStart = (uint8_t*)item;
+ const uint8_t* itemEnd = itemStart + item->length();
+ for(const uint8_t* p=itemStart; p < itemEnd; ++p)
+ printf("%02X ", *p);
+ }
+ else {
+ printf("no DR info");
+ }
+ printf("\n");
+ }
+ }
+ else {
+ fprintf(stderr, "superblob of DRs has a different number of elements than dylib load commands\n");
+ }
+ }
+ else {
+ fprintf(stderr, "superblob of DRs invalid\n");
+ }
+ }
+}
+
+
+
+
+
+template <typename A>
+void DyldInfoPrinter<A>::printDataInCode()
+{
+ if ( fDataInCode == NULL ) {
+ printf("no data-in-code info\n");
+ }
+ else {
+ printf("offset length data-kind\n");
+ const macho_data_in_code_entry<P>* start = (macho_data_in_code_entry<P>*)((uint8_t*)fHeader + fDataInCode->dataoff());
+ const macho_data_in_code_entry<P>* end = (macho_data_in_code_entry<P>*)((uint8_t*)fHeader + fDataInCode->dataoff() + fDataInCode->datasize());
+ for (const macho_data_in_code_entry<P>* p=start; p < end; ++p) {
+ const char* kindStr = "???";
+ switch ( p->kind() ) {
+ case 1:
+ kindStr = "data";
+ break;
+ case 2:
+ kindStr = "jumptable8";
+ break;
+ case 3:
+ kindStr = "jumptable16";
+ break;
+ case 4:
+ kindStr = "jumptable32";
+ break;
+ case 5:
+ kindStr = "jumptable32absolute";
+ break;
+ }
+ printf("0x%08X 0x%04X %s\n", p->offset(), p->length(), kindStr);
+ }
+ }
+}
+
+
template <>
ppc::P::uint_t DyldInfoPrinter<ppc>::relocBase()
return fFirstWritableSegment->vmaddr();
}
+#if SUPPORT_ARCH_arm_any
template <>
arm::P::uint_t DyldInfoPrinter<arm>::relocBase()
{
else
return fFirstSegment->vmaddr();
}
+#endif
template <>
return "??";
}
+#if SUPPORT_ARCH_arm_any
template <>
const char* DyldInfoPrinter<arm>::relocTypeName(uint8_t r_type)
{
else
return "??";
}
-
+#endif
template <typename A>
void DyldInfoPrinter<A>::printRelocRebaseInfo()
else
throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
break;
+#if SUPPORT_ARCH_arm_any
case CPU_TYPE_ARM:
if ( DyldInfoPrinter<arm>::validFile(p + offset) )
DyldInfoPrinter<arm>::make(p + offset, size, path, (sPreferredArch == 0));
else
throw "in universal file, arm slice does not contain arm mach-o";
break;
+#endif
default:
throwf("in universal file, unknown architecture slice 0x%x\n", cputype);
}
else if ( DyldInfoPrinter<x86_64>::validFile(p) ) {
DyldInfoPrinter<x86_64>::make(p, length, path, false);
}
+#if SUPPORT_ARCH_arm_any
else if ( DyldInfoPrinter<arm>::validFile(p) ) {
DyldInfoPrinter<arm>::make(p, length, path, false);
}
+#endif
else {
throw "not a known file type";
}
{
fprintf(stderr, "Usage: dyldinfo [-arch <arch>] <options> <mach-o file>\n"
"\t-dylibs print dependent dylibs\n"
+ "\t-dr print dependent dylibs and show any recorded DR info\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-opcodes print opcodes used to generate the rebase and binding information\n"
"\t-function_starts print table of function start addresses\n"
"\t-export_dot print a GraphViz .dot file of the exported symbols trie\n"
+ "\t-data_in_code print any data-in-code inforamtion\n"
);
}
else if ( strcmp(arch, "x86_64") == 0 )
sPreferredArch = CPU_TYPE_X86_64;
else {
+ const char* archName = argv[++i];
+ if ( archName == NULL )
+ throw "-arch missing architecture name";
bool found = false;
- for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) {
- if ( strcmp(t->subTypeName,arch) == 0 ) {
- sPreferredArch = CPU_TYPE_ARM;
- sPreferredSubArch = t->subType;
- found = true;
- break;
+ for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) {
+ if ( strcmp(t->archName,archName) == 0 ) {
+ sPreferredArch = t->cpuType;
+ if ( t->isSubType )
+ sPreferredSubArch = t->cpuSubType;
}
}
if ( !found )
- throwf("unknown architecture %s", arch);
+ throwf("unknown architecture %s", archName);
}
}
else if ( strcmp(arg, "-rebase") == 0 ) {
else if ( strcmp(arg, "-dylibs") == 0 ) {
printDylibs = true;
}
+ else if ( strcmp(arg, "-dr") == 0 ) {
+ printDRs = true;
+ }
+ else if ( strcmp(arg, "-data_in_code") == 0 ) {
+ printDataCode = true;
+ }
else {
throwf("unknown option: %s\n", arg);
}
// check that all load commands fit within the load command space file
const macho_encryption_info_command<P>* encryption_info = NULL;
const macho_thread_command<P>* threadInfo = NULL;
+ const macho_entry_point_command<P>* entryPoint = 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();
case LC_LOAD_UPWARD_DYLIB:
case LC_VERSION_MIN_MACOSX:
case LC_VERSION_MIN_IPHONEOS:
- case LC_FUNCTION_STARTS:
case LC_RPATH:
+ case LC_FUNCTION_STARTS:
+ case LC_DYLD_ENVIRONMENT:
+ case LC_DATA_IN_CODE:
+ case LC_DYLIB_CODE_SIGN_DRS:
+ case LC_SOURCE_VERSION:
break;
case LC_DYLD_INFO:
case LC_DYLD_INFO_ONLY:
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;
+ case LC_MAIN:
+ if ( fHeader->filetype() != MH_EXECUTE )
+ throw "LC_MAIN can only be used in MH_EXECUTE file types";
+ entryPoint = (macho_entry_point_command<P>*)cmd;
+ break;
case LC_UNIXTHREAD:
if ( fHeader->filetype() != MH_EXECUTE )
throw "LC_UNIXTHREAD can only be used in MH_EXECUTE file types";
if ( (initialPC < fTEXTSegment->vmaddr()) || (initialPC >= (fTEXTSegment->vmaddr()+fTEXTSegment->vmsize())) )
throwf("entry point 0x%0llX is outside __TEXT segment", (long long)initialPC);
}
-
+ else if ( entryPoint != NULL ) {
+ pint_t initialOffset = entryPoint->entryoff();
+ if ( (initialOffset < fTEXTSegment->fileoff()) || (initialOffset >= (fTEXTSegment->fileoff()+fTEXTSegment->filesize())) )
+ throwf("entry point 0x%0llX is outside __TEXT segment", (long long)initialOffset);
+ }
// checks for executables
bool isStaticExecutable = false;
cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
}
if ( isStaticExecutable ) {
- if ( fHeader->flags() != MH_NOUNDEFS )
+ if ( (fHeader->flags() != MH_NOUNDEFS) && (fHeader->flags() != (MH_NOUNDEFS|MH_PIE)) )
throw "invalid bits in mach_header flags for static executable";
}
}
break;
case LC_DYSYMTAB:
{
- if ( isStaticExecutable )
+ if ( isStaticExecutable &&! fSlidableImage )
throw "LC_DYSYMTAB should not be used in static executable";
foundDynamicSymTab = true;
fDynamicSymbolTable = (macho_dysymtab_command<P>*)cmd;
throw "function starts data size not a multiple of pointer size";
}
break;
+ case LC_DATA_IN_CODE:
+ {
+ const macho_linkedit_data_command<P>* info = (macho_linkedit_data_command<P>*)cmd;
+ if ( info->dataoff() < linkEditSegment->fileoff() )
+ throw "data-in-code data not in __LINKEDIT";
+ if ( (info->dataoff()+info->datasize()) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) )
+ throw "data-in-code data not in __LINKEDIT";
+ if ( (info->dataoff() % sizeof(pint_t)) != 0 )
+ throw "data-in-code data table not pointer aligned";
+ if ( (info->datasize() % sizeof(pint_t)) != 0 )
+ throw "data-in-code data size not a multiple of pointer size";
+ }
+ break;
+ case LC_DYLIB_CODE_SIGN_DRS:
+ {
+ const macho_linkedit_data_command<P>* info = (macho_linkedit_data_command<P>*)cmd;
+ if ( info->dataoff() < linkEditSegment->fileoff() )
+ throw "dependent dylib DR data not in __LINKEDIT";
+ if ( (info->dataoff()+info->datasize()) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) )
+ throw "dependent dylib DR data not in __LINKEDIT";
+ if ( (info->dataoff() % sizeof(pint_t)) != 0 )
+ throw "dependent dylib DR data table not pointer aligned";
+ if ( (info->datasize() % sizeof(pint_t)) != 0 )
+ throw "dependent dylib DR data size not a multiple of pointer size";
+ }
+ break;
}
cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
}
// FIX: check r_symbol
}
+#if SUPPORT_ARCH_arm_any
template <>
void MachOChecker<arm>::checkExternalReloation(const macho_relocation_info<P>* reloc)
{
throw "external relocation address not in writable segment";
// FIX: check r_symbol
}
+#endif
template <>
throw "local relocation address not in writable segment";
}
+#if SUPPORT_ARCH_arm_any
template <>
void MachOChecker<arm>::checkLocalReloation(const macho_relocation_info<P>* reloc)
{
throw "local relocation address not in writable segment";
}
}
+#endif
template <typename A>
void MachOChecker<A>::checkRelocations()
}
}
}
+ return false;
}
template <typename A>
else
throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
break;
+#if SUPPORT_ARCH_arm_any
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;
+#endif
default:
throwf("in universal file, unknown architecture slice 0x%x\n", cputype);
}
else if ( MachOChecker<x86_64>::validFile(p) ) {
MachOChecker<x86_64>::make(p, length, path);
}
+#if SUPPORT_ARCH_arm_any
else if ( MachOChecker<arm>::validFile(p) ) {
MachOChecker<arm>::make(p, length, path);
}
+#endif
else {
throw "not a known file type";
}
}
}
+#if SUPPORT_ARCH_arm_any
template <>
void Rebaser<arm>::doLocalRelocation(const macho_relocation_info<P>* reloc)
{
}
}
}
+#endif
template <typename A>
void Rebaser<A>::doLocalRelocation(const macho_relocation_info<P>* reloc)
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 {
- bool found = false;
- for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) {
- if ( strcmp(t->subTypeName,arch) == 0 ) {
- onlyArchs.insert(CPU_TYPE_ARM);
- found = true;
- break;
- }
+ const char* archName = argv[++i];
+ if ( archName == NULL )
+ throw "-arch missing architecture name";
+ bool found = false;
+ for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) {
+ if ( strcmp(t->archName,archName) == 0 ) {
+ onlyArchs.insert(t->cpuType);
+ found = true;
}
- if ( !found )
- throwf("unknown architecture %s", arch);
}
+ if ( !found )
+ throwf("unknown architecture %s", archName);
}
else {
usage();
use Data::Dumper;
use File::Find;
use Cwd qw(realpath);
+use English;
my @args = @ARGV;
'stderr' => [],
};
+# Determine how many tests to run at a time in parallel. Default to cpu count.
+my $max_concurrent_tests = $ENV{'LD_UNIT_TEST_CONCURRENCY'};
+if (!defined $max_concurrent_tests) {
+ # shell command returns cpu count in exit status
+ system("/bin/csh", "-c", "set n=`sysctl hw.ncpu`; exit \$n[2]");
+ if ($? == -1 || $? & 127) {
+ die("could not determine cpu count");
+ }
+ $max_concurrent_tests = $? >> 8;
+}
+
my $keyword;
my $max_keyword_len = 0;
foreach $keyword (keys %$keywords)
sub print_line
{
- my ($keyword, $val) = @_;
-
+ my ($file, $keyword, $val) = @_;
+
if(!exists($$keywords{$keyword}))
{
- print STDERR "error: keyword $keyword not in \$keywords set\n";
- exit(1);
+ print STDERR "error: keyword $keyword not in \$keywords set\n";
+ exit(1);
}
-
+
my $keyword_len = 0;
-
+
if($keyword ne $last_keyword)
{
- print("$keyword"); print($delim);
- $keyword_len = length($keyword) + length($delim);
+ print($file "$keyword"); print($file $delim);
+ $keyword_len = length($keyword) + length($delim);
}
if($max_keyword_len > $keyword_len)
{
- my $num_spaces = $max_keyword_len - $keyword_len;
- print(' ' x $num_spaces);
+ my $num_spaces = $max_keyword_len - $keyword_len;
+ print($file ' ' x $num_spaces);
}
- print("$val");
+ print($file "$val");
if(0)
{
- $last_keyword = $keyword;
+ $last_keyword = $keyword;
}
}
my $root = '.';
$root = &realpath($root);
-print_line("root", "$root\n");
-
+print_line(*STDOUT, "root", "$root\n");
+my $running_test_count=0;
find($find_opts, $root);
+while ( $running_test_count > 0 ) {
+ &reaper;
+}
sub find_callback
{
if(exists($$makefiles{$_}))
{
- my $makefile = $_;
- my $reldir = $File::Find::dir;
- $reldir =~ s|^$root/||;
-
- &print_line("cwd", "\$root/$reldir\n");
- my $cmd = [ "make" ];
-
- my $arg; foreach $arg (@ARGV) { push @$cmd, $arg; } # better way to do this?
- &print_line("cmd", "@$cmd\n");
-
- open(SAVEOUT, ">&STDOUT") || die("$!");
- open(SAVEERR, ">&STDERR") || die("$!");
- open(STDOUT, ">/tmp/unit-tests-stdout") || die("$!");
- open(STDERR, ">/tmp/unit-tests-stderr") || die("$!");
-
- $ENV{UNIT_TEST_NAME} = $reldir;
- my $exit = system(@$cmd);
-
- close(STDOUT) || die("$!");
- close(STDERR) || die("$!");
- open(STDOUT, ">&SAVEOUT") || die("$!");
- open(STDERR, ">&SAVEERR") || die("$!");
-
- &print_line("exit", "$exit\n");
+ my $makefile = $_;
+ my $reldir = $File::Find::dir;
+ $reldir =~ s|^$root/||;
+
+ my $cmd = [ "make" ];
+
+ my $arg; foreach $arg (@ARGV) { push @$cmd, $arg; } # better way to do this?
+
+ $ENV{UNIT_TEST_NAME} = $reldir;
+ my $pid = fork();
+ if (not defined $pid) {
+ die "Couldn't fork"
+ }
+ elsif ($pid == 0) {
+ # Child. Redirect stdout/stderr to files and exec test.
+ open(STDOUT, ">/tmp/unit-tests-stdout.$PID") || die("$!");
+ open(STDERR, ">/tmp/unit-tests-stderr.$PID") || die("$!");
+ exec 'make', @ARGV;
+ exit(-1); #just to be sure
+ }
+
+ # Write the test cwd/cmd to a temporary file associated with the child's pid, to be retrieved later.
+ my $info;
+ open($info, ">/tmp/unit-tests-info.$pid") || die("$!");
+ &print_line($info, "cwd", "\$root/$reldir\n"); # post filtering depends on this line being first
+ &print_line($info, "cmd", "@$cmd\n");
+ close($info) || die("$!");
+
+ $running_test_count++;
+ # if we have reached max # of concurrent tests, wait for one to exit
+ if ( $running_test_count == $max_concurrent_tests ) {
+ &reaper;
+ }
+ }
+}
- open(OUT, "</tmp/unit-tests-stdout") || die("$!");
- while(<OUT>)
- {
- &print_line("stdout", "$_");
- }
- close(OUT) || die("$!");
- unlink("/tmp/unit-tests-stdout");
-
- open(ERR, "</tmp/unit-tests-stderr") || die("$!");
- while(<ERR>)
- {
- &print_line("stderr", "$_");
+sub reaper {
+ if ( $running_test_count > 0 ) {
+ my $pid = wait;
+ if ( $pid == -1 ) {
+ die("no child\n");
+ }
+ my $exit = $?;
+
+ $running_test_count--;
+
+ open(INFO, "</tmp/unit-tests-info.$pid") || die("$!");
+ while(<INFO>)
+ {
+ print $_;
+ }
+ close(INFO) || die("$!");
+ unlink("/tmp/unit-tests-info.$pid");
+
+ &print_line(*STDOUT, "exit", "$exit\n");
+
+ open(OUT, "</tmp/unit-tests-stdout.$pid") || die("$!");
+ while(<OUT>)
+ {
+ &print_line(*STDOUT, "stdout", "$_");
+ }
+ close(OUT) || die("$!");
+ unlink("/tmp/unit-tests-stdout.$pid");
+
+ open(ERR, "</tmp/unit-tests-stderr.$pid") || die("$!");
+ while(<ERR>)
+ {
+ &print_line(*STDOUT, "stderr", "$_");
+ }
+ close(ERR) || die("$!");
+ unlink("/tmp/unit-tests-stderr.$pid");
}
- close(ERR) || die("$!");
- }
- unlink("/tmp/unit-tests-stderr");
}
+
# set default to be all
VALID_ARCHS ?= "i386 x86_64 armv6"
-IOS_SDK = /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.Internal.sdk
MYDIR=$(shell cd ../../bin;pwd)
LD = ld
SDKExtra = -isysroot /Developer/SDKs/MacOSX10.6.sdk
endif
-CC = cc -arch ${ARCH} ${SDKExtra}
+CC = $(shell xcrun -find clang) -arch ${ARCH} ${SDKExtra}
CCFLAGS = -Wall
ASMFLAGS =
VERSION_NEW_LINKEDIT = -mmacosx-version-min=10.6
VERSION_OLD_LINKEDIT = -mmacosx-version-min=10.4
LD_NEW_LINKEDIT = -macosx_version_min 10.6
-CXX = c++ -arch ${ARCH} ${SDKExtra}
+CXX = $(shell xcrun -find clang++) -arch ${ARCH} ${SDKExtra}
CXXFLAGS = -Wall
+IOS_SDK = $(shell xcodebuild -sdk iphoneos6.0.internal -version Path)
+
ifeq ($(ARCH),armv6)
LDFLAGS := -syslibroot $(IOS_SDK)
override FILEARCH = arm
- CC = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/clang -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
- CXX = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/clang++ -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
+ CC = $(shell xcrun -find clang) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
+ CXX = $(shell xcrun -find clang++) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
VERSION_NEW_LINKEDIT = -miphoneos-version-min=4.0
VERSION_OLD_LINKEDIT = -miphoneos-version-min=3.0
LD_SYSROOT = -syslibroot $(IOS_SDK)
ifeq ($(ARCH),armv7)
LDFLAGS := -syslibroot $(IOS_SDK)
override FILEARCH = arm
- CC = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/clang -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
- CXX = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/clang++ -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
+ CC = $(shell xcrun -find clang) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
+ CXX = $(shell xcrun -find clang++) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
VERSION_NEW_LINKEDIT = -miphoneos-version-min=4.0
VERSION_OLD_LINKEDIT = -miphoneos-version-min=3.0
LD_SYSROOT = -syslibroot $(IOS_SDK)
CXXFLAGS += -mthumb
override ARCH = armv6
override FILEARCH = arm
- CC = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/clang -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
- CXX = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/clang++ -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
+ CC = $(shell xcrun -find clang) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
+ CXX = $(shell xcrun -find clang++) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
VERSION_NEW_LINKEDIT = -miphoneos-version-min=4.0
VERSION_OLD_LINKEDIT = -miphoneos-version-min=3.0
LD_SYSROOT = -syslibroot $(IOS_SDK)
CXXFLAGS += -mthumb
override ARCH = armv7
override FILEARCH = arm
- CC = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/clang -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
- CXX = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/clang++ -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
+ CC = $(shell xcrun -find clang) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
+ CXX = $(shell xcrun -find clang++) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
VERSION_NEW_LINKEDIT = -miphoneos-version-min=4.0
VERSION_OLD_LINKEDIT = -miphoneos-version-min=3.0
LD_SYSROOT = -syslibroot $(IOS_SDK)
all_archs="x86_64 i386"
valid_archs="x86_64 i386"
# only test arm code if iOS platform tools installed
-if [ -d /Developer/Platforms/iPhoneOS.platform/Developer/SDKs ]
+if [ -d /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs ]
then
all_archs="${all_archs} armv7"
valid_archs="${valid_archs} armv7"
${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -arch ${ARCH} -c -O2 tl_test2.c -o tl_test2-${ARCH}.o
# verify that the alignment is correct in the .o
- ${OBJECTDUMP} -only _ai -align -no_content tl_test2-${ARCH}.o|${FAIL_IF_ERROR} grep '\<0 mod 16\>' >/dev/null
+ ${OBJECTDUMP} -only _ai -align -no_content tl_test2-${ARCH}.o|${FAIL_IF_ERROR} grep '0 mod 16' >/dev/null
# now verify the executable
${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -arch ${ARCH} -O2 tl_test2-${ARCH}.o -o tl_test2-${ARCH}
- ${FAIL_IF_ERROR} sh -c "nm tl_test2-${ARCH}|grep '0 D _ai\>' >/dev/null"
+ ${FAIL_IF_ERROR} sh -c "nm tl_test2-${ARCH}|grep '0 D _ai' >/dev/null"
${PASS_IFF_GOOD_MACHO} tl_test2-${ARCH}
clean:
--- /dev/null
+##
+# Copyright (c) 2012 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
+
+#
+# The point of this test is to check that -ObjC loads all (and only)
+# .o files that contain Objective-C code that has gone through ld -r.
+#
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} foo.m -c -o foo.o
+ ${LD} -r -arch ${ARCH} foo.o -o foo-r.o
+ ${CC} ${CCFLAGS} bar.c -c -o bar.o
+ ${LD} -r -arch ${ARCH} bar.o -o bar-r.o
+ ${CC} ${CCFLAGS} baz.m -c -o baz.o
+ ${LD} -r -arch ${ARCH} baz.o -o baz-r.o
+ ${CC} ${CCFLAGS} cat.m -c -o cat.o
+ ${LD} -r -arch ${ARCH} cat.o -o cat-r.o
+ libtool -static foo-r.o bar-r.o baz-r.o cat-r.o -o liball.a
+ ${CC} ${CCFLAGS} main.c liball.a -o main -ObjC -framework Foundation -framework CoreFoundation
+ ${FAIL_IF_BAD_MACHO} main
+ nm main | grep "_bar" | ${FAIL_IF_STDIN}
+ nm main | grep "_Foo" | ${FAIL_IF_EMPTY}
+ nm main | grep "_Baz" | ${FAIL_IF_EMPTY}
+ nm main | grep "_mycatfunc" | ${FAIL_IF_EMPTY}
+ ${PASS_IFF_GOOD_MACHO} main
+
+clean:
+ rm -rf main *.o *.a
--- /dev/null
+
+int bar() { return 0; }
--- /dev/null
+#include <Foundation/Foundation.h>
+
+@interface Baz : NSObject
+@end
+
+@implementation Baz
+@end
+
--- /dev/null
+#include <Foundation/Foundation.h>
+
+@interface NSObject(Cat)
+@end
+
+@implementation NSObject(Cat)
+@end
+
+@interface NSObject(Dog)
+@end
+
+@implementation NSObject(Dog)
+@end
+
+void mycatfunc() {}
+
--- /dev/null
+#include <Foundation/Foundation.h>
+
+@interface Foo : NSObject
+@end
+
+@implementation Foo
+@end
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <stdio.h>
+
+
+int main()
+{
+ fprintf(stdout, "hello\n");
+ return 0;
+}
all:
+ # Verify that we fail if there is no valid place to insert branch islands.
+ ${CC} ${CCFLAGS} hello.c atomic_space.s extra.c -o hello ${ARCH_FLAGS} 2>&1 | grep "Unable to insert branch island. No insertion point available." | ${PASS_IFF_STDIN}
+
${CC} ${CCFLAGS} hello.c space.s extra.c -o hello ${ARCH_FLAGS}
${PASS_IFF_GOOD_MACHO} hello
+
clean:
rm hello
--- /dev/null
+
+#if __ppc__
+
+ .text
+
+_prejunk:
+ mr r3,r5
+ mr r3,r4
+ blr
+
+
+_space1:
+ .space 15*1024*1024 + 2
+
+ .align 5
+_junk:
+ mr r3,r5
+ mr r3,r4
+ blr
+
+
+_space2:
+ .space 2*1024*1024
+
+#endif
+
+
+#if __arm__
+
+ .text
+_prejunk:
+ mov r0, #1
+ nop
+
+#if __thumb2__
+ // thumb2 branches are +/- 16MB
+_space1:
+ .space 14*1024*1024
+_space2:
+ .space 14*1024*1024
+_space3:
+ .space 14*1024*1024
+
+
+#elif __thumb__
+ // thumb1 branches are +/- 4MB
+_space1:
+ .space 3*1024*1024
+_space2:
+ .space 3*1024*1024
+_space3:
+ .space 3*1024*1024
+
+#else
+
+ // ARM branches are +/- 32MB
+_space1:
+ .space 14*1024*1024
+_space2:
+ .space 14*1024*1024
+_space3:
+ .space 14*1024*1024
+
+#endif
+
+ .align 5
+_junk:
+ mov r0, #1
+ nop
+
+
+_space4:
+ .space 2*1024*1024
+#endif
+
+ //.subsections_via_symbols
--- /dev/null
+##
+# Copyright (c) 2011 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 L$start$.. labels are tracked
+#
+
+all:
+ ${CC} ${CCFLAGS} -c test.s -o test.o
+ ${LD} -r -arch ${ARCH} test.o -o test2.o
+ #nm main | grep _dtrace_probe | ${FAIL_IF_EMPTY}
+ #${PASS_IFF_GOOD_MACHO} main
+
+clean:
+ rm -rf test.o test2.o
--- /dev/null
+
+#include <stdio.h>
+
+#define DTRACE_STRINGIFY(s) #s
+#define DTRACE_TOSTRING(s) DTRACE_STRINGIFY(s)
+
+#define DTRACE_NOPS \
+ "nop" "\n\t" \
+ "nop" "\n\t" \
+ "nop" "\n\t"
+
+
+#define DTRACE_LAB(p, n) \
+ "__dtrace_probe$" DTRACE_TOSTRING(%=__LINE__) DTRACE_STRINGIFY(_##p##___##n)
+
+#define DTRACE_LABEL(p, n) \
+ ".section __DATA, __data\n\t" \
+ ".globl " DTRACE_LAB(p, n) "\n\t" \
+ DTRACE_LAB(p, n) ":\n\t" ".long 1f""\n\t" \
+ ".text" "\n\t" \
+ "1:"
+
+#define DTRACE_CALL(p,n) \
+ DTRACE_LABEL(p,n) \
+ DTRACE_NOPS
+
+#define DTRACE_CALL0ARGS(provider, name) \
+ __asm volatile ( \
+ DTRACE_CALL(provider, name) \
+ : \
+ : \
+ );
+
+int deadwood()
+{
+ DTRACE_CALL0ARGS(__foo__, test2)
+ return 0;
+}
+
+
+int main() {
+ int a = 1;
+
+ while(a) {
+ DTRACE_CALL0ARGS(__foo__, test1)
+ }
+
+ return 0;
+}
--- /dev/null
+
+ .text
+
+_foo:
+ nop
+ nop
+l$start$data$1:
+ nop
+ nop
+ nop
+l$start$jt8$2:
+ nop
+ nop
+l$start$jt16$3:
+ nop
+ nop
+l$start$code$n4:
+ nop
+ nop
+
+
+
+ .subsections_via_symbols
+
+
\ No newline at end of file
--- /dev/null
+##
+# Copyright (c) 2007-2010 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 multiple duplicate symbols are reported.
+#
+
+all:
+ ${CC} -arch ${ARCH} -c main_extern.c
+ ${CC} -arch ${ARCH} -c main_no_extern.c
+ ${CC} -arch ${ARCH} -c duplicates.c
+ ${CC} -arch ${ARCH} -dynamiclib -o duplicates.dylib duplicates.o
+ libtool -static -o duplicates.a duplicates.o
+
+ # simple case of directly linking duplicates should fail
+ ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main_extern.o duplicates.o 2>&1 | grep "2 duplicate symbols" | ${FAIL_IF_EMPTY}
+
+
+ # duplicates in a dylib succeed
+ ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} main_extern.o duplicates.dylib 2>&1 | grep "2 duplicate symbols" | ${FAIL_IF_STDIN}
+
+ # static lib - only fail if module containing duplicate symbols is actually referenced
+ ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main_extern.o duplicates.a 2>&1 | grep "2 duplicate symbols" | ${FAIL_IF_EMPTY}
+ ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} main_no_extern.o duplicates.a 2>&1 | grep "2 duplicate symbols" | ${FAIL_IF_STDIN}
+
+ # simple case should succeed if we dead strip because none of the duplicates are referenced
+ ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -dead_strip main_extern.o duplicates.o
+
+ rm -f a.out *.o *.a *.dylib
+ ${PASS_IFF} true
--- /dev/null
+#include <stdio.h>
+
+void a() {
+}
+
+void b() {
+}
+
+void c() {
+}
--- /dev/null
+/*
+This file references an extern function c() that lives in
+a separate compilation unit that also has a() and b().
+*/
+
+extern void c();
+
+void a() {
+}
+
+void b() {
+}
+
+int main() {
+c();
+}
--- /dev/null
+/*
+This file contains symbols that are duplicated in another file,
+but does not reference anything that would pull in the duplicates.
+*/
+
+void a() {
+}
+
+void b() {
+}
+
+int main() {
+}
--- /dev/null
+##
+# Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# Test that a program can have is "main" in a dylib
+#
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} main.c -dynamiclib -o libmain.dylib
+ ${CC} ${CCFLAGS} foo.c libmain.dylib -o main -Wl,-new_main
+ ${PASS_IFF_GOOD_MACHO} main
+
+clean:
+ rm -rf libmain.dylib main
--- /dev/null
+void foo() {}
--- /dev/null
+#include <stdio.h>
+
+int main()
+{
+ fprintf(stdout, "hello\n");
+ return 0;
+}
\ No newline at end of file
--- /dev/null
+##
+# Copyright (c) 2011 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 forcing a symbol weak does not cause spurious warnings
+#
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} foo.c test.c -dynamiclib -o libtest.dylib -Wl,-force_symbols_weak_list,weak.exp
+ otool -Iv libtest.dylib | grep _foo | ${FAIL_IF_EMPTY}
+ ${PASS_IFF_GOOD_MACHO} libtest.dylib
+
+clean:
+ rm libtest.dylib
--- /dev/null
+
+int foo = 5;
\ No newline at end of file
--- /dev/null
+
+extern int foo;
+
+int getfoo() { return foo; }
+
--- /dev/null
+##
+# Copyright (c) 2011 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 $ld$install_name$osXX$/new/path overides install_name
+#
+
+FORCE_MIN_OS_VERSION = -mmacosx-version-min=10.5
+
+ifeq ($(FILEARCH),arm)
+ FORCE_MIN_OS_VERSION = -miphoneos-version-min=4.0
+endif
+
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -install_name /usr/local/lib/libfoo.dylib
+ otool -L libfoo.dylib | grep /usr/local/lib/libfoo.dylib | ${FAIL_IF_EMPTY}
+ ${CC} ${CCFLAGS} main.c libfoo.dylib -o main
+ otool -L main | grep /usr/local/lib/libfoo.dylib | ${FAIL_IF_EMPTY}
+ ${CC} ${CCFLAGS} main.c libfoo.dylib -o mainAlt ${FORCE_MIN_OS_VERSION}
+ otool -L mainAlt | grep /usr/lib/libfoo.dylib | ${FAIL_IF_EMPTY}
+ ${PASS_IFF_GOOD_MACHO} libfoo.dylib
+
+clean:
+ rm -f libfoo.dylib main mainAlt
--- /dev/null
+
+int foo()
+{
+ return 0;
+}
+
+
+#if __arm__
+ #define INSTALL_NAME_4_0(sym) \
+ extern const char install_name_4_0 __asm("$ld$install_name$os4.0$" #sym ); const char install_name_4_0 = 0;
+
+ INSTALL_NAME_4_0(/usr/lib/libfoo.dylib)
+#else
+ #define INSTALL_NAME_10_5(sym) \
+ extern const char install_name_10_5 __asm("$ld$install_name$os10.5$" #sym ); const char install_name_10_5 = 0;
+
+ INSTALL_NAME_10_5(/usr/lib/libfoo.dylib)
+#endif
--- /dev/null
+
+extern int foo();
+
+int main()
+{
+ foo();
+ return 0;
+}
+
#
# Test the we set the stack execution bit properly.
-run:
- if [ -f /Developer/usr/bin/llvm-gcc-4.2 ] ; then \
- $(MAKE) all ; \
- else \
- ${PASS_IFF} /usr/bin/true ; \
- fi
all: zero one two three four five six seven eight nine ten \
eleven twelve thirteen fourteen fifteen sixteen seventeen \
--- /dev/null
+##
+# Copyright (c) 2012 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
+
+#
+# <rdar://problem/11124216> [lto] Linking libLTO.dylib causing an assertion in ld
+#
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} -flto bar.c -c -o bar.o
+ ${CC} ${CCFLAGS} bar.o -dynamiclib -o libbar.dylib -dead_strip -Wl,-exported_symbol,_bar
+ ${PASS_IFF_GOOD_MACHO} libbar.dylib
+
+clean:
+ rm -f bar.o libbar.dylib
+
--- /dev/null
+
+
+void qux() {}
+
+asm("\t.text\n"
+ "\t.globl _foo\n"
+ "_foo:\n"
+ "\tnop\n");
+
+extern void foo();
+
+void (*bar())() {
+ return foo;
+}
TESTROOT = ../..
include ${TESTROOT}/include/common.makefile
+SHELL = bash # use bash shell so we can redirect just stderr
#
# <rdar://problem/7438246> LTO with 'dead code strip' can't ignore unused functions with undefined references
${CC} ${CCFLAGS} -flto main.c -c -o main.o
${CC} ${CCFLAGS} main.o bar.o -o main
${CC} ${CCFLAGS} main.o bar.o -o main -dead_strip
+ ${CC} ${CCFLAGS} main.o bar.o -dynamiclib -o libmain.dylib -Wl,-exported_symbol,_main
+ ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.o bar.o -dynamiclib -o libmain.dylib -dead_strip 2>/dev/null
${PASS_IFF_GOOD_MACHO} main
clean:
# 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 \
+ ${CC} ${CCFLAGS} -flto a.c -c -o a.o
+ ${CC} ${CCFLAGS} -flto b.c -c -o b.o
+ ${CC} ${CCFLAGS} -flto main.c -c -o main.o
+ ${CC} ${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}
${CC} ${CCFLAGS} foo.c -o foo -gdwarf-2
${FAIL_IF_BAD_MACHO} foo
${OTOOL} -hlv foo | grep LC_UUID | ${FAIL_IF_EMPTY}
- rm -f foo
-
-# Test main executable built with stabs has uuid
- ${CC} ${CCFLAGS} foo.c -o foo -gfull -gstabs+
- ${FAIL_IF_BAD_MACHO} foo
- ${OTOOL} -hlv foo | grep LC_UUID | ${FAIL_IF_EMPTY}
-
-# Test main executable built with dwarf and -no_uuid does not have uuid
- ${CC} ${CCFLAGS} foo.c -o foo -Wl,-no_uuid -gdwarf-2
- ${FAIL_IF_BAD_MACHO} foo
- ${OTOOL} -hlv foo | grep LC_UUID | ${FAIL_IF_STDIN}
-
-# Test ld -r of stabs file has no uuid (llvm does not support stabs, so use gcc)
- gcc-4.2 -arch ${ARCH} ${CCFLAGS} foo.c -c -o foo.o -gfull -gstabs+
- ${LD} -arch ${ARCH} foo.o -r -o foo2.o
- ${OTOOL} -hlv foo2.o | grep LC_UUID | ${FAIL_IF_STDIN}
-
-# Test ld -r of two files one with uuid produces a uuid
- ${CC} ${CCFLAGS} foo.c -c -o foo.o -gdwarf-2
- ${LD} -arch ${ARCH} foo.o -r -o foo2.o
- ${CC} ${CCFLAGS} bar.c -c -gstabs+ -o bar.o
- ${LD} -arch ${ARCH} -r foo.o bar.o -o foobar.o
- ${OTOOL} -hlv foobar.o | grep LC_UUID | ${PASS_IFF_STDIN}
+ ${PASS_IFF_GOOD_MACHO} foo
clean:
- rm -rf foo foo.o foo2.o bar.o foobar.o foo.dSYM
+ rm -rf foo
all:
${CC} ${CCFLAGS} -g test.m -c -o test.o
- libtool -static test.o -o libtest.a
+ ${CC} ${CCFLAGS} -g test2.m -c -o test2.o
+ libtool -static test.o test2.o -o libtest.a
${CC} ${CCFLAGS} main.m libtest.a -ObjC -o main -framework Foundation
nm main | grep mycatmethod1 | ${FAIL_IF_EMPTY}
nm main | grep mycatmethod2 | ${FAIL_IF_EMPTY}
- ${LD} -arch ${ARCH} -r -S -keep_private_externs test.o -o test-stripped.o
+ nm main | grep mymethod2 | ${FAIL_IF_EMPTY}
+ ${LD} -arch ${ARCH} -r -S -keep_private_externs test.o test2.o -o test-stripped.o
libtool -static test-stripped.o -o libtest-stripped.a
${CC} ${CCFLAGS} main.m libtest-stripped.a -all_load -dead_strip -o main2 -framework Foundation
nm main2 | grep mycatmethod1 | ${FAIL_IF_EMPTY}
nm main2 | grep mycatmethod2 | ${FAIL_IF_EMPTY}
+ nm main2 | grep mymethod2 | ${FAIL_IF_EMPTY}
${PASS_IFF_GOOD_MACHO} main2
clean:
- rm -rf test.o test-stripped.o libtest.a libtest-stripped.a main main2
+ rm -rf test.o test2.o test-stripped.o libtest.a libtest-stripped.a main main2
--- /dev/null
+
+#include <Foundation/Foundation.h>
+
+@interface MyClass : NSObject
+- (void) mymethod2;
+@end
+
+@implementation MyClass
+- (void) mymethod2 { }
+@end
+
run: all
all:
- ${CC} ${CCFLAGS} test.m -g -o test -framework Foundation
+ ${CC} ${CCFLAGS} test.m -g -o test -framework Foundation -Wno-objc-protocol-method-implementation
nm -ap test | grep GSYM | grep category | ${FAIL_IF_STDIN}
${PASS_IFF_GOOD_MACHO} test
OPTIONS =
ifeq ($(ARCH),i386)
- OPTIONS = -fobjc-abi-version=2 -Wl,-objc_abi_version,2
+ OPTIONS = -fobjc-abi-version=2 -Wl,-objc_abi_version,2 -isysroot /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk -fobjc-legacy-dispatch
endif
all: all-${ARCH}
all-i386: all-rest
all-x86_64: all-rest
all-armv6: all-rest
+all-armv7: all-rest
all-rest:
# check optimzation of category methods
OPTIONS =
ifeq ($(ARCH),i386)
- OPTIONS = -fobjc-abi-version=2 -Wl,-objc_abi_version,2
+ OPTIONS = -fobjc-abi-version=2 -Wl,-objc_abi_version,2 -isysroot /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk -fobjc-legacy-dispatch
+endif
+
+ifeq ($(ARCH),arm)
+ OPTIONS = -isysroot=${IOS_SDK}
endif
all: all-${ARCH}
all-i386: all-rest
all-x86_64: all-rest
all-armv6: all-rest
+all-armv7: all-rest
all-rest:
# check optimization can be turned off
OPTIONS =
ifeq ($(ARCH),i386)
- OPTIONS = -fobjc-abi-version=2 -Wl,-objc_abi_version,2
+ OPTIONS = -fobjc-abi-version=2 -Wl,-objc_abi_version,2 -isysroot /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk -fobjc-legacy-dispatch
endif
all: all-${FILEARCH}
${PASS_IFF_GOOD_MACHO} libfoo2.dylib
clean:
- rm -rf lib*.dylib warnings*.txt
\ No newline at end of file
+ rm -rf lib*.dylib warnings*.txt
${CC} ${CCFLAGS} main.c extra.s -DSUBSECTIONS=1 -o main4 -Wl,-order_file -Wl,main4.order
${FAIL_IF_BAD_MACHO} main4
- nm -n -g -j main4 | nm -njg main4 | egrep '*[1-9]' > main4.nm
+ nm -n -g -j main4 | egrep '_[a-zA-Z]+[1-9]' > main4.nm
${PASS_IFF} diff main4.nm main4.expected
--- /dev/null
+##
+# Copyright (c) 2012 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 these pipelined linking cases:
+# - missing pipe
+# - bad file passed via pipe
+# - eof on pipe
+# - success case
+# - differing file orders produce same binary
+#
+# When testing the success case take care to verify that pipelined linking is actually
+# enabled (environment variable is set). Because there is no difference in the command
+# line the test will still succeed with pipelined linking turned off, without testing
+# pipelined linking.
+#
+
+all:
+ # Make some object files
+ ${CC} ${CCFLAGS} foo.c -c -o foo.o
+ ${CC} ${CCFLAGS} bar.c -c -o bar.o
+ ${CC} ${CCFLAGS} cat.c -c -o cat.o
+ ls *.o > filelist
+
+ # missing fifo
+ (LD_PIPELINE_FIFO=bad_fifo ; export LD_PIPELINE_FIFO ; ${CC} ${CCFLAGS} -filelist filelist -o partial ; exit 0) 2>&1 | ${PASS_IFF_STDIN}
+
+ # partial file list passed via pipe
+ head -1 filelist > pipelineFile
+ (LD_PIPELINE_FIFO=pipelineFile ; export LD_PIPELINE_FIFO ; ${CC} ${CCFLAGS} -filelist filelist -o partial ; exit 0) 2>&1 | ${PASS_IFF_STDIN}
+ ${FAIL_IFF} ${MACHOCHECK} partial 2>&1 > /dev/null
+
+ # bogus file passed via pipe
+ echo "bogus_file.o" > pipelineFile
+ (LD_PIPELINE_FIFO=pipelineFile ; export LD_PIPELINE_FIFO ; ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} -filelist filelist -o bogus ; exit 0) 2>&1 | ${PASS_IFF_STDIN}
+ ${FAIL_IFF} ${MACHOCHECK} bogus 2>&1 > /dev/null
+
+ # success cases - check different orderings of files
+ sort < filelist > pipelineFile
+ (LD_PIPELINE_FIFO=pipelineFile ; export LD_PIPELINE_FIFO ; ${CC} ${CCFLAGS} -filelist filelist -o normal)
+ ${FAIL_IF_BAD_MACHO} normal
+ sort -r < filelist > pipelineFile
+ (LD_PIPELINE_FIFO=pipelineFile ; export LD_PIPELINE_FIFO ; ${CC} ${CCFLAGS} -filelist filelist -o reverse)
+ ${FAIL_IF_BAD_MACHO} reverse
+
+ # verify that the same binary was generated using both forward and reverse ordering
+ ${PASS_IFF} cmp reverse normal
+
+
+clean:
+ rm -f *.o filelist partial bogus reverse normal pipelineFile
+
--- /dev/null
+void bar()
+{
+}
+
--- /dev/null
+void cat()
+{
+}
+
--- /dev/null
+
+void foo()
+{
+}
+
+int main()
+{
+return 0;
+}
+
--- /dev/null
+##
+# Copyright (c) 2011 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
+
+RELOC_COUNT = 3
+ifeq (${ARCH},x86_64)
+ RELOC_COUNT = 2
+endif
+
+
+#
+# Test that ld can link a pie static executable
+#
+
+all:
+ ${CC} ${CCFLAGS} test.c -c -o test.o
+ ${CC} ${CCFLAGS} test.o -static -Wl,-pie -o test -e _entry -nostdlib -Wl,-new_linker
+ otool -rv test | grep __DATA | wc -l | grep ${RELOC_COUNT} | ${FAIL_IF_EMPTY}
+ # verify trying to use absolute addressing fails
+ ${CC} ${CCFLAGS} -static bad.c -c -o bad.o
+ ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} test.o bad.o -static -Wl,-pie -o test.bad -e _entry -nostdlib -Wl,-new_linker 2>/dev/null
+ ${PASS_IFF_GOOD_MACHO} test
+
+clean:
+ rm -rf test test.o bad.o test.bad
+
+
--- /dev/null
+static int my;
+
+int getmy()
+{
+#if __x86_64__
+ __asm(" .quad _my");
+#endif
+ return my;
+}
--- /dev/null
+
+int a;
+int b = 5;
+int* pa = &a;
+int* pb = &b;
+
+int foo()
+{
+ *pa = 4;
+ return a+b;
+}
+
+
+int entry()
+{
+ return foo();
+}
+
+
${CC} ${CCFLAGS} main.c libfoo_code.a -o main_code
nm -m main_code | grep _foo | grep __TEXT | ${FAIL_IF_STDIN}
# verify warning when code force to override tent
- ${CC} ${CCFLAGS} main.c -Wl,-force_load,libfoo_code.a -o main_code_force 2>main_code_force_warnings.txt
- grep warning main_code_force_warnings.txt | ${FAIL_IF_EMPTY}
+ #${CC} ${CCFLAGS} main.c -Wl,-force_load,libfoo_code.a -o main_code_force 2>main_code_force_warnings.txt
+ #grep warning main_code_force_warnings.txt | ${FAIL_IF_EMPTY}
${PASS_IFF_GOOD_MACHO} main_code
clean:
run: all
all:
- clang ${CCFLAGS} main.c -o main -dead_strip -mmacosx-version-min=10.7
+ ${CC} ${CCFLAGS} main.c -o main -dead_strip -mmacosx-version-min=10.7
otool -lv main | grep S_THREAD_LOCAL_VARIABLES | ${FAIL_IF_EMPTY}
otool -lv main | egrep 'S_THREAD_LOCAL_REGULAR|S_THREAD_LOCAL_ZEROFILL' | ${FAIL_IF_EMPTY}
${PASS_IFF_GOOD_MACHO} main
--- /dev/null
+##
+# Copyright (c) 2010 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 setting weak_import on symbols in a linkage unit works
+#
+
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} -o weak weak.c -undefined dynamic_lookup
+ ${DYLDINFO} -bind weak | grep _myweakfunc | grep "weak import" | ${FAIL_IF_EMPTY}
+ ${DYLDINFO} -lazy_bind weak | grep _myweakfunc | grep "weak import" | ${FAIL_IF_EMPTY}
+ ${PASS_IFF_GOOD_MACHO} weak
+
+clean:
+ rm -rf main
--- /dev/null
+#include <stdio.h>
+
+char *myweakfunc(void) __attribute__((weak)) ;
+
+int main(int argc, char **argv)
+{
+ if (myweakfunc)
+ printf ("found myweakfunc %s\n", myweakfunc());
+ else
+ printf("Weak func not found\n");
+ return 0;
+}
+
+++ /dev/null
-##
-# Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
-#
-# @APPLE_LICENSE_HEADER_START@
-#
-# This file contains Original Code and/or Modifications of Original Code
-# as defined in and that are subject to the Apple Public Source License
-# Version 2.0 (the 'License'). You may not use this file except in
-# compliance with the License. Please obtain a copy of the License at
-# http://www.opensource.apple.com/apsl/ and read it before using this
-# file.
-#
-# The Original Code and all software distributed under the License are
-# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
-# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
-# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
-# Please see the License for the specific language governing rights and
-# limitations under the License.
-#
-# @APPLE_LICENSE_HEADER_END@
-##
-TESTROOT = ../..
-include ${TESTROOT}/include/common.makefile
-
-#
-# Test the weak_import attribute works
-#
-
-
-run: all
-
-all:
- ${CC} ${CCFLAGS} -c foo.c -o foo.o
- ${FAIL_IF_BAD_OBJ} foo.o
-
- ${CC} ${CCFLAGS} -c foo1.c -o foo1.o
- ${FAIL_IF_BAD_OBJ} foo1.o
-
- ${PASS_IFF_ERROR} ${CC} ${CCFLAGS} -dynamiclib foo.o foo1.o -o libfoo.dylib
-
-clean:
- rm -rf foo.o foo1.o libfoo.dylib
-
-
-#2>/dev/null
\ No newline at end of file
+++ /dev/null
-Test the weak_import attribute works
+++ /dev/null
-
-
-#include "foo.h"
-
-void func1() {}
-void func2() {}
-void func3() {}
-void func4() {}
-
-
-int data1 = 0;
-int data2 = 0; // weak_import initialized
-int data3;
-int data4; // weak_import uninitialized
-int data5 = 0;
-int data6 = 0; // weak_import
-
+++ /dev/null
-
-
-extern void func1();
-extern void func2() __attribute__((weak_import));
-extern void func3();
-extern void func4() __attribute__((weak_import));
-
-extern int data1;
-extern int data2 __attribute__((weak_import));
-extern int data3;
-extern int data4 __attribute__((weak_import));
-extern int data5;
-extern int data6 __attribute__((weak_import));
-
-
-
+++ /dev/null
-#include "foo.h"
-
-int data4; // foo.c also has weak_import uninitialized
-
+++ /dev/null
-
-#include "foo.h"
-
-
-int* pdata5 = &data5;
-int* pdata6 = &data6;
-
-
-int main (void)
-{
- // make non-lazy reference to func3 and func4
- if ( &func3 == &func4 ) {
- // make lazy reference to func3 and func4
- func1();
- func2();
- }
-
- return data1 + data2 + data3 + data4;
-}
-