From ebf6f43431fe84b7b17822014a6d1f0169516e93 Mon Sep 17 00:00:00 2001 From: Apple Date: Wed, 1 Aug 2012 23:05:07 +0000 Subject: [PATCH] ld64-133.3.tar.gz --- ChangeLog | 1681 ----------------- compile_stubs | 51 + doc/design/bindings.png | Bin 0 -> 25309 bytes doc/design/hello.png | Bin 0 -> 11577 bytes doc/design/linker.html | 423 +++++ doc/man/man1/ld.1 | 2 +- ld64.xcodeproj/project.pbxproj | 199 +- src/abstraction/MachOFileAbstraction.hpp | 161 +- src/abstraction/MachOTrie.hpp | 6 +- src/create_configure | 45 + src/ld/HeaderAndLoadCommands.hpp | 130 +- src/ld/InputFiles.cpp | 596 ++++-- src/ld/InputFiles.h | 66 +- src/ld/LinkEdit.hpp | 193 +- src/ld/LinkEditClassic.hpp | 96 +- src/ld/Options.cpp | 651 +++++-- src/ld/Options.h | 73 +- src/ld/OutputFile.cpp | 427 +++-- src/ld/OutputFile.h | 9 + src/ld/Resolver.cpp | 178 +- src/ld/Resolver.h | 10 +- src/ld/Snapshot.cpp | 538 ++++++ src/ld/Snapshot.h | 153 ++ src/ld/SymbolTable.cpp | 482 ++--- src/ld/SymbolTable.h | 14 +- src/ld/code-sign-blobs/blob.cpp | 129 ++ src/ld/code-sign-blobs/blob.h | 217 +++ src/ld/code-sign-blobs/endian.h | 138 ++ src/ld/code-sign-blobs/memutils.h | 124 ++ src/ld/code-sign-blobs/superblob.h | 249 +++ src/ld/ld.cpp | 34 +- src/ld/ld.hpp | 120 +- src/ld/parsers/archive_file.cpp | 179 +- src/ld/parsers/archive_file.h | 2 +- src/ld/parsers/lto_file.cpp | 86 +- src/ld/parsers/lto_file.h | 5 +- src/ld/parsers/macho_dylib_file.cpp | 52 +- src/ld/parsers/macho_dylib_file.h | 2 +- src/ld/parsers/macho_relocatable_file.cpp | 125 +- src/ld/parsers/macho_relocatable_file.h | 4 +- src/ld/parsers/opaque_section_file.cpp | 8 +- src/ld/parsers/opaque_section_file.h | 2 +- src/ld/passes/branch_island.cpp | 72 +- src/ld/passes/branch_shim.cpp | 4 +- src/ld/passes/compact_unwind.cpp | 8 + src/ld/passes/dtrace_dof.cpp | 6 +- src/ld/passes/objc.cpp | 71 +- src/ld/passes/order.cpp | 5 +- src/ld/passes/stubs/stub_arm.hpp | 89 + src/ld/passes/stubs/stub_x86_64.hpp | 75 +- src/ld/passes/stubs/stubs.cpp | 43 +- src/other/ObjectDump.cpp | 52 +- src/other/dyldinfo.cpp | 166 +- src/other/machochecker.cpp | 57 +- src/other/rebase.cpp | 31 +- unit-tests/bin/make-recursive.pl | 153 +- unit-tests/include/common.makefile | 23 +- unit-tests/run-all-unit-tests | 2 +- .../test-cases/16-byte-alignment/Makefile | 4 +- unit-tests/test-cases/archive-r-ObjC/Makefile | 52 + unit-tests/test-cases/archive-r-ObjC/bar.c | 2 + unit-tests/test-cases/archive-r-ObjC/baz.m | 8 + unit-tests/test-cases/archive-r-ObjC/cat.m | 16 + unit-tests/test-cases/archive-r-ObjC/foo.m | 8 + unit-tests/test-cases/archive-r-ObjC/main.c | 31 + unit-tests/test-cases/branch-islands/Makefile | 4 + .../test-cases/branch-islands/atomic_space.s | 76 + unit-tests/test-cases/data-in-code/Makefile | 37 + unit-tests/test-cases/data-in-code/main.c | 49 + unit-tests/test-cases/data-in-code/test.s | 25 + .../test-cases/duplicate_symbols/Makefile | 52 + .../test-cases/duplicate_symbols/duplicates.c | 10 + .../duplicate_symbols/main_extern.c | 16 + .../duplicate_symbols/main_no_extern.c | 13 + .../{weak_import3 => dylib-main}/Makefile | 28 +- unit-tests/test-cases/dylib-main/foo.c | 1 + unit-tests/test-cases/dylib-main/main.c | 7 + unit-tests/test-cases/force-weak/Makefile | 38 + unit-tests/test-cases/force-weak/foo.c | 2 + unit-tests/test-cases/force-weak/test.c | 5 + unit-tests/test-cases/force-weak/weak.exp | 2 + .../test-cases/install-name-override/Makefile | 50 + .../test-cases/install-name-override/foo.c | 18 + .../test-cases/install-name-override/main.c | 9 + .../test-cases/llvm-integration/Makefile | 6 - .../lto-dead_strip-inline-asm/Makefile | 41 + .../lto-dead_strip-inline-asm/bar.c | 14 + .../test-cases/lto-dead_strip-unused/Makefile | 3 + .../test-cases/lto-preload-pie/Makefile | 11 +- unit-tests/test-cases/no-uuid/Makefile | 26 +- .../test-cases/objc-category-archive/Makefile | 9 +- .../test-cases/objc-category-archive/test2.m | 11 + .../objc-category-debug-notes/Makefile | 2 +- .../objc-category-optimize-load/Makefile | 3 +- .../objc-category-optimize/Makefile | 7 +- .../test-cases/objc-category-warning/Makefile | 4 +- unit-tests/test-cases/order_file/Makefile | 2 +- .../test-cases/pipelined-linking/Makefile | 74 + unit-tests/test-cases/pipelined-linking/bar.c | 4 + unit-tests/test-cases/pipelined-linking/cat.c | 4 + unit-tests/test-cases/pipelined-linking/foo.c | 10 + .../test-cases/static-executable-pie/Makefile | 50 + .../test-cases/static-executable-pie/bad.c | 9 + .../test-cases/static-executable-pie/test.c | 19 + .../tentative-and-archive-code/Makefile | 4 +- unit-tests/test-cases/tlv-dead_strip/Makefile | 2 +- .../test-cases/weak_import-undefined/Makefile | 40 + .../test-cases/weak_import-undefined/weak.c | 13 + .../test-cases/weak_import3/comment.txt | 1 - unit-tests/test-cases/weak_import3/foo.c | 17 - unit-tests/test-cases/weak_import3/foo.h | 16 - unit-tests/test-cases/weak_import3/foo1.c | 4 - unit-tests/test-cases/weak_import3/main.c | 20 - 113 files changed, 6482 insertions(+), 3024 deletions(-) delete mode 100644 ChangeLog create mode 100755 compile_stubs create mode 100644 doc/design/bindings.png create mode 100644 doc/design/hello.png create mode 100644 doc/design/linker.html create mode 100755 src/create_configure create mode 100644 src/ld/Snapshot.cpp create mode 100644 src/ld/Snapshot.h create mode 100644 src/ld/code-sign-blobs/blob.cpp create mode 100644 src/ld/code-sign-blobs/blob.h create mode 100644 src/ld/code-sign-blobs/endian.h create mode 100644 src/ld/code-sign-blobs/memutils.h create mode 100644 src/ld/code-sign-blobs/superblob.h create mode 100644 unit-tests/test-cases/archive-r-ObjC/Makefile create mode 100644 unit-tests/test-cases/archive-r-ObjC/bar.c create mode 100644 unit-tests/test-cases/archive-r-ObjC/baz.m create mode 100644 unit-tests/test-cases/archive-r-ObjC/cat.m create mode 100644 unit-tests/test-cases/archive-r-ObjC/foo.m create mode 100644 unit-tests/test-cases/archive-r-ObjC/main.c create mode 100644 unit-tests/test-cases/branch-islands/atomic_space.s create mode 100644 unit-tests/test-cases/data-in-code/Makefile create mode 100644 unit-tests/test-cases/data-in-code/main.c create mode 100644 unit-tests/test-cases/data-in-code/test.s create mode 100644 unit-tests/test-cases/duplicate_symbols/Makefile create mode 100644 unit-tests/test-cases/duplicate_symbols/duplicates.c create mode 100644 unit-tests/test-cases/duplicate_symbols/main_extern.c create mode 100644 unit-tests/test-cases/duplicate_symbols/main_no_extern.c rename unit-tests/test-cases/{weak_import3 => dylib-main}/Makefile (72%) create mode 100644 unit-tests/test-cases/dylib-main/foo.c create mode 100644 unit-tests/test-cases/dylib-main/main.c create mode 100644 unit-tests/test-cases/force-weak/Makefile create mode 100644 unit-tests/test-cases/force-weak/foo.c create mode 100644 unit-tests/test-cases/force-weak/test.c create mode 100644 unit-tests/test-cases/force-weak/weak.exp create mode 100644 unit-tests/test-cases/install-name-override/Makefile create mode 100644 unit-tests/test-cases/install-name-override/foo.c create mode 100644 unit-tests/test-cases/install-name-override/main.c create mode 100644 unit-tests/test-cases/lto-dead_strip-inline-asm/Makefile create mode 100644 unit-tests/test-cases/lto-dead_strip-inline-asm/bar.c create mode 100644 unit-tests/test-cases/objc-category-archive/test2.m create mode 100644 unit-tests/test-cases/pipelined-linking/Makefile create mode 100644 unit-tests/test-cases/pipelined-linking/bar.c create mode 100644 unit-tests/test-cases/pipelined-linking/cat.c create mode 100644 unit-tests/test-cases/pipelined-linking/foo.c create mode 100644 unit-tests/test-cases/static-executable-pie/Makefile create mode 100644 unit-tests/test-cases/static-executable-pie/bad.c create mode 100644 unit-tests/test-cases/static-executable-pie/test.c create mode 100644 unit-tests/test-cases/weak_import-undefined/Makefile create mode 100644 unit-tests/test-cases/weak_import-undefined/weak.c delete mode 100644 unit-tests/test-cases/weak_import3/comment.txt delete mode 100644 unit-tests/test-cases/weak_import3/foo.c delete mode 100644 unit-tests/test-cases/weak_import3/foo.h delete mode 100644 unit-tests/test-cases/weak_import3/foo1.c delete mode 100644 unit-tests/test-cases/weak_import3/main.c diff --git a/ChangeLog b/ChangeLog deleted file mode 100644 index 0514d7f..0000000 --- a/ChangeLog +++ /dev/null @@ -1,1681 +0,0 @@ - --------- tagged ld64-128.1 - -2011-09-13 Nick Kledzik - - Enable dyld to be put into the dyld shared cache - -2011-09-13 Nick Kledzik - - Fix "using ld_classic" warning for i386 kexts - -2011-09-13 Nick Kledzik - - LTO many have eliminated need for some undefines - --------- tagged ld64-128 - -2011-09-08 Nick Kledzik - - Rework 8924157 fix to sort all sections - -2011-09-07 Nick Kledzik - - ER: -current_version should allow 64-bit a.b.c.d.e tuple - -2011-09-06 Nick Kledzik - - -mdynamic-no-pic broke with movw of weak thumb symbol - -2011-09-01 Nick Kledzik - - 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 - - Add -fatal_warnings - -2011-08-30 Nick Kledzik - - Support .weak_def_can_be_hidden via LTO interface - -2011-08-29 Nick Kledzik - - improve performance of zerofill->data ordering - -2011-08-29 Nick Kledzik - - Implement -print_statistics - -2011-08-25 Nick Kledzik - - check for overlaps between pinned segments and regular segments - -2011-08-23 Nick Kledzik - - do got elimination more aggressively in static and preload mode - -2011-08-22 Nick Kledzik - - enable __dso_handle in -preload - -2011-08-22 Nick Kledzik - - fix section$end to sort to end of __mod_init_func section - -2011-08-18 Nick Kledzik - - __constructor section removed with -dead_strip - -2011-08-11 Nick Kledzik - - remove ld support for PowerPC - -2011-08-11 Nick Kledzik - - Fix spurious -segaddr alignment warning - --------- tagged ld64-127.3 - -2011-08-31 Nick Kledzik - - [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 - - suppress version load command for simulator builds - --------- tagged ld64-127.1 - -2011-07-26 Nick Kledzik - - Csu needs to support for armv7 variants - --------- tagged ld64-127 - -2011-07-26 Nick Kledzik - - crash with TLS + -dead_strip - -2011-07-20 Nick Kledzik - - ld64-123.2.1/ChangeLog contains internal train names and radar titles - -2011-07-17 Nick Kledzik - - ld crashes with an assertion failure when linking WebKit with LTO - -2011-07-14 Nick Kledzik - - Personalities missing when using compact unwind - -2011-07-13 Nick Kledzik - - force loaded archives not listed in LD_TRACE - -2011-07-05 Nick Kledzik - - spurious warning: Codegen prevents image from working in dyld shared cache - -2011-07-01 Nick Kledzik - - Fix -classic_linker option - --------- tagged ld64-126.5 - -2011-06-15 Nick Kledzik - - ld64-124.6: ld -r introduces duplicate symbols - -2011-06-15 Nick Kledzik - - loosen check for 32-bit absolute address out of range - --------- tagged ld64-126.3.1 - -2011-06-15 Nick Kledzik - - Update armv7 variants - --------- tagged ld64-126.2 - -2011-06-13 Nick Kledzik - - iOS ld -r loses dont-dead-strip attribute on __objc_nlclslist section - -2011-06-13 Nick Kledzik - - LC_ENCRYPTION_INFO size can be wrong - - --------- tagged ld64-126.1 - -2011-06-10 Nick Kledzik - - Add back support for armv7 variants - --------- tagged ld64-126 - -2011-06-09 Nick Kledzik - - -ObjC does not work for simulator - -2011-06-09 Nick Kledzik - - clang ld: bad codegen, pointer diff - Added test case: unit-tests/test-cases/weak-def-hidden-and-global - -2011-06-03 Nick Kledzik - - warning then assertion when libSystem.dylib is missing - -2011-06-02 Nick Kledzik - - ld crash with resolver functions - -2011-06-01 Nick Kledzik - - 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 - - Allow 8612550 (turn ordered zero fill symbols into zero data) to work not just for dyld - -2011-06-01 Nick Kledzik - - Remove trailing /. in dwarf source dirs to cannoicalize paths - -2011-06-01 Nick Kledzik - - Sort debug notes by output order instead of input order. - -2011-06-01 Nick Kledzik - - remove support for invoking ld_classic in iOS - -2011-06-01 Nick Kledzik - - Fix arm branch interworking in -r for armv6 - -2011-06-01 Nick Kledzik - - i386 regression with pointer-diff of same pointer - -2011-05-27 Nick Kledzik - - Canonicalize dwarf source file dirname to always end in / - -2011-05-27 Nick Kledzik - - support arm branch interworking in -r mode (use extern relocs) - -2011-05-27 Nick Kledzik - - Add -page_align_data_atoms option - -2011-05-24 Nick Kledzik - - align(16384) doesn't produce 16K aligned globals on ARMv7 - -2011-05-24 Nick Kledzik - - support arm shims in sections other than __text - -2011-05-23 Nick Kledzik - - ld64 should only install to the platform in iOS - -2011-05-19 Nick Kledzik - - Ld assertion with unusual section order - -2011-05-17 Nick Kledzik - - Linker is not automatically weak loading dylibs when all references are weak - --------- tagged ld64-125.3 - -2011-05-12 Nick Kledzik - - Fix missing split-seg-info for kindSetTargetImageOffset - -2011-05-12 Nick Kledzik - - Linker crashes with __gcc_except_tab data belonging to no FDE - -2011-05-11 Nick Kledzik - - Fix nop padding for arm code - -2011-05-05 Nick Kledzik - - x86_64: cmp of GOT slot loses weak_import bit - --------- tagged ld64-125.2 - -2011-05-02 Nick Kledzik - - Fix -flat_namespace issue with not all indirect dylibs being processed - -2011-04-29 Nick Kledzik - - Fix sign extention on i386 addends of extern vanilla relocs - -2011-04-29 Nick Kledzik - - Don't let -ObjC double load any archive members - -2011-04-29 Nick Kledzik - - better warning about unaligned ARM functions - --------- tagged ld64-125.1 - -2011-04-28 Nick Kledzik - - Fix sign extention on arm sect-diff relocs so as to not trip rangeCheckAbsolute32() - --------- tagged ld64-125 - -2011-04-24 Nick Kledzik - - the entry point should start out initially undefined - -2011-04-24 Nick Kledzik - - ld should never have a symbol in the non-lazy indirect symbol table with index 0 - -2011-04-24 Nick Kledzik - - ld adds undefined symbol from .exp file to kext bundle - -2011-04-24 Nick Kledzik - - Linker typo suggestions should ignore l- and L- symbols - -2011-04-24 Nick Kledzik - - -order_file_statistics warns about syms in multiple .o files even when names in order file are prefixed - -2011-04-23 Nick Kledzik - - 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 - - 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 - - x86_64 -- lossy relocation at static link time (push/mov $imm) - -2011-04-23 Nick Kledzik - - Add comment to error message when __ZTV symbols are undefined - -2011-04-23 Nick Kledzik - - obsolete -no_compact_linkedit - -2011-04-23 Nick Kledzik - - sect->sectname() passed to "%s" formats - -2011-04-14 Nick Kledzik - - linking a sub library of libSystem should not warn about common symbols - -2011-04-14 Nick Kledzik - - support movw/movt in static executables - -2011-04-12 Nick Kledzik - - Rework ARM subtype handling to be table driven - -2011-04-11 Nick Kledzik - - Error if -init or -e function not in image being linked - -2011-04-01 Nick Kledzik - - -static and -stack_addr don't work together - -2011-03-31 Nick Kledzik - - ld assert in LTO mode if libLTO suppresses a weak symbol it should have perserved - --------- tagged ld64-124.1 - -2011-03-30 Nick Kledzik - - log warning if ld_classic is invoked - -2011-03-30 Nick Kledzik - - Support "-arch arm -force_cpusubtype_ALL" to keep gcc building - --------- tagged ld64-124 - -2011-03-24 Nick Kledzik - - make libgcc_s and libSystem work for any link order - -2011-03-18 Nick Kledzik - - ld64 should only install to the platform in iOS trains - -2011-03-18 Nick Kledzik - - ld64 should build stand-alone and not need libunwind headers - -2011-03-18 Nick Kledzik - - add LC_VERSION_MIN_IPHONEOS to iOS targets, warn on mismatches - -2011-03-18 Nick Kledzik - - Make iOS simulator a real platform with command line versioning - -2011-03-15 Nick Kledzik - - static executables don't get function start information - -2011-03-15 Nick Kledzik - - allow_sub_type_mismatches linker flag broken - -2011-03-15 Nick Kledzik - - Add option to support merging zero fill sections - Add test case: unit-tests/test-cases/merge_zero_fill_sections - -2011-03-15 Nick Kledzik - - Improve error message about text-relocs caused by direct access to global weak symbols. - -2011-03-10 Nick Kledzik - - ld assert linking armv7 kext bundle on b/bl to external function - --------- tagged ld64-123.10 - -2011-03-03 Nick Kledzik - - linking x86_64 causes assert from changes in ld64-123.9 - --------- tagged ld64-123.9 - -2011-03-03 Nick Kledzik - - movw/movt don't work in dyld shared cache - -2011-03-03 Nick Kledzik - - classic linkedit does not match compact for non-lazy pointers - -2011-02-24 Nick Kledzik - - Support armv7 variants - --------- tagged ld64-123.8 - -2011-02-10 Nick Kledzik - - Switch arm32 kexts to MH_KEXT_BUNDLE - --------- tagged ld64-123.7 - -2011-02-10 Nick Kledzik - - Switch arm32 kexts to MH_KEXT_BUNDLE, if LD_KEXT_BUNDLE is set - -2011-01-28 Nick Kledzik - - spurious 'found branch-22 without store' warning - --------- tagged ld64-123.6 - -2011-01-26 Nick Kledzik - - crash with arm hi16/lo16 to external symbols - --------- tagged ld64-123.5 - -2011-01-24 Nick Kledzik - - dyld synthesized tail call stubs don't always work - --------- tagged ld64-123.4 - -2011-01-19 Nick Kledzik - - __text with >10 alignment should disable close-stub optimization - -2011-01-18 Nick Kledzik - - :upper16: / :lower16: not working when targeting thumb functions - --------- tagged ld64-123.3 - -2010-12-14 Nick Kledzik - - ld64 making shims when not necessary - -2010-12-14 Nick Kledzik - - Add work around for latest llvm-c/lto.h - --------- tagged ld64-123.2.1 - -2011-03-07 Nick Kledzik - - enable i386 ASLR - --------- tagged ld64-123.2 - -2010-12-10 Nick Kledzik - - Man page typo: "dysmutil" under object_path_lto - -2010-12-10 Nick Kledzik - - ld64 crashes when warning about re-exported symbol - --------- tagged ld64-123.1 - -2010-12-07 Nick Kledzik - - assertion if symbol from re-exported dylib is in -exported_symbols_list - --------- tagged ld64-123 - -2010-12-06 Nick Kledzik - - Change default search order and add -search_dylibs_first to restore old behavior - -2010-12-06 Nick Kledzik - - ld should consistently warn when resolvers are not exported - -2010-12-02 Nick Kledzik - - data segment has file offset 0 - --------- tagged ld64-122 - -2010-12-01 Nick Kledzik - - Add #define ARM_RELOC_HALF in case trying to build with old mach-o/arm/reloc.h header - -2010-11-30 Nick Kledzik - - Linker should synthesize interworking stubs for tail calls - added test case: unit-tests/test-cases/branch-interworking - -2010-11-30 Nick Kledzik - - 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 - - Add -dylibs option to dyldinfo tool - -2010-11-03 Nick Kledzik - - Need support for ARM/thumb upper/lower 16 bit relocation - -2010-11-03 Nick Kledzik - - Spelling typo in linker warning - -2010-11-02 Nick Kledzik - - Xcode 4: ld -r doesn't work on files compiled with llvm-gcc -flto - -2010-11-01 Nick Kledzik - - ld wrongly complaining about tlv relocs for i386 - -2010-11-01 Nick Kledzik - - Fix -why_live to list all references, not just first - -2010-11-01 Nick Kledzik - - iOS is missing dof sections for armv7 slice - --------- tagged ld64-120.3 - -2010-11-09 Nick Kledzik - - revert default search order - --------- tagged ld64-120.2 - -2010-11-09 Nick Kledzik - - ld -r corrupts multiple non-lazy sections - --------- tagged ld64-120.1 - -2010-10-25 Nick Kledzik - - When order file used on data, turn ordered zero fill symbols into zero data - --------- tagged ld64-120 - -2010-10-25 Nick Kledzik - - 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 - - Value of N_SO stabs should be address of first atom from translation unit - -2010-10-25 Nick Kledzik - - Always print arch name on undefined symbols error - -2010-10-25 Nick Kledzik - - Add ld64 version number to crash logs - -2010-10-22 Nick Kledzik - - -objc_abi_version 1 not supported - -2010-10-22 Nick Kledzik - - Add support to ld64 for N_FUN stabs when used for symbolic constants - -2010-10-22 Nick Kledzik - - Change default search order and add -search_dylibs_first to restore old behavior - -2010-10-22 Nick Kledzik - - -L flag should support a space between it and its argument - -2010-10-22 Nick Kledzik - - Hidden resolver functions don't work with DYLD_BIND_AT_LAUNCH - -2010-10-22 Nick Kledzik - - Support resolver functions for function pointer use from same linkage unit - -2010-10-19 Nick Kledzik - - section$start$ does not always point to start of built in section types - --------- tagged ld64-119.2 - -2010-10-18 Nick Kledzik - - make having an ObjC2 class symbol in an export list be a warning instead of an error - -2010-10-15 Nick Kledzik - - lazily produce (crt1.o missing) error - --------- tagged ld64-119.1 - -2010-10-05 Nick Kledzik - - ld -r can produce output with LC_DYLD_INFO load command - -2010-10-05 Nick Kledzik - - ld doesn't pass stabs debug info through to the final executable any longer - -2010-10-05 Nick Kledzik - - __UNIXSTACK placed incorrectly when -stack_addr < 0x4000000 - -2010-10-05 Nick Kledzik - - ld mishandles -lazy-l option - Updated test case: unit-tests/test-cases/lazy-dylib - -2010-10-04 Nick Kledzik - - -no_compact_unwind should suppress dwarf->CUE warnings - --------- tagged ld64-119 - -2010-10-01 Nick Kledzik - - use ld64 to link iBoot - -2010-10-01 Nick Kledzik - - crash when entrypoint is thumb - -2010-10-01 Nick Kledzik - - If -ios_version_min is used with -arch i386, assume simulator - -2010-10-01 Nick Kledzik - - crash with multiple re-exported dylibs with same install_name - -2010-09-28 Nick Kledzik - - Linker complains about resolver functions when architecture is inferred. - -2010-09-28 Nick Kledzik - - ARM subtype not set on LTO programs - -2010-09-28 Nick Kledzik - - 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 - - Support -dyld_env NAME=value - -2010-09-23 Nick Kledzik - - Previous BranchIsland code changes to make buildable with clang++ were bad. Fix. - -2010-09-23 Nick Kledzik - - ld64 objc category merging asserts on category from old framework - -2010-09-23 Nick Kledzik - - ASLR Sidebuild: Many Projects Fail checksyms_read_only_relocs Verifier - -2010-09-22 Nick Kledzik - - Fix DOF section name bug - -2010-09-22 Nick Kledzik - - Fixes to build with clang++ - -2010-09-21 Nick Kledzik - - In Resolver::fillInHelpersInInternalState(), dyld never needs stubs - -2010-09-21 Ivan Krstic - - ld: support non-executable heap Mach-O header flag - -2010-09-21 Nick Kledzik - - Xcode 4 linker fails with "address not in any section" - -2010-09-20 Nick Kledzik - - assertion failure reading i386 yasm .o (not using scattered reloc) - -2010-09-20 Nick Kledzik - - ld crashes when parsing dwarf and all code is not in __text - -2010-09-17 Nick Kledzik - - magic segment symbol names don't work with preload executables - Update test case: unit-tests/test-cases/segment-labels - -2010-09-17 Nick Kledzik - - OSO in DebugNotes for LTO should point to generated mach-o not, bitcode .o file - -2010-09-16 Nick Kledzik - - FUN in debug map not rebased - Update test case: unit-tests/test-cases/rebase-basic - -2010-09-16 Nick Kledzik - - Relocation failure with i386 32-bit diff to stub - -2010-09-16 Nick Kledzik - - assert when targeting 10.5 and crt1.o/dylib1.o is not supplied - --------- tagged ld64-118.1 - -2010-09-15 Nick Kledzik - - Fix missing rebase commands that broke perl - -2010-09-15 Nick Kledzik - - assert when .objc_class_name_* symbol missing - Add test case: unit-tests/test-cases/archive-ObjC-unexported - -2010-09-13 Nick Kledzik - - linker does not honor $ld$hide for umbrella libraries - Added test case: unit-tests/test-cases/symbol-hiding-umbrella - -2010-09-09 Nick Kledzik - - LC_UUID uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats - -2010-09-09 Nick Kledzik - - support -bind_at_load - -2010-09-07 Nick Kledzik - - 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 - - -preload should not have LINKEDIT segment - Added test case: unit-tests/test-cases/efi-basic - -2010-09-02 Nick Kledzik - - trivial Objective-C app fails when using libLTO - Added test case: unit-tests/test-cases/lto-objc-image-info - -2010-09-02 Nick Kledzik - - 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 - - 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 - - Warn if unaligned ARM code is detected - -2010-09-01 Nick Kledzik - - Mach-O linked by current linker don't load in VIrtualBox any more - -2010-09-01 Nick Kledzik - - Linker should pick default visibility instead of warning about conflicts - Updated test case: unit-tests/test-cases/visibility-warning - -2010-09-01 Nick Kledzik - - Enable new load commands - -2010-09-01 Nick Kledzik - - Do not pass -demangle to ld_classic - -2010-09-01 Nick Kledzik - - iOS 4.3 armv7 should be PIE by default - better error message for direct access to external globals when not linking read_only_relocs - linker does not error on direct (static) data references to a dylib symbol - --------- tagged ld64-117.11 - -2010-09-03 Nick Kledzik - - mask thumb bit off non lazy pointers content when parsing arm .o files - --------- tagged ld64-117.10 - -2010-08-26 Nick Kledzik - - 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 - - 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 - - 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 - - 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 - - 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 - - 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 - - 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 - - Fix resolver functions to survive ld -r. - Warn if resolver functions are made non-external. - -2010-08-17 Nick Kledzik - - Make it an error for resolver functions to be used in anything but a dylib. - --------- tagged ld64-117.3 - -2010-08-17 Nick Kledzik - - Fix thumb resolver functions - Enable updward dylibs and symbol re-exports for iOS 4.2 - -2010-08-16 Nick Kledzik - - Latest ld no longer supports EFI -preload - Rearrange LINKEDIT chunks in -preload mode - --------- tagged ld64-117.2 - -2010-08-14 Nick Kledzik - - Latest ld no longer supports EFI -preload - Add LC_UNIXTHREAD to -preload - -2010-08-14 Nick Kledzik - - SWB: ld64-117.1 on 8F54: Assertion failed: (categoryAtom->size() == Category::size()) - gcc-4.0 uses 'L' labels on categories. This merges them onto previous data and disable category optimzation - -2010-08-14 Nick Kledzik - - SWB: ld64-117.1 on 8F54: bad category optimization - Disable category optimization for i386 and arm until further testing - -2010-08-14 Nick Kledzik - - 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 - - Latest ld no longer supports EFI -preload - --------- tagged ld64-117.1 - -2010-08-11 Nick Kledzik - - Make missing exported symbols a warning to help adoption of new linker - -2010-08-11 Nick Kledzik - - Add ExternalRelocationsAtom<>::pointerReloc() to more easily support kext bundles - -2010-08-09 Nick Kledzik - - SWB: ld64-116.2 fix branch to label-4 - -2010-08-09 Nick Kledzik - - Error with empty non_lazy_symbol_pointers section - -2010-08-06 Nick Kledzik - - Add command line options to control symbol weak-def-bit on exported symbols - --------- tagged ld64-117 - -2010-07-28 Nick Kledzik - - split seg info wrong for x86_64 stub helpers - -2010-07-26 Nick Kledzik - - __nlcatlist categories should not be optimized - -2010-07-23 Nick Kledzik - - ld64 assertion on object file - -2010-07-21 Nick Kledzik - - Reorder sections to reduce page faults in object files - -2010-06-30 Nick Kledzik - - Support resolver functions in iOS dylibs - --------- tagged ld64-116.2 - -2010-06-30 Nick Kledzik - - C programs get objc GCness from dylibs - Update: unit-tests/test-cases/objc-gc-checks - --------- tagged ld64-116.1 - -2010-06-22 Nick Kledzik - - address range check should not apply to preload executables - -2010-06-22 Nick Kledzik - - Warn instead of error when CPU_SUBTYPE_ARM_ALL .o files used. - -2010-06-22 Nick Kledzik - - Fix assert in objc category optimzation. Metaclass also has copy of propery list to update. - -2010-06-22 Nick Kledzik - - Fix crash in -r mode with -alias. - --------- tagged ld64-116 - -2010-06-21 Nick Kledzik - - Add support for -ios_version_min as an alias for -iphoneos_version_min - -2010-06-21 Nick Kledzik - - 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 - - i386 TLV PIC reloc content is negated - -2010-06-15 Nick Kledzik - - Added better error messages and asserts for bad thread local object files - -2010-06-09 Nick Kledzik - - 'rebase' makes timestamps invalid/unreadable for GDB - -2010-06-09 Nick Kledzik - - executable has no debug symbols when compiled with LTO - Added test case: unit-tests/test-cases/lto-object_path - -2010-06-09 Nick Kledzik - - stop promoting hidden referenced dynamically symbols to global - Updated test case: unit-tests/test-cases/main-stripped - -2010-06-04 Nick Kledzik - - ER: individual symbol re-exports - Added test case: unit-tests/test-cases/re-export-symbo - -2010-06-03 Nick Kledzik - - 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 - - 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 - - provide better undefined symbol error message - -2010-05-28 Nick Kledzik - - * ld should also merge file attributes from lazy loaded archives - * Move attribute gathering from InputFiles to Resolver - -2010-05-28 Nick Kledzik - - * 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 - - * strip of .o files removes __objc_imageinfo section - * Added test case: unit-tests/test-cases/dwarf-strip-objc - -2010-05-25 Nick Kledzik - - * crash when parsing local vanilla reloc to weak def - --------- tagged ld64-115.2 - -2010-05-21 Nick Kledzik - - * switch back to using ld_classic for -static arm code - - -2010-05-21 Nick Kledzik - - * warn instead of error when seg1addr is out of range for ARM - - -2010-05-21 Nick Kledzik - - * fix -undefined dynamic_lookup -nodefaults to not error about missing dyld_stub_binder - --------- tagged ld64-115.1 - -2010-05-19 Nick Kledzik - - * Fix trie nodes for resolver functions to have second address be stub not helper - -2010-05-19 Nick Kledzik - - * work around for old checksyms tools - * Make i386 stub section named "__symbol_stub" instead of "__stubs" - -2010-05-10 Nick Kledzik - - * linking with LTO prints "/tmp/lto.o" - --------- tagged ld64-115 - -2010-05-06 Nick Kledzik - - * linker loses x86_64 addend to 'L' symbols - * properly handle addend to 'L' symbols that are ignored - -2010-05-05 Nick Kledzik - - * rework min OS version parsing to enable the linker to handle unknown OS versions - - -2010-05-05 Nick Kledzik - - * 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 - - * implement optional demangling in linker - * Add option: -demangle - * Add test case: unit-tests/test-cases/demangle - - -2010-05-03 Nick Kledzik - - * 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 - - * -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 - - * 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 - - * 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 - - * 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 - - * The documentation for the -allowable_client option doesn't say enough about it - -2010-04-26 Nick Kledzik - - * back out LD_NO_PIE - -2010-04-22 Nick Kledzik - - * 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 - - * Crash with messed up BNSYM - - -2010-04-07 Nick Kledzik - - * Fix crash with blank dylib stubs - --------- tagged ld64-114.11 - -2010-04-07 Nick Kledzik - - * for ppc, add split-seg info for TEXT pointers to DATA - - -2010-04-07 Nick Kledzik - - * Cannot build ppc64 target with ObjC code - - -2010-04-01 Nick Kledzik - - * let .exp files override auto-hide so that they can be exported if needed - - -2010-04-01 Nick Kledzik - - * 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 - - * 'l' symbols not being automatically removed - - -2010-03-31 Nick Kledzik - - * 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 - - * assert with .o file with two LSDA sections - - --------- tagged ld64-114.9 - -2010-03-30 Nick Kledzik - - * 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 - - * 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 - - * __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 - - * Support LD_NO_PIE again - -2010-03-25 Nick Kledzik - - * Rosetta crashes on launch in 11A133a - * Fix -segaddr __TEXT to cause other floating segments to be contiguous with TEXT - -2010-03-24 Nick Kledzik - - * Page Zero segment seems to be getting dead stripped - -2010-03-24 Nick Kledzik - - * kernel sdt dtrace probes not visible - --------- tagged ld64-114.6 - -2010-03-23 Nick Kledzik - - * 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 - - * missing __objc_imageinfo section - * Real fix will be in 7780438. For now have Resolver also accumulate objc constraint info - - -2010-03-19 Nick Kledzik - - * ld64-114 does not error on missing exported symbols with -dead_strip - --------- tagged ld64-114.4 - -2010-03-16 Nick Kledzik - - * dyld missing LC_ID_DYLINKER - --------- tagged ld64-114.3 - -2010-03-15 Nick Kledzik - - * 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 - - * ld64-114 generates x86_64 kext external call sites with incorrect addend - --------- tagged ld64-114.1 - -2010-03-12 Nick Kledzik - - * Fix dyldinfo tool to correct show ordinal info for classic linkedit - -2010-03-12 Nick Kledzik - - ld64-114 is causing read_only_reloc verification errors for ppc - * Update machochecker to check this. - --------- tagged ld64-114 - -2010-03-11 Nick Kledzik - - * 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 - - * Allow CPU_SUBTYPE_ARM_ALL .o files to be linked into any arm arch linkage - -2010-03-11 Nick Kledzik - - 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 - - * prevent possible crash in warning about can't export hidden symbols - -2010-03-10 Nick Kledzik - - * make sure split-info data is zero terminated - --------- tagged ld64-112 - -2010-03-09 Nick Kledzik - - * 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 - - * Add support for "-arch arm -force_cpusubtype_ALL" to keep gcc building - -2010-03-02 Nick Kledzik - - * Add some checking to the use of upward dylibs - --------- tagged ld64-110 - -2010-03-01 Nick Kledzik - - * Don't coalesce cstrings across segments - -2010-03-01 Nick Kledzik - - * 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 - - * Make sure building dyld results in a thread load command - --------- tagged ld64-109 - -2010-02-26 Nick Kledzik - - * 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 - - * Support pointer-diffs to zero sized atom in zero sized section - -2010-02-25 Nick Kledzik - - * Add support for -r mode with ppc64 - -2010-02-25 Nick Kledzik - - * 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 - - * Handle old ppc .o files that have stubs to static functions - -2010-02-25 Nick Kledzik - - * Add basic ppc64 support - -2010-02-24 Nick Kledzik - - * Range check TOC entries in archives - - -2010-02-23 Nick Kledzik - - * Fix spurious dylib re-export warnings that are just regular linkage cycles - - -2010-02-23 Nick Kledzik - - * re-partition bits in mach_o::relocatable::Atom ivars to allow more functions per file - - -2010-02-22 Nick Kledzik - - * re-partition bits in mach_o::relocatable::Atom ivars to allow more fixups per function - - -2010-02-22 Nick Kledzik - - * Handle re-exported dylibs that are re-exported again - * Added test case: unit-tests/test-cases/re-export-layers - - -2010-02-22 Nick Kledzik - - * Properly handle X86_64_RELOC_SUBTRACTOR with non-external target symbol - - --------- tagged ld64-108 - -2010-02-17 Nick Kledzik - - * ER: Support upward dylib dependencies - * Add test case: unit-tests/test-cases/dylib-upward - - -2010-02-17 Nick Kledzik - - * ld(1) man page typo - * Linker (ld) man page typo: "unredable" in -pagezero_size option description - * Typo in ld(1) man page, "-x" option - * man ld: Change "if" -> "is" - * DOC: ld(1) mentions -dynamiclib when it means -dylib - - -2010-02-17 Nick Kledzik - - * Wordsmith ld warning about missing directories - - -2010-02-17 Nick Kledzik - - * 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 - - * Fix bugs with -preload - - -2010-02-16 Nick Kledzik - - * Fix dylib re-export cylce detection - - -2010-02-16 Nick Kledzik - - * -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 - - * ld glibly removes /dev/null - - -2010-02-15 Nick Kledzik - - * 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 - - * 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 - - * ER: Need a way to detect weak exports in dev tools - * implement -warn_weak_exports - -2010-02-15 Nick Kledzik - - * Add support for LD_DYLD_PATH - - -2010-02-15 Nick Kledzik - - * 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 - - * Assertion failed: when class is translation unit scoped - * added test case unit-tests/test-cases/objc-visibility - - -2010-02-12 Nick Kledzik - - * crash with missing crt? - - -2010-02-12 Nick Kledzik - - * Suppress indirect symbol table in static executables - - -2010-02-12 Nick Kledzik - - * Rework CIE parsing to work with icc generated code - - -2010-02-11 Nick Kledzik - - * Fix creation of debug notes - * Tweak unit-tests/test-cases/dwarf-debug-notes to match llvm symbol layout - - -2010-02-11 Nick Kledzik - - * 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 - - * Use symbolic constants for bit field sizes - - -2010-02-10 Nick Kledzik - - * Handle out of order sections in .o files - - --------- tagged ld64-104 - -2010-02-10 Nick Kledzik - - * 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 - - * Better warning messages about mismatched architectures - - -2010-02-10 Nick Kledzik - - * Gracefully ignore if there are >8000 line info per function in debug info - - -2010-02-10 Nick Kledzik - - * Properly handle when regular definition is weak_imported - * Add unit-tests/test-cases/weak_import-local - - -2010-02-10 Nick Kledzik - - * Don't try to coalesce zero length cstrings in mach-o parser. - - -2010-02-10 Nick Kledzik - - * Add work around for llvm using L labels for backing string of CFString with -fwritable-strings - - -2010-02-09 Nick Kledzik - - * 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 - - * Fix file offset computation with large zero-fill sections - - -2010-02-09 Nick Kledzik - - * 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 - - * Temporarily change assert() to call exit(1) instead of abort() - - -2010-02-04 Nick Kledzik - - * Fix another case in -r mode where the vmsize was less that filesize - - -2010-02-04 Nick Kledzik - - * Fix assert when generating GSYM stab debug notes - - -2010-02-04 Nick Kledzik - - * Add SRCROOT to crash logs - - -2010-02-04 Nick Kledzik - - * Remove architectureName() from InputFiles - - --------- tagged ld64-102 - -2010-02-03 Nick Kledzik - - * Add follow-on reference from symbol text atom to non-symboled text atom - - --------- tagged ld64-101 - -2010-01-29 Nick Kledzik - - * fix -alias symbols to be global by default - - --------- tagged ld64-100 - -2010-01-28 Nick Kledzik - - * Merge new/refactored linker to trunk - diff --git a/compile_stubs b/compile_stubs new file mode 100755 index 0000000..e142e4d --- /dev/null +++ b/compile_stubs @@ -0,0 +1,51 @@ +#!/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 diff --git a/doc/design/bindings.png b/doc/design/bindings.png new file mode 100644 index 0000000000000000000000000000000000000000..7a563888edbf49ee4c6304cadf8bd98b789fd739 GIT binary patch literal 25309 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 literal 0 HcmV?d00001 diff --git a/doc/design/hello.png b/doc/design/hello.png new file mode 100644 index 0000000000000000000000000000000000000000..9140e1691c246c3cc47aca957863d061f7224ba8 GIT binary patch literal 11577 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 Mts+$=VI1;50NeYjJpcdz literal 0 HcmV?d00001 diff --git a/doc/design/linker.html b/doc/design/linker.html new file mode 100644 index 0000000..0e39f2f --- /dev/null +++ b/doc/design/linker.html @@ -0,0 +1,423 @@ + + + Linker + + + + +

+ Inside the Linker +

+
+ + +

+ Introduction +

+ +

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. +

+ +

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.

+ +

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.

+
+

+ Atom model +

+ +

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. +

+ +

A simple "hello world" object file would be modeled like this:

+hello world graphic +

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.

+ +
+

+ File model +

+ +

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.

+

Object File +

+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. + +

Static Library (Archive) +

+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. + +

Dynamic Library (Shared Object) +

+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.

+ +
+

+ Linking Steps +

+

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.

+

The overall steps in linking are:

+

    +
  1. Command line processing
  2. +
  3. Parsing input files
  4. +
  5. Resolving
  6. +
  7. Passes/Optimizations
  8. +
  9. Generate output file
  10. +
+ +

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.

+ +

Resolving +

+

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.

+ +

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.

+ +

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:

+binding types graphic + +

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. +

+ +

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. +

+ +

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. +

+ +

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. +

+ +

Passes +

+

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:

+
    +
  • Objective-C optimizations (Apple)
  • +
  • stub (PLT) generation
  • +
  • GOT instantiation
  • +
  • TLV instantiation (Apple)
  • +
  • order_file optimization
  • +
  • branch island generation
  • +
  • branch shim generation
  • +
  • dtrace probe processing (Apple)
  • +
  • compact unwind encoding (Apple)
  • +
+

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).

+ +

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.

+ +

Generate Output File +

+

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. +

+ + +

+ Future Directions +

+ +

Sections +

+

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? +

+

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. +

+ +

Mach-o Object File Format +

+

+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. +

+ +

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.

+ +

New Object File Model +

+

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. +

+

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.

+

+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. +

+

+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. +

+
+---
+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 ]
+
+...
+
+ +

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. +

+ +

Debug Info +

+

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.

+ +

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.

+ +

Extending Atom attributes to ELF and XCOFF +

+

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. +

+
    +
  • Name
  • +
  • Size
  • +
  • Section (I'd like to get rid of this)
  • +
  • ContentType (currently some of this comes from section)
  • +
      +
    • code
    • +
    • stub
    • +
    • data
    • +
    • zeroFill
    • +
    • initializerPointer
    • +
    • objc1Class
    • +
    • objc2Class
    • +
    • objcClassPointer
    • +
    • objc2CategoryList
    • +
    • non-lazy-pointer
    • +
    • lazy-pointer
    • +
    • constant
    • +
    • literal4
    • +
    • literal8
    • +
    • literal16
    • +
    • cstring
    • +
    • cstringPointer
    • +
    • utf16string
    • +
    • CFString
    • +
    • CFI
    • +
    • LSDA
    • +
    + +
  • Scope +
      +
    • translationUnit (static functions)
    • +
    • linkageUnit (visibility hidden)
    • +
    • global
    • +
    +
  • +
  • DefinitionKind +
      +
    • regular
    • +
    • tentative (ANSI C feature)
    • +
    • absolute (assembly code feature)
    • +
    • proxy (stand-in for dynamic library symbol)
    • +
    +
  • +
  • Combine +
      +
    • never
    • +
    • byName (weak symbols)
    • +
    • byContent (simple constants)
    • +
    • byContentAndReferences (complex constants)
    • +
    +
  • +
  • SymbolTableStatus +
      +
    • In
    • +
    • notIn (anonymous)
    • +
    • inAsAbsolute (assembly code feature)
    • +
    • inAndNeverStrip (tell strip tool to leave)
    • +
    • inWithRandomName (mach-o .o feature)
    • +
    +
  • Alignment +
      +
    • powerOfTwo
    • +
    • modulus
    • +
    +
  • NeverDeadStrip (boolean)
  • +
  • IsThumb (ARM specific)
  • +
+

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. +

+ + + + diff --git a/doc/man/man1/ld.1 b/doc/man/man1/ld.1 index dc2a501..a0850f6 100644 --- a/doc/man/man1/ld.1 +++ b/doc/man/man1/ld.1 @@ -743,7 +743,7 @@ Optimize stabs debug symbols to remove duplicates. This is the default. This o 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. diff --git a/ld64.xcodeproj/project.pbxproj b/ld64.xcodeproj/project.pbxproj index 65f20a7..11e716e 100644 --- a/ld64.xcodeproj/project.pbxproj +++ b/ld64.xcodeproj/project.pbxproj @@ -43,6 +43,7 @@ /* 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 */; }; @@ -76,6 +77,7 @@ 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 */; }; @@ -239,7 +241,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - C02A29DE0953B26E001FB8C1 /* ChangeLog */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = ChangeLog; sourceTree = ""; }; + B3B672411406D42800A376BB /* Snapshot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Snapshot.cpp; path = src/ld/Snapshot.cpp; sourceTree = ""; }; + B3B672441406D44300A376BB /* Snapshot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Snapshot.h; path = src/ld/Snapshot.h; sourceTree = ""; }; + B3C7A09914295B9C005FC714 /* compile_stubs */ = {isa = PBXFileReference; lastKnownFileType = text.script.csh; path = compile_stubs; sourceTree = ""; }; 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 = ""; }; F92D9C2710657AAB00FF369B /* stub_x86_64_classic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_x86_64_classic.hpp; sourceTree = ""; }; @@ -311,6 +315,12 @@ F9C0D48A06DD1E1B001C7193 /* Options.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Options.cpp; path = src/ld/Options.cpp; sourceTree = ""; }; F9C0D48B06DD1E1B001C7193 /* Options.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Options.h; path = src/ld/Options.h; sourceTree = ""; }; F9C12E9F0ED63DB1005BC69D /* dyldinfo.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = dyldinfo.1; path = doc/man/man1/dyldinfo.1; sourceTree = ""; }; + F9CC24141461FB4300A92174 /* blob.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = blob.cpp; sourceTree = ""; }; + F9CC24151461FB4300A92174 /* blob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = blob.h; sourceTree = ""; }; + F9CC24161461FB4300A92174 /* endian.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = endian.h; sourceTree = ""; }; + F9CC24171461FB4300A92174 /* memutils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = memutils.h; sourceTree = ""; }; + F9CC24181461FB4300A92174 /* superblob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = superblob.h; sourceTree = ""; }; + F9CCF761144CE1AD007CB524 /* create_configure */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = create_configure; path = src/create_configure; sourceTree = ""; }; F9EA72CB097454A6008B4F1D /* machocheck */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = machocheck; sourceTree = BUILT_PRODUCTS_DIR; }; F9EA72D4097454FF008B4F1D /* machochecker.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = machochecker.cpp; path = src/other/machochecker.cpp; sourceTree = ""; }; F9EA7582097882F3008B4F1D /* debugline.c */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 2; lastKnownFileType = sourcecode.c.c; name = debugline.c; path = src/ld/debugline.c; sourceTree = ""; tabWidth = 8; usesTabs = 1; }; @@ -368,7 +378,6 @@ F9023C2C06D5A227001BBF46 = { isa = PBXGroup; children = ( - C02A29DE0953B26E001FB8C1 /* ChangeLog */, F9B813A80EC27B6300F94C13 /* abstraction */, F9B813AD0EC27B8500F94C13 /* ld */, F9B813B00EC27B9E00F94C13 /* other */, @@ -492,11 +501,14 @@ 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 = ""; @@ -504,6 +516,8 @@ F9B813B00EC27B9E00F94C13 /* other */ = { isa = PBXGroup; children = ( + B3C7A09914295B9C005FC714 /* compile_stubs */, + F9CCF761144CE1AD007CB524 /* create_configure */, F9EA72D4097454FF008B4F1D /* machochecker.cpp */, F971EED706D5AD240041D381 /* ObjectDump.cpp */, F9BA515B0ECE58AA00D1D62E /* dyldinfo.cpp */, @@ -515,6 +529,19 @@ name = other; sourceTree = ""; }; + 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 = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -522,7 +549,8 @@ 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 */, @@ -542,6 +570,7 @@ isa = PBXNativeTarget; buildConfigurationList = F933D91F09291AC90083EAC8 /* Build configuration list for PBXNativeTarget "ObjectDump" */; buildPhases = ( + F9CCF773144CE304007CB524 /* make configure.h */, F971EED006D5ACF60041D381 /* Sources */, F971EED106D5ACF60041D381 /* Frameworks */, ); @@ -558,6 +587,7 @@ isa = PBXNativeTarget; buildConfigurationList = F9A3DDCF0ED762C100C590B9 /* Build configuration list for PBXNativeTarget "libprunetrie" */; buildPhases = ( + F9CCF781144CE3DF007CB524 /* make configure.h */, F9A3DDC70ED762B700C590B9 /* Sources */, F9A3DE140ED76D7700C590B9 /* CopyFiles */, ); @@ -574,6 +604,7 @@ isa = PBXNativeTarget; buildConfigurationList = F9B670050DDA176100E6D0DA /* Build configuration list for PBXNativeTarget "unwinddump" */; buildPhases = ( + F9CCF77C144CE36B007CB524 /* make configure.h */, F9B670020DDA176100E6D0DA /* Sources */, F9B670040DDA176100E6D0DA /* Frameworks */, F9B813870EC2659600F94C13 /* install man page */, @@ -591,6 +622,7 @@ isa = PBXNativeTarget; buildConfigurationList = F9BA516D0ECE58DA00D1D62E /* Build configuration list for PBXNativeTarget "dyldinfo" */; buildPhases = ( + F9CCF76B144CE2AD007CB524 /* make configure.h */, F9BA515E0ECE58BE00D1D62E /* Sources */, F9BA515F0ECE58BE00D1D62E /* Frameworks */, F9C12EA50ED63E05005BC69D /* install man page */, @@ -608,6 +640,7 @@ isa = PBXNativeTarget; buildConfigurationList = F9EA72CF097454D5008B4F1D /* Build configuration list for PBXNativeTarget "machocheck" */; buildPhases = ( + F9CCF76F144CE2D6007CB524 /* make configure.h */, F9EA72C8097454A6008B4F1D /* Sources */, F9EA72C9097454A6008B4F1D /* Frameworks */, ); @@ -624,6 +657,7 @@ isa = PBXNativeTarget; buildConfigurationList = F9EC77F00A2F8616002A3E39 /* Build configuration list for PBXNativeTarget "rebase" */; buildPhases = ( + F9CCF765144CE244007CB524 /* make configure.h */, F9EC77EB0A2F85F6002A3E39 /* Sources */, F9EC77EC0A2F85F6002A3E39 /* Frameworks */, F9B1A25E0A3A44CB00DA8FAB /* install man page */, @@ -674,6 +708,23 @@ /* 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; @@ -685,7 +736,7 @@ ); 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 */ = { @@ -700,23 +751,119 @@ ); 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 */ @@ -749,6 +896,8 @@ 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; }; @@ -867,8 +1016,6 @@ 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; @@ -892,7 +1039,7 @@ 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; @@ -903,6 +1050,7 @@ 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", ); @@ -910,7 +1058,10 @@ 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 = ""; @@ -922,8 +1073,6 @@ 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; @@ -962,13 +1111,14 @@ 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; @@ -985,8 +1135,6 @@ 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; @@ -1006,7 +1154,7 @@ "$(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; @@ -1033,7 +1181,7 @@ "$(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; @@ -1048,6 +1196,8 @@ 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; }; @@ -1056,6 +1206,8 @@ 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; }; @@ -1082,6 +1234,8 @@ 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; }; @@ -1101,8 +1255,6 @@ 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; @@ -1140,13 +1292,14 @@ 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; @@ -1213,7 +1366,7 @@ "$(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; diff --git a/src/abstraction/MachOFileAbstraction.hpp b/src/abstraction/MachOFileAbstraction.hpp index f4d136f..80a4e81 100644 --- a/src/abstraction/MachOFileAbstraction.hpp +++ b/src/abstraction/MachOFileAbstraction.hpp @@ -31,13 +31,13 @@ #include #include #include -#include #include #include #include #include "FileAbstraction.hpp" +#include "configure.h" // stuff that will eventually go away once newer cctools headers are widespread #ifndef LC_LOAD_UPWARD_DYLIB @@ -54,9 +54,6 @@ #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 @@ -206,9 +203,18 @@ #define LC_DYLD_ENVIRONMENT 0x27 #endif -// hack until newer 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) @@ -217,24 +223,77 @@ #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 +#endif + +// hack until newer everywhere +#define ARM_RELOC_HALF 8 +#define ARM_RELOC_HALF_SECTDIFF 9 + // @@ -1278,7 +1337,7 @@ public: 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 @@ -1329,6 +1388,70 @@ private: }; +// +// mach-o source version load command +// +template +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 +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 +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__ diff --git a/src/abstraction/MachOTrie.hpp b/src/abstraction/MachOTrie.hpp index 7b30cad..76f575a 100644 --- a/src/abstraction/MachOTrie.hpp +++ b/src/abstraction/MachOTrie.hpp @@ -360,8 +360,10 @@ static inline void processExportNode(const uint8_t* const start, const uint8_t* ++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); } } diff --git a/src/create_configure b/src/create_configure new file mode 100755 index 0000000..5375911 --- /dev/null +++ b/src/create_configure @@ -0,0 +1,45 @@ +#!/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 + + +# 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 + + + diff --git a/src/ld/HeaderAndLoadCommands.hpp b/src/ld/HeaderAndLoadCommands.hpp index 903a2bf..e425fd4 100644 --- a/src/ld/HeaderAndLoadCommands.hpp +++ b/src/ld/HeaderAndLoadCommands.hpp @@ -1,6 +1,6 @@ /* -*- 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@ * @@ -96,7 +96,9 @@ private: 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; @@ -106,6 +108,8 @@ private: 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; @@ -120,6 +124,7 @@ private: bool _hasDyldLoadCommand; bool _hasDylibIDLoadCommand; bool _hasThreadLoadCommand; + bool _hasEntryPointLoadCommand; bool _hasEncryptionLoadCommand; bool _hasSplitSegInfoLoadCommand; bool _hasRoutinesLoadCommand; @@ -130,6 +135,9 @@ private: bool _hasSubFrameworkLoadCommand; bool _hasVersionLoadCommand; bool _hasFunctionStartsLoadCommand; + bool _hasDataInCodeLoadCommand; + bool _hasSourceVersionLoadCommand; + bool _hasDependentDRInfo; uint32_t _dylibLoadCommmandsCount; uint32_t _allowableClientLoadCommmandsCount; uint32_t _dyldEnvironExrasCount; @@ -161,9 +169,8 @@ HeaderAndLoadCommandsAtom::HeaderAndLoadCommandsAtom(const Options& opts, ld: _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); @@ -189,7 +196,7 @@ HeaderAndLoadCommandsAtom::HeaderAndLoadCommandsAtom(const Options& opts, ld: } break; case Options::kStaticExecutable: - _hasDynamicSymbolTableLoadCommand = false; + _hasDynamicSymbolTableLoadCommand = opts.positionIndependentExecutable(); break; case Options::kPreload: _hasDynamicSymbolTableLoadCommand = opts.positionIndependentExecutable(); @@ -199,6 +206,9 @@ HeaderAndLoadCommandsAtom::HeaderAndLoadCommandsAtom(const Options& opts, ld: _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(); @@ -328,9 +338,15 @@ uint64_t HeaderAndLoadCommandsAtom::size() const if ( _hasVersionLoadCommand ) sz += sizeof(macho_version_min_command

); + + if ( _hasSourceVersionLoadCommand ) + sz += sizeof(macho_source_version_command

); if ( _hasThreadLoadCommand ) sz += this->threadLoadCommandSize(); + + if ( _hasEntryPointLoadCommand ) + sz += sizeof(macho_entry_point_command

); if ( _hasEncryptionLoadCommand ) sz += sizeof(macho_encryption_info_command

); @@ -377,6 +393,12 @@ uint64_t HeaderAndLoadCommandsAtom::size() const if ( _hasFunctionStartsLoadCommand ) sz += sizeof(macho_linkedit_data_command

); + if ( _hasDataInCodeLoadCommand ) + sz += sizeof(macho_linkedit_data_command

); + + if ( _hasDependentDRInfo ) + sz += sizeof(macho_linkedit_data_command

); + return sz; } @@ -409,8 +431,14 @@ uint32_t HeaderAndLoadCommandsAtom::commandsCount() const if ( _hasVersionLoadCommand ) ++count; + if ( _hasSourceVersionLoadCommand ) + ++count; + if ( _hasThreadLoadCommand ) ++count; + + if ( _hasEntryPointLoadCommand ) + ++count; if ( _hasEncryptionLoadCommand ) ++count; @@ -436,6 +464,12 @@ uint32_t HeaderAndLoadCommandsAtom::commandsCount() const if ( _hasFunctionStartsLoadCommand ) ++count; + if ( _hasDataInCodeLoadCommand ) + ++count; + + if ( _hasDependentDRInfo ) + ++count; + return count; } @@ -473,6 +507,8 @@ uint32_t HeaderAndLoadCommandsAtom::flags() const else { if ( _options.outputKind() == Options::kStaticExecutable ) { bits |= MH_NOUNDEFS; + if ( _options.positionIndependentExecutable() ) + bits |= MH_PIE; } else if ( _options.outputKind() == Options::kPreload ) { bits |= MH_NOUNDEFS; @@ -704,7 +740,12 @@ uint32_t HeaderAndLoadCommandsAtom::sectionFlags(ld::Internal::FinalSection* 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: @@ -715,7 +756,13 @@ uint32_t HeaderAndLoadCommandsAtom::sectionFlags(ld::Internal::FinalSection* else return S_REGULAR | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS; case ld::Section::typeInitializerPointers: - return S_MOD_INIT_FUNC_POINTERS; + // 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: @@ -1006,17 +1053,26 @@ uint8_t* HeaderAndLoadCommandsAtom::copyVersionLoadCommand(uint8_t* p) const cmd->set_cmd(LC_VERSION_MIN_MACOSX); cmd->set_cmdsize(sizeof(macho_version_min_command

)); 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

)); cmd->set_version((uint32_t)iOSVersion); - cmd->set_sdk(0); + cmd->set_sdk(_options.sdkVersion()); } return p + sizeof(macho_version_min_command

); } +template +uint8_t* HeaderAndLoadCommandsAtom::copySourceVersionLoadCommand(uint8_t* p) const +{ + macho_source_version_command

* cmd = (macho_source_version_command

*)p; + cmd->set_cmd(LC_SOURCE_VERSION); + cmd->set_cmdsize(sizeof(macho_source_version_command

)); + cmd->set_version(_options.sourceVersion()); + return p + sizeof(macho_source_version_command

); +} template <> @@ -1087,6 +1143,24 @@ uint8_t* HeaderAndLoadCommandsAtom::copyThreadsLoadCommand(uint8_t* p) cons return p + threadLoadCommandSize(); } + + +template +uint8_t* HeaderAndLoadCommandsAtom::copyEntryPointLoadCommand(uint8_t* p) const +{ + macho_entry_point_command

* cmd = (macho_entry_point_command

*)p; + cmd->set_cmd(LC_MAIN); + cmd->set_cmdsize(sizeof(macho_entry_point_command

)); + 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

); +} + + template uint8_t* HeaderAndLoadCommandsAtom::copyEncryptionLoadCommand(uint8_t* p) const { @@ -1224,6 +1298,30 @@ uint8_t* HeaderAndLoadCommandsAtom::copyFunctionStartsLoadCommand(uint8_t* p) } +template +uint8_t* HeaderAndLoadCommandsAtom::copyDataInCodeLoadCommand(uint8_t* p) const +{ + macho_linkedit_data_command

* cmd = (macho_linkedit_data_command

*)p; + cmd->set_cmd(LC_DATA_IN_CODE); + cmd->set_cmdsize(sizeof(macho_linkedit_data_command

)); + cmd->set_dataoff(_writer.dataInCodeSection->fileOffset); + cmd->set_datasize(_writer.dataInCodeSection->size); + return p + sizeof(macho_linkedit_data_command

); +} + + +template +uint8_t* HeaderAndLoadCommandsAtom::copyDependentDRLoadCommand(uint8_t* p) const +{ + macho_linkedit_data_command

* cmd = (macho_linkedit_data_command

*)p; + cmd->set_cmd(LC_DYLIB_CODE_SIGN_DRS); + cmd->set_cmdsize(sizeof(macho_linkedit_data_command

)); + cmd->set_dataoff(_writer.dependentDRsSection->fileOffset); + cmd->set_datasize(_writer.dependentDRsSection->size); + return p + sizeof(macho_linkedit_data_command

); +} + + template void HeaderAndLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const { @@ -1271,8 +1369,14 @@ void HeaderAndLoadCommandsAtom::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); @@ -1318,7 +1422,13 @@ void HeaderAndLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const if ( _hasFunctionStartsLoadCommand ) p = this->copyFunctionStartsLoadCommand(p); - + + if ( _hasDataInCodeLoadCommand ) + p = this->copyDataInCodeLoadCommand(p); + + if ( _hasDependentDRInfo ) + p = this->copyDependentDRLoadCommand(p); + } diff --git a/src/ld/InputFiles.cpp b/src/ld/InputFiles.cpp index 7b05a96..766b17c 100644 --- a/src/ld/InputFiles.cpp +++ b/src/ld/InputFiles.cpp @@ -39,6 +39,8 @@ #include #include #include +#include +#include #include #include @@ -60,12 +62,19 @@ #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 { @@ -182,7 +191,15 @@ const char* InputFiles::fileArch(const uint8_t* p, unsigned len) if ( strncmp((const char*)p, "!\n", 8) == 0 ) return "archive"; - return "unsupported file format"; + char *unsupported = (char *)malloc(128); + strcpy(unsupported, "unsupported file format ("); + for (unsigned i=0; imagic == 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; @@ -222,7 +240,7 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib } 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; @@ -260,19 +278,26 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib 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; @@ -283,18 +308,17 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib 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 ) { - // 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"; @@ -302,7 +326,10 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib 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 ) @@ -318,13 +345,13 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib // 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); } } @@ -409,9 +436,12 @@ ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* from 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(reader); if ( dylibReader != NULL ) { + addDylib(dylibReader, info); //_installPathToDylibs[strdup(installPath)] = dylibReader; this->logDylib(dylibReader, true); return dylibReader; @@ -438,12 +468,15 @@ ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* from // 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(reader); if ( dylibReader != NULL ) { //assert(_installPathToDylibs.find(installPath) != _installPathToDylibs.end()); //_installPathToDylibs[strdup(installPath)] = dylibReader; + addDylib(dylibReader, info); this->logDylib(dylibReader, true); return dylibReader; } @@ -461,7 +494,8 @@ ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* from 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(); @@ -510,10 +544,7 @@ void InputFiles::createOpaqueFileSections() { // 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)); } } @@ -643,71 +674,158 @@ void InputFiles::inferArchitecture(Options& opts, const char** archName) 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& 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& 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::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& 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); @@ -750,8 +868,9 @@ ld::File* InputFiles::addDylib(ld::dylib::File* reader, const Options::FileInfo& } } } - if ( !dylibOnCommandLineTwice && !isSymlink ) - warning("dylibs with same install name: %s and %s", pos->second->path(), reader->path()); + // remove warning for 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 ) @@ -761,106 +880,240 @@ ld::File* InputFiles::addDylib(ld::dylib::File* reader, const Options::FileInfo& 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::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 fileMap; + const std::vector& files = _options.getInputFiles(); + for (std::vector::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::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::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(file); - ld::archive::File* archiveFile = dynamic_cast(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& 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; + // 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::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) { @@ -879,8 +1132,10 @@ bool InputFiles::searchLibraries(const char* name, bool searchDylibs, bool searc 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 } } @@ -904,6 +1159,11 @@ bool InputFiles::searchWeakDefInDylib(const char* name) const } return false; } + +static bool vectorContains(const std::vector& vec, ld::dylib::File* key) +{ + return std::find(vec.begin(), vec.end(), key) != vec.end(); +} void InputFiles::dylibs(ld::Internal& state) { @@ -928,8 +1188,11 @@ void InputFiles::dylibs(ld::Internal& state) ld::dylib::File* dylibFile = dynamic_cast(*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()); } @@ -938,15 +1201,30 @@ void InputFiles::dylibs(ld::Internal& state) 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::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; + + // 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 + diff --git a/src/ld/InputFiles.h b/src/ld/InputFiles.h index 1842c8e..afc2077 100644 --- a/src/ld/InputFiles.h +++ b/src/ld/InputFiles.h @@ -25,6 +25,8 @@ #ifndef __INPUT_FILES_H__ #define __INPUT_FILES_H__ +#define HAVE_PTHREADS 1 + #include #include #include @@ -40,6 +42,9 @@ #include #include #include +#if HAVE_PTHREADS +#include +#endif #include @@ -58,7 +63,7 @@ public: 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; @@ -69,29 +74,34 @@ public: 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: @@ -105,9 +115,41 @@ private: InstallNameToDylib _installPathToDylibs; std::set _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 _searchLibraries; }; } // namespace tool diff --git a/src/ld/LinkEdit.hpp b/src/ld/LinkEdit.hpp index 7bca2e3..e436082 100644 --- a/src/ld/LinkEdit.hpp +++ b/src/ld/LinkEdit.hpp @@ -37,6 +37,7 @@ #include "ld.hpp" #include "Architectures.hpp" #include "MachOFileAbstraction.hpp" +#include "code-sign-blobs/superblob.h" namespace ld { namespace tool { @@ -1249,7 +1250,6 @@ void SplitSegInfoAtom::encode() const _64bitPointerLocations.clear(); } - template class FunctionStartsAtom : public LinkEditAtom { @@ -1292,6 +1292,9 @@ void FunctionStartsAtom::encode() const std::vector& atoms = sect->atoms; for (std::vector::iterator ait = atoms.begin(); ait != atoms.end(); ++ait) { const ld::Atom* atom = *ait; + // 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; @@ -1313,6 +1316,194 @@ void FunctionStartsAtom::encode() const } +// Need way to formalize data in code +template +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

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 +ld::Section DataInCodeAtom::_s_section("__LINKEDIT", "__dataInCode", ld::Section::typeLinkEdit, true); + + +template +void DataInCodeAtom::encode() const +{ + if ( this->_writer.hasDataInCode ) { + uint64_t mhAddress = 0; + for (std::vector::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::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + // gather all code-in-data labels + std::vector 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::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; +} + + + + + +// linker needs to cache "Designated Requirements" in linked binary +template +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 +ld::Section DependentDRAtom::_s_section("__LINKEDIT", "__dependentDR", ld::Section::typeLinkEdit, true); + + +template +void DependentDRAtom::encode() const +{ + Security::SuperBlobCore, Security::kSecCodeMagicDRList, uint32_t>::Maker maker; + + uint32_t index = 0; + for(std::vector::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 ) { + // 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* 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 diff --git a/src/ld/LinkEditClassic.hpp b/src/ld/LinkEditClassic.hpp index 7fbc755..172bb4c 100644 --- a/src/ld/LinkEditClassic.hpp +++ b/src/ld/LinkEditClassic.hpp @@ -230,6 +230,7 @@ private: 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 > _globals; @@ -242,18 +243,21 @@ private: uint32_t _stabsIndexEnd; static ld::Section _s_section; + static int _s_anonNameIndex; + }; template ld::Section SymbolTableAtom::_s_section("__LINKEDIT", "__symbol_table", ld::Section::typeLinkEdit, true); +template +int SymbolTableAtom::_s_anonNameIndex = 1; template bool SymbolTableAtom::addLocal(const ld::Atom* atom, StringPoolAtom* pool) { macho_nlist

entry; - static int s_anonNameIndex = 1; assert(atom->symbolTableInclusion() != ld::Atom::symbolTableNotIn); // set n_strx @@ -264,7 +268,7 @@ bool SymbolTableAtom::addLocal(const ld::Atom* atom, StringPoolAtom* pool) if ( atom->combine() == ld::Atom::combineByNameAndContent ) { // don't use 'l' labels for x86_64 strings // 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; } } @@ -279,7 +283,7 @@ bool SymbolTableAtom::addLocal(const ld::Atom* atom, StringPoolAtom* pool) } 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; } } @@ -335,7 +339,16 @@ void SymbolTableAtom::addGlobal(const ld::Atom* atom, StringPoolAtom* pool) macho_nlist

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 ) { @@ -356,8 +369,9 @@ void SymbolTableAtom::addGlobal(const ld::Atom* atom, StringPoolAtom* pool) 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); } } @@ -609,6 +623,49 @@ bool SymbolTableAtom::hasStabs(uint32_t& ssos, uint32_t& ssoe, uint32_t& sos, return ( (_stabsIndexStart != _stabsIndexEnd) || (_stabsStringsOffsetStart != _stabsStringsOffsetEnd) ); } + +template +void SymbolTableAtom::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

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 void SymbolTableAtom::encode() { @@ -616,6 +673,7 @@ void SymbolTableAtom::encode() // make nlist entries for all local symbols std::vector& localAtoms = this->_writer._localAtoms; + std::vector& globalAtoms = this->_writer._exportedAtoms; _locals.reserve(localAtoms.size()+this->_state.stabs.size()); this->_writer._localSymbolsStartIndex = 0; // make nlist entries for all debug notes @@ -638,11 +696,19 @@ void SymbolTableAtom::encode() if ( this->addLocal(atom, this->_writer._stringPoolAtom) ) this->_writer._atomToSymbolIndex[atom] = symbolIndex++; } + // recreate L$start$ labels in -r mode + if ( (_options.outputKind() == Options::kObjectFile) && this->_writer.hasDataInCode ) { + for (std::vector::const_iterator it=globalAtoms.begin(); it != globalAtoms.end(); ++it) { + this->addDataInCodeLabels(*it, symbolIndex); + } + for (std::vector::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& globalAtoms = this->_writer._exportedAtoms; _globals.reserve(globalAtoms.size()); this->_writer._globalSymbolsStartIndex = symbolIndex; for (std::vector::const_iterator it=globalAtoms.begin(); it != globalAtoms.end(); ++it) { @@ -919,7 +985,9 @@ uint64_t ExternalRelocationsAtom::size() const return (_pointerLocations.size() + _callSiteLocations.size()) * sizeof(macho_relocation_info

); } +#if SUPPORT_ARCH_arm_any template <> uint32_t ExternalRelocationsAtom::pointerReloc() { return ARM_RELOC_VANILLA; } +#endif template <> uint32_t ExternalRelocationsAtom::pointerReloc() { return GENERIC_RELOC_VANILLA; } template <> uint32_t ExternalRelocationsAtom::pointerReloc() { return X86_64_RELOC_UNSIGNED; } @@ -1363,8 +1431,8 @@ void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* } 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); @@ -1390,6 +1458,7 @@ void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* } +#if SUPPORT_ARCH_arm_any template <> void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* sect, const Entry& entry, std::vector >& relocs) @@ -1614,6 +1683,7 @@ void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* } } +#endif @@ -1866,14 +1936,18 @@ bool IndirectSymbolTableAtom::kextBundlesDontHaveIndirectSymbolTable() template void IndirectSymbolTableAtom::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::iterator sit = this->_state.sections.begin(); sit != this->_state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; diff --git a/src/ld/Options.cpp b/src/ld/Options.cpp index 629903f..58d49a7 100644 --- a/src/ld/Options.cpp +++ b/src/ld/Options.cpp @@ -26,15 +26,21 @@ #include #include #include +#include #include #include +#include +#include +#include +#include +#include #include -#include "configure.h" #include "Options.h" #include "Architectures.hpp" #include "MachOFileAbstraction.hpp" +#include "Snapshot.h" // upward dependency on lto::version() namespace lto { @@ -43,9 +49,19 @@ 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 + // 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; @@ -88,6 +104,19 @@ void throwf(const char* format, ...) 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), @@ -102,8 +131,8 @@ Options::Options(int argc, const char* argv[]) 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), @@ -112,7 +141,7 @@ Options::Options(int argc, const char* argv[]) 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), @@ -131,10 +160,16 @@ Options::Options(int argc, const char* argv[]) 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(); @@ -148,7 +183,6 @@ Options::~Options() { } - bool Options::errorBecauseOfWarnings() const { return (sFatalWarnings && (sWarningsCount > 0)); @@ -479,114 +513,77 @@ bool Options::keepLocalSymbol(const char* symbolName) const 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; } @@ -674,7 +671,6 @@ Options::FileInfo Options::findFramework(const char* frameworkName) Options::FileInfo Options::findFramework(const char* rootName, const char* suffix) { - struct stat statBuffer; for (std::vector::iterator it = fFrameworkSearchPaths.begin(); it != fFrameworkSearchPaths.end(); it++) { @@ -695,15 +691,12 @@ Options::FileInfo Options::findFramework(const char* rootName, const char* suffi 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; } } @@ -717,7 +710,6 @@ Options::FileInfo Options::findFramework(const char* rootName, const char* suffi 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) ) { @@ -731,19 +723,13 @@ Options::FileInfo Options::findFile(const char* path) const 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; } @@ -756,10 +742,7 @@ Options::FileInfo Options::findFile(const char* path) const 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; } } @@ -885,7 +868,7 @@ void Options::parseSegAddrTable(const char* segAddrPath, const char* installPth) 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, ','); @@ -901,16 +884,17 @@ void Options::loadFileList(const char* 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'); @@ -921,10 +905,34 @@ void Options::loadFileList(const char* fileOfPaths) 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); @@ -1719,6 +1727,9 @@ void Options::warnObsolete(const char* arg) // 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); @@ -1730,12 +1741,17 @@ void Options::parse(int argc, const char* 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() @@ -1782,22 +1798,38 @@ void Options::parse(int argc, const char* argv[]) 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. @@ -1831,6 +1863,7 @@ void Options::parse(int argc, const char* argv[]) 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. @@ -1851,10 +1884,12 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-sectorder") == 0 ) { if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) ) throw "-sectorder missing

"; + 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 ) { @@ -1865,6 +1900,7 @@ void Options::parse(int argc, const char* argv[]) 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
"; + snapshotFileArgIndex = 3; addSection(argv[i+1], argv[i+2], argv[i+3]); i += 3; } @@ -1873,7 +1909,7 @@ void Options::parse(int argc, const char* argv[]) || (strcmp(arg, "-dylinker_install_name") == 0) || (strcmp(arg, "-install_name") == 0)) { fDylibInstallName = argv[++i]; - if ( fDylibInstallName == NULL ) + if ( fDylibInstallName == NULL ) throw "-install_name missing "; } // Sets the base address of the output. @@ -1893,10 +1929,12 @@ void Options::parse(int argc, const char* argv[]) } // 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 "; - 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; @@ -1918,6 +1956,7 @@ void Options::parse(int argc, const char* argv[]) } } else if ( strcmp(arg, "-interposable_list") == 0 ) { + snapshotFileArgIndex = 1; fInterposeMode = kInterposeSome; loadExportFile(argv[++i], "-interposable_list", fInterposeList); } @@ -1926,12 +1965,14 @@ void Options::parse(int argc, const char* argv[]) 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; @@ -1950,12 +1991,14 @@ void Options::parse(int argc, const char* argv[]) 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; @@ -1971,27 +2014,42 @@ void Options::parse(int argc, const char* argv[]) } // 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; } @@ -2002,7 +2060,7 @@ void Options::parse(int argc, const char* argv[]) // 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 ) { @@ -2038,7 +2096,7 @@ void Options::parse(int argc, const char* argv[]) // 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 @@ -2066,6 +2124,8 @@ void Options::parse(int argc, const char* argv[]) // 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 @@ -2122,6 +2182,7 @@ void Options::parse(int argc, const char* argv[]) } // ??? 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"; @@ -2185,10 +2246,12 @@ void Options::parse(int argc, const char* argv[]) i += 2; } else if ( strcmp(arg, "-bundle_loader") == 0 ) { + snapshotFileArgIndex = 1; fBundleLoader = argv[++i]; if ( (fBundleLoader == NULL) || (fBundleLoader[0] == '-') ) throw "-bundle_loader missing "; FileInfo info = findFile(fBundleLoader); + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); info.options.fBundleLoader = true; fInputFiles.push_back(info); } @@ -2394,6 +2457,7 @@ void Options::parse(int argc, const char* argv[]) // previously handled by buildSearchPaths() } else if ( strcmp(arg, "-syslibroot") == 0 ) { + snapshotArgCount = 0; ++i; // previously handled by buildSearchPaths() } @@ -2404,6 +2468,7 @@ void Options::parse(int argc, const char* argv[]) fUUIDMode = kUUIDRandom; } else if ( strcmp(arg, "-dtrace") == 0 ) { + snapshotFileArgIndex = 1; const char* name = argv[++i]; if ( name == NULL ) throw "-dtrace missing argument"; @@ -2426,6 +2491,7 @@ void Options::parse(int argc, const char* argv[]) 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' @@ -2468,33 +2534,51 @@ void Options::parse(int argc, const char* argv[]) 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 ) { @@ -2539,6 +2623,7 @@ void Options::parse(int argc, const char* argv[]) fMarkDeadStrippableDylib = true; } else if ( strcmp(arg, "-exported_symbols_order") == 0 ) { + snapshotFileArgIndex = 1; loadSymbolOrderFile(argv[++i], fExportSymbolsOrder); } else if ( strcmp(arg, "-no_compact_linkedit") == 0 ) { @@ -2613,6 +2698,12 @@ void Options::parse(int argc, const char* argv[]) 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 ) @@ -2622,9 +2713,11 @@ void Options::parse(int argc, const char* argv[]) 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 ) { @@ -2640,6 +2733,7 @@ void Options::parse(int argc, const char* argv[]) 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); @@ -2654,13 +2748,56 @@ void Options::parse(int argc, const char* argv[]) } 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 "; + 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 "; + 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 @@ -2670,8 +2807,13 @@ void Options::parse(int argc, const char* argv[]) // 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(); } @@ -2746,11 +2888,12 @@ void Options::buildSearchPaths(int argc, const char* argv[]) 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); } } @@ -2924,6 +3067,19 @@ void Options::parsePreCommandLineEnvironmentSettings() 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; + } } @@ -2966,6 +3122,13 @@ void Options::parsePostCommandLineEnvironmentSettings() // 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); + } } @@ -3019,10 +3182,10 @@ void Options::reconfigureDefaults() 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 } @@ -3030,13 +3193,13 @@ void Options::reconfigureDefaults() 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 } @@ -3058,7 +3221,7 @@ void Options::reconfigureDefaults() } 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; } @@ -3080,7 +3243,8 @@ void Options::reconfigureDefaults() // iOS 5.0 and later use new MH_KEXT_BUNDLE type fMakeCompressedDyldInfo = false; fMakeCompressedDyldInfoForceOff = true; - fAllowTextRelocs = true; + fAllowTextRelocs = true; + fKextsUseStubs = !fAllowTextRelocs; fUndefinedTreatment = kUndefinedDynamicLookup; break; } @@ -3357,27 +3521,14 @@ void Options::reconfigureDefaults() // 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; @@ -3437,9 +3588,9 @@ void Options::reconfigureDefaults() // set fOutputSlidable switch ( fOutputKind ) { case Options::kObjectFile: - case Options::kStaticExecutable: fOutputSlidable = false; break; + case Options::kStaticExecutable: case Options::kDynamicExecutable: fOutputSlidable = fPositionIndependentExecutable; break; @@ -3490,10 +3641,12 @@ void Options::reconfigureDefaults() 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; @@ -3530,6 +3683,130 @@ void Options::reconfigureDefaults() // 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() @@ -3560,6 +3837,7 @@ void Options::checkIllegalOptionCombinations() if ( strcmp(&lastSlash[1], subUmbrella) == 0 ) { info.options.fReExport = true; found = true; + fLinkSnapshot.recordSubUmbrella(info.path); break; } } @@ -3582,6 +3860,7 @@ void Options::checkIllegalOptionCombinations() if ( strncmp(&lastSlash[1], subLibrary, dot-lastSlash-1) == 0 ) { info.options.fReExport = true; found = true; + fLinkSnapshot.recordSubLibrary(info.path); break; } } @@ -3891,10 +4170,10 @@ void Options::checkIllegalOptionCombinations() } } - // 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"; @@ -3902,14 +4181,15 @@ void Options::checkIllegalOptionCombinations() 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: @@ -3966,6 +4246,9 @@ void Options::checkForClassic(int argc, const char* argv[]) 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); @@ -4018,31 +4301,6 @@ void Options::checkForClassic(int argc, const char* argv[]) } } } - - // -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[]) @@ -4096,3 +4354,32 @@ 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; +} + diff --git a/src/ld/Options.h b/src/ld/Options.h index 5a8a7e0..be6dc56 100644 --- a/src/ld/Options.h +++ b/src/ld/Options.h @@ -34,10 +34,13 @@ #include #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: @@ -82,12 +85,42 @@ 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; @@ -244,6 +277,7 @@ public: 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; } @@ -282,11 +316,12 @@ public: 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; @@ -294,8 +329,18 @@ public: 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 { @@ -343,7 +388,7 @@ private: 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); @@ -423,9 +468,12 @@ private: 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; @@ -454,6 +502,7 @@ private: bool fDeadStripDylibs; bool fAllowTextRelocs; bool fWarnTextRelocs; + bool fKextsUseStubs; bool fUsingLazyDylibLinking; bool fEncryptable; bool fOrderData; @@ -502,9 +551,20 @@ private: 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; @@ -526,6 +586,9 @@ private: std::vector fSDKPaths; std::vector fDyldEnvironExtras; bool fSaveTempFiles; + mutable Snapshot fLinkSnapshot; + bool fSnapshotRequested; + const char * fPipelineFifo; }; diff --git a/src/ld/OutputFile.cpp b/src/ld/OutputFile.cpp index f217eb0..6fc91e8 100644 --- a/src/ld/OutputFile.cpp +++ b/src/ld/OutputFile.cpp @@ -72,11 +72,12 @@ namespace tool { 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), @@ -87,6 +88,8 @@ OutputFile::OutputFile(const Options& opts) _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()), @@ -109,7 +112,9 @@ OutputFile::OutputFile(const Options& opts) _weakBindingInfoAtom(NULL), _exportInfoAtom(NULL), _splitSegInfoAtom(NULL), - _functionStartsAtom(NULL) + _functionStartsAtom(NULL), + _dataInCodeAtom(NULL), + _dependentDRInfoAtom(NULL) { } @@ -179,10 +184,14 @@ bool OutputFile::findSegment(ld::Internal& state, uint64_t addr, uint64_t* start void OutputFile::assignAtomAddresses(ld::Internal& state) { + const bool log = false; + if ( log ) fprintf(stderr, "assignAtomAddresses()\n"); for (std::vector::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::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 @@ -239,6 +248,18 @@ void OutputFile::updateLINKEDITAddresses(ld::Internal& state) _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(); @@ -572,8 +593,8 @@ void OutputFile::assignFileOffsets(ld::Internal& state) } } - 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 ) @@ -605,6 +626,9 @@ void OutputFile::assignFileOffsets(ld::Internal& state) if ( hasZeroForFileOffset(sect) ) { // fileoff of zerofill sections is moot, but historically it is set to zero sect->fileOffset = 0; + + // align file offset with address layout + fileOffset += sect->alignmentPaddingBytes; } else { // page align file offset at start of each segment @@ -713,6 +737,11 @@ uint64_t OutputFile::addressOf(const ld::Internal& state, const ld::Fixup* fixup 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"; @@ -1205,6 +1234,13 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: 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); @@ -1529,35 +1565,8 @@ bool OutputFile::hasZeroForFileOffset(const ld::Section* sect) 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; @@ -1598,74 +1607,153 @@ void OutputFile::writeOutputFile(ld::Internal& state) } } } +} + + +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::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); + } + // 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::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); - } - // 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 @@ -1778,27 +1866,6 @@ void OutputFile::buildSymbolTable(ld::Internal& state) continue; } - // 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(atom))->setCombine(ld::Atom::combineByName); - } - else if ( atom->combine() == ld::Atom::combineByName ) { - if ( _options.forceNotWeak(name) ) - (const_cast(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()) ) { @@ -1879,6 +1946,7 @@ void OutputFile::buildSymbolTable(ld::Internal& state) void OutputFile::addPreloadLinkEdit(ld::Internal& state) { switch ( _options.architecture() ) { +#if SUPPORT_ARCH_i386 case CPU_TYPE_I386: if ( _hasLocalRelocations ) { _localRelocsAtom = new LocalRelocationsAtom(_options, state, *this); @@ -1897,6 +1965,8 @@ void OutputFile::addPreloadLinkEdit(ld::Internal& state) stringPoolSection = state.addAtom(*_stringPoolAtom); } break; +#endif +#if SUPPORT_ARCH_x86_64 case CPU_TYPE_X86_64: if ( _hasLocalRelocations ) { _localRelocsAtom = new LocalRelocationsAtom(_options, state, *this); @@ -1915,6 +1985,8 @@ void OutputFile::addPreloadLinkEdit(ld::Internal& state) stringPoolSection = state.addAtom(*_stringPoolAtom); } break; +#endif +#if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: if ( _hasLocalRelocations ) { _localRelocsAtom = new LocalRelocationsAtom(_options, state, *this); @@ -1933,6 +2005,7 @@ void OutputFile::addPreloadLinkEdit(ld::Internal& state) stringPoolSection = state.addAtom(*_stringPoolAtom); } break; +#endif default: throw "architecture not supported for -preload"; } @@ -1947,6 +2020,7 @@ void OutputFile::addLinkEdit(ld::Internal& state) return addPreloadLinkEdit(state); switch ( _options.architecture() ) { +#if SUPPORT_ARCH_i386 case CPU_TYPE_I386: if ( _hasSectionRelocations ) { _sectionsRelocationsAtom = new SectionRelocationsAtom(_options, state, *this); @@ -1980,6 +2054,14 @@ void OutputFile::addLinkEdit(ld::Internal& state) _functionStartsAtom = new FunctionStartsAtom(_options, state, *this); functionStartsSection = state.addAtom(*_functionStartsAtom); } + if ( _hasDataInCodeInfo ) { + _dataInCodeAtom = new DataInCodeAtom(_options, state, *this); + dataInCodeSection = state.addAtom(*_dataInCodeAtom); + } + if ( _hasDependentDRInfo ) { + _dependentDRInfoAtom = new DependentDRAtom(_options, state, *this); + dependentDRsSection = state.addAtom(*_dependentDRInfoAtom); + } if ( _hasSymbolTable ) { _symbolTableAtom = new SymbolTableAtom(_options, state, *this); symbolTableSection = state.addAtom(*_symbolTableAtom); @@ -1995,6 +2077,8 @@ void OutputFile::addLinkEdit(ld::Internal& state) stringPoolSection = state.addAtom(*_stringPoolAtom); } break; +#endif +#if SUPPORT_ARCH_x86_64 case CPU_TYPE_X86_64: if ( _hasSectionRelocations ) { _sectionsRelocationsAtom = new SectionRelocationsAtom(_options, state, *this); @@ -2028,6 +2112,14 @@ void OutputFile::addLinkEdit(ld::Internal& state) _functionStartsAtom = new FunctionStartsAtom(_options, state, *this); functionStartsSection = state.addAtom(*_functionStartsAtom); } + if ( _hasDataInCodeInfo ) { + _dataInCodeAtom = new DataInCodeAtom(_options, state, *this); + dataInCodeSection = state.addAtom(*_dataInCodeAtom); + } + if ( _hasDependentDRInfo ) { + _dependentDRInfoAtom = new DependentDRAtom(_options, state, *this); + dependentDRsSection = state.addAtom(*_dependentDRInfoAtom); + } if ( _hasSymbolTable ) { _symbolTableAtom = new SymbolTableAtom(_options, state, *this); symbolTableSection = state.addAtom(*_symbolTableAtom); @@ -2043,6 +2135,8 @@ void OutputFile::addLinkEdit(ld::Internal& state) stringPoolSection = state.addAtom(*_stringPoolAtom); } break; +#endif +#if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: if ( _hasSectionRelocations ) { _sectionsRelocationsAtom = new SectionRelocationsAtom(_options, state, *this); @@ -2076,6 +2170,14 @@ void OutputFile::addLinkEdit(ld::Internal& state) _functionStartsAtom = new FunctionStartsAtom(_options, state, *this); functionStartsSection = state.addAtom(*_functionStartsAtom); } + if ( _hasDataInCodeInfo ) { + _dataInCodeAtom = new DataInCodeAtom(_options, state, *this); + dataInCodeSection = state.addAtom(*_dataInCodeAtom); + } + if ( _hasDependentDRInfo ) { + _dependentDRInfoAtom = new DependentDRAtom(_options, state, *this); + dependentDRsSection = state.addAtom(*_dependentDRInfoAtom); + } if ( _hasSymbolTable ) { _symbolTableAtom = new SymbolTableAtom(_options, state, *this); symbolTableSection = state.addAtom(*_symbolTableAtom); @@ -2091,6 +2193,7 @@ void OutputFile::addLinkEdit(ld::Internal& state) stringPoolSection = state.addAtom(*_stringPoolAtom); } break; +#endif default: throw "unknown architecture"; } @@ -2099,18 +2202,24 @@ void OutputFile::addLinkEdit(ld::Internal& state) void OutputFile::addLoadCommands(ld::Internal& state) { switch ( _options.architecture() ) { +#if SUPPORT_ARCH_x86_64 case CPU_TYPE_X86_64: _headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom(_options, state, *this); headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom); break; +#endif +#if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: _headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom(_options, state, *this); headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom); break; +#endif +#if SUPPORT_ARCH_i386 case CPU_TYPE_I386: _headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom(_options, state, *this); headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom); break; +#endif default: throw "unknown architecture"; } @@ -2485,7 +2594,15 @@ void OutputFile::generateLinkEditInfo(ld::Internal& state) } 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) ) { @@ -2530,7 +2647,8 @@ void OutputFile::noteTextReloc(const ld::Atom* atom, const ld::Atom* target) 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. " @@ -2568,9 +2686,10 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* 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; } @@ -2596,9 +2715,10 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* 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; } @@ -2620,7 +2740,7 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s uint8_t rebaseType = REBASE_TYPE_POINTER; uint8_t type = BIND_TYPE_POINTER; const ld::dylib::File* dylib = dynamic_cast(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; @@ -2716,6 +2836,22 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s 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::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 ) { @@ -2745,10 +2881,20 @@ void OutputFile::addClassicRelocs(ld::Internal& state, ld::Internal::FinalSectio 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 @@ -3091,8 +3237,8 @@ void OutputFile::writeMapFile(ld::Internal& state) // uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]); //} // write table of object files - std::map readerToOrdinal; - std::map ordinalToReader; + std::map readerToOrdinal; + std::map ordinalToReader; std::map readerToFileOrdinal; for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; @@ -3103,8 +3249,8 @@ void OutputFile::writeMapFile(ld::Internal& state) const ld::File* reader = atom->file(); if ( reader == NULL ) continue; - uint32_t readerOrdinal = reader->ordinal(); - std::map::iterator pos = readerToOrdinal.find(reader); + ld::File::Ordinal readerOrdinal = reader->ordinal(); + std::map::iterator pos = readerToOrdinal.find(reader); if ( pos == readerToOrdinal.end() ) { readerToOrdinal[reader] = readerOrdinal; ordinalToReader[readerOrdinal] = reader; @@ -3113,13 +3259,10 @@ void OutputFile::writeMapFile(ld::Internal& state) } fprintf(mapFile, "# Object files:\n"); fprintf(mapFile, "[%3u] %s\n", 0, "linker synthesized"); - uint32_t fileIndex = 0; - readerToFileOrdinal[NULL] = fileIndex++; - for(std::map::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::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"); @@ -3196,8 +3339,8 @@ public: 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); @@ -3300,11 +3443,13 @@ void OutputFile::synthesizeDebugNotes(ld::Internal& state) 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 + 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 diff --git a/src/ld/OutputFile.h b/src/ld/OutputFile.h index 8401716..d3567ae 100644 --- a/src/ld/OutputFile.h +++ b/src/ld/OutputFile.h @@ -74,6 +74,7 @@ public: bool _noReExportedDylibs; bool hasThreadLocalVariableDefinitions; bool pieDisabled; + bool hasDataInCode; ld::Internal::FinalSection* headerAndLoadCommandsSection; ld::Internal::FinalSection* rebaseSection; ld::Internal::FinalSection* bindingSection; @@ -82,6 +83,8 @@ public: 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; @@ -137,6 +140,8 @@ public: }; 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); @@ -243,6 +248,8 @@ private: const bool _hasSectionRelocations; const bool _hasSplitSegInfo; const bool _hasFunctionStartsInfo; + const bool _hasDataInCodeInfo; + const bool _hasDependentDRInfo; bool _hasDynamicSymbolTable; bool _hasLocalRelocations; bool _hasExternalRelocations; @@ -280,6 +287,8 @@ public: class LinkEditAtom* _exportInfoAtom; class LinkEditAtom* _splitSegInfoAtom; class LinkEditAtom* _functionStartsAtom; + class LinkEditAtom* _dataInCodeAtom; + class LinkEditAtom* _dependentDRInfoAtom; }; } // namespace tool diff --git a/src/ld/Resolver.cpp b/src/ld/Resolver.cpp index 2502820..3a28788 100644 --- a/src/ld/Resolver.cpp +++ b/src/ld/Resolver.cpp @@ -292,6 +292,7 @@ void Resolver::buildAtomList() // each input files contributes initial atoms _atoms.reserve(1024); _inputFiles.forEachInitialAtom(*this); + _completedInitialObjectFiles = true; //_symbolTable.printStatistics(); @@ -332,10 +333,6 @@ void Resolver::doFile(const ld::File& file) 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; @@ -427,16 +424,16 @@ void Resolver::doAtom(const ld::Atom& atom) } 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)); } } } @@ -446,7 +443,7 @@ void Resolver::doAtom(const ld::Atom& atom) (const_cast(&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; @@ -457,7 +454,7 @@ void Resolver::doAtom(const ld::Atom& atom) //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; } @@ -491,11 +488,7 @@ void Resolver::doAtom(const ld::Atom& atom) // 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() ) @@ -856,9 +849,10 @@ void Resolver::deadStripOptimize() // 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_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() : "")); } } @@ -869,9 +863,56 @@ void Resolver::deadStripOptimize() 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_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& undefs) +{ + StringSet undefSet; + // search all atoms for references that are unbound + for (std::vector::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& undefs) { StringSet undefSet; @@ -990,7 +1031,7 @@ bool Resolver::printReferencedBy(const char* name, SymbolTable::IndirectBindingS ++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 } @@ -1029,9 +1070,10 @@ void Resolver::checkUndefines(bool force) break; } std::vector unresolvableUndefines; - // LTO many have eliminated need for some undefines - if ( _options.deadCodeStrip() || _haveLLVMObjs ) + if ( _options.deadCodeStrip() ) this->liveUndefines(unresolvableUndefines); + else if( _haveLLVMObjs ) + this->remainingUndefines(unresolvableUndefines); // LTO may have eliminated need for some undefines else _symbolTable.undefines(unresolvableUndefines); @@ -1056,7 +1098,7 @@ void Resolver::checkUndefines(bool force) 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 @@ -1174,8 +1216,6 @@ const ld::Atom* Resolver::entryPoint(bool searchArchives) 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]; } @@ -1262,7 +1302,17 @@ void Resolver::fillInInternalState() 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_iterator it=_atoms.begin(); it != _atoms.end(); ++it) { + fprintf(stderr, " atom=%p %s\n", *it, (*it)->name()); + } + } } void Resolver::linkTimeOptimize() @@ -1275,7 +1325,7 @@ 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(); @@ -1289,7 +1339,7 @@ void Resolver::linkTimeOptimize() std::vector newAtoms; std::vector 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 @@ -1299,36 +1349,12 @@ void Resolver::linkTimeOptimize() // some atoms might have been optimized way (marked coalesced), remove them this->removeCoalescedAwayAtoms(); - - // add new atoms into their final section - for (std::vector::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::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::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_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::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 @@ -1336,7 +1362,6 @@ void Resolver::linkTimeOptimize() _inputFiles.searchLibraries(targetName, true, true, false, *this); } } - _addToFinalSection = false; // if -dead_strip on command line if ( _options.deadCodeStrip() ) { @@ -1346,19 +1371,11 @@ void Resolver::linkTimeOptimize() } // and re-compute dead code this->deadStripOptimize(); - - // remove newly dead atoms from each section - for (std::vector::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 @@ -1370,6 +1387,39 @@ void Resolver::linkTimeOptimize() } +void Resolver::tweakWeakness() +{ + // Add command line options to control symbol weak-def bit on exported symbols + if ( _options.hasWeakBitTweaks() ) { + for (std::vector::iterator sit = _internal.sections.begin(); sit != _internal.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + for (std::vector::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(atom))->setCombine(ld::Atom::combineByName); + } + else if ( atom->combine() == ld::Atom::combineByName ) { + if ( _options.forceNotWeak(name) ) + (const_cast(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(); @@ -1381,8 +1431,10 @@ void Resolver::resolve() this->checkUndefines(); this->checkDylibSymbolCollisions(); this->removeCoalescedAwayAtoms(); - this->fillInInternalState(); this->linkTimeOptimize(); + this->fillInInternalState(); + this->tweakWeakness(); + _symbolTable.checkDuplicateSymbols(); } diff --git a/src/ld/Resolver.h b/src/ld/Resolver.h index 65e2006..c761990 100644 --- a/src/ld/Resolver.h +++ b/src/ld/Resolver.h @@ -57,10 +57,10 @@ namespace tool { 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) {} @@ -94,8 +94,9 @@ private: void markLive(const ld::Atom& atom, WhyLiveBackChain* previous); bool isDtraceProbe(ld::Fixup::Kind kind); void liveUndefines(std::vector&); + void remainingUndefines(std::vector&); bool printReferencedBy(const char* name, SymbolTable::IndirectBindingSlot slot); - + void tweakWeakness(); class CStringEquals { public: @@ -118,14 +119,13 @@ private: }; const Options& _options; - const InputFiles& _inputFiles; + InputFiles& _inputFiles; ld::Internal& _internal; std::vector _atoms; std::set _deadStripRoots; std::vector _atomsWithUnresolvedReferences; SymbolTable _symbolTable; bool _haveLLVMObjs; - bool _addToFinalSection; bool _completedInitialObjectFiles; }; diff --git a/src/ld/Snapshot.cpp b/src/ld/Snapshot.cpp new file mode 100644 index 0000000..27ce370 --- /dev/null +++ b/src/ld/Snapshot.cpp @@ -0,0 +1,538 @@ +// +// Snapshot.cpp +// ld64 +// +// Created by Josh Behnke on 8/25/11. +// Copyright (c) 2011 Apple Inc. All rights reserved. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; iaddSnapshotLinkArg(argIndex, argCount, fileArg); })); + } else { + char buf[PATH_MAX]; + const char *subdir = dataFilesString; + for (int i=0, arg=argIndex; irecordArch(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; irecordDylibSymbol(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(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); + } + } +} diff --git a/src/ld/Snapshot.h b/src/ld/Snapshot.h new file mode 100644 index 0000000..8cfd17e --- /dev/null +++ b/src/ld/Snapshot.h @@ -0,0 +1,153 @@ +// +// 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 +#include +#include +#include + +#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 SnapshotLog; + + struct strcompclass { + bool operator() (const char *a, const char *b) const { return ::strcmp(a, b) < 0; } + }; + typedef std::vector StringVector; + typedef std::map DylibMap; + typedef std::map 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 // 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 diff --git a/src/ld/SymbolTable.cpp b/src/ld/SymbolTable.cpp index 66ff358..406556a 100644 --- a/src/ld/SymbolTable.cpp +++ b/src/ld/SymbolTable.cpp @@ -34,6 +34,8 @@ #include #include +#include +#include #include #include #include @@ -57,14 +59,12 @@ namespace tool { // 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& ibt) - : _options(opts), _cstringTable(6151), _indirectBindingTable(ibt), _hasExternalTentativeDefinitions(false) + : _options(opts), _cstringTable(6151), _indirectBindingTable(ibt), _hasExternalTentativeDefinitions(false) { _s_indirectBindingTable = this; - _s_doDemangle = _options.demangleSymbols(); } @@ -119,251 +119,295 @@ bool SymbolTable::ReferencesHashFuncs::operator()(const ld::Atom* left, const ld } +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; + _duplicateSymbols.insert(std::pair(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 ) { - // 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() ) { - // 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() ) { - // 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 ) { + // 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); + // 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() ) { + // 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() ) { + // 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 + // 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: - // 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 ) { @@ -474,6 +518,16 @@ void SymbolTable::markCoalescedAway(const ld::Atom* atom) } + +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& undefs) { // return all names in _byNameTable that have no associated atom @@ -483,7 +537,8 @@ void SymbolTable::undefines(std::vector& undefs) 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); } @@ -684,35 +739,6 @@ const ld::Atom* SymbolTable::indirectAtom(IndirectBindingSlot slot) const 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", diff --git a/src/ld/SymbolTable.h b/src/ld/SymbolTable.h index f352a51..451d064 100644 --- a/src/ld/SymbolTable.h +++ b/src/ld/SymbolTable.h @@ -93,6 +93,9 @@ private: typedef std::map SlotToName; typedef __gnu_cxx::hash_map, CStringEquals> NameToMap; + + typedef std::vector DuplicatedSymbolAtomList; + typedef std::map DuplicateSymbols; public: @@ -126,17 +129,24 @@ 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; @@ -154,7 +164,7 @@ private: std::vector& _indirectBindingTable; bool _hasExternalTentativeDefinitions; - static bool _s_doDemangle; + DuplicateSymbols _duplicateSymbols; }; diff --git a/src/ld/code-sign-blobs/blob.cpp b/src/ld/code-sign-blobs/blob.cpp new file mode 100644 index 0000000..5b02f32 --- /dev/null +++ b/src/ld/code-sign-blobs/blob.cpp @@ -0,0 +1,129 @@ +/* + * 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(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(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 diff --git a/src/ld/code-sign-blobs/blob.h b/src/ld/code-sign-blobs/blob.h new file mode 100644 index 0000000..db18746 --- /dev/null +++ b/src/ld/code-sign-blobs/blob.h @@ -0,0 +1,217 @@ +/* + * 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 { ... } +// 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(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 + +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 ) + 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 + T *at(Offset offset) + { return LowLevelMemoryUtilities::increment(this, offset); } + + template + const T *at(Offset offset) const + { return LowLevelMemoryUtilities::increment(this, offset); } + + template + bool contains(Offset1 offset, Offset2 size) const + { return offset >= 0 && size_t(offset) >= sizeof(BlobCore) && (size_t(offset) + size) <= this->length(); } + + template + 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 + 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 mMagic; + Endian 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 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(blob)) { + if (p->validateBlob()) + return p; + if (unalloc) + ::free(p); + } + return NULL; + } + + static const BlobType *specific(const BlobCore *blob) + { + const BlobType *p = static_cast(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 { +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 diff --git a/src/ld/code-sign-blobs/endian.h b/src/ld/code-sign-blobs/endian.h new file mode 100644 index 0000000..f312e43 --- /dev/null +++ b/src/ld/code-sign-blobs/endian.h @@ -0,0 +1,138 @@ +/* + * 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 +#include +//#include +#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 +static inline Base *h2n(Base *p) { return (Base *)h2n(uintptr_t(p)); } + +template +static inline Base *n2h(Base *p) { return (Base *)n2h(uintptr_t(p)); } + + +// +// In-place fix operations +// +template +static inline void h2ni(Type &v) { v = h2n(v); } + +template +static inline void n2hi(Type &v) { v = n2h(v); } + +// +// Endian 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 as appropriate. +// +// Note well that the address of an Endian is not an address-of-T, +// and there is no conversion available. +// +template +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 diff --git a/src/ld/code-sign-blobs/memutils.h b/src/ld/code-sign-blobs/memutils.h new file mode 100644 index 0000000..391ddc1 --- /dev/null +++ b/src/ld/code-sign-blobs/memutils.h @@ -0,0 +1,124 @@ +/* + * 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 +#include +#include +#include + + +// +// 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 +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 +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(alignUp(uintptr_t(p), alignment)); +} + +inline const void *alignUp(const void *p, size_t alignment = systemAlignment) +{ + return reinterpret_cast(alignUp(uintptr_t(p), alignment)); +} + +template +inline const T *increment(const void *p, ptrdiff_t offset) +{ return reinterpret_cast(uintptr_t(p) + offset); } + +template +inline T *increment(void *p, ptrdiff_t offset) +{ return reinterpret_cast(uintptr_t(p) + offset); } + +inline const void *increment(const void *p, ptrdiff_t offset) +{ return increment(p, offset); } + +inline void *increment(void *p, ptrdiff_t offset) +{ return increment(p, offset); } + +template +inline const T *increment(const void *p, ptrdiff_t offset, size_t alignment) +{ return increment(alignUp(p, alignment), offset); } + +template +inline T *increment(void *p, ptrdiff_t offset, size_t alignment) +{ return increment(alignUp(p, alignment), offset); } + +inline const void *increment(const void *p, ptrdiff_t offset, size_t alignment) +{ return increment(p, offset, alignment); } + +inline void *increment(void *p, ptrdiff_t offset, size_t alignment) +{ return increment(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 diff --git a/src/ld/code-sign-blobs/superblob.h b/src/ld/code-sign-blobs/superblob.h new file mode 100644 index 0000000..7fea85e --- /dev/null +++ b/src/ld/code-sign-blobs/superblob.h @@ -0,0 +1,249 @@ +// +// SuperBlob - a typed bag of Blobs +// +#ifndef _H_SUPERBLOB +#define _H_SUPERBLOB + +#include "blob.h" +#include +#include +#include + +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 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 BlobType *at(Offset offset) { return BlobCore::at(offset); } + template const BlobType *at(Offset offset) const { return BlobCore::at(offset); } + + void setup(size_t size, unsigned cnt) + { this->initialize(size); this->mCount = cnt; } + + struct Index { + Endian type; // type of sub-Blob + Endian 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(off) : NULL; } + + template + 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 + const BlobType *find(Type t) const { return BlobType::specific(find(t)); } + +private: + Endian mCount; // number of sub-Blobs following + Index mIndex[0]; // IndexSlot structures + // followed by sub-Blobs, packed and ordered in an undefined way +}; + + +template +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(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 +class SuperBlob : public SuperBlobCore, _magic, _Type> { +}; + + +template +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(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 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 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 +void SuperBlobCore<_BlobType, _magic, _Type>::Maker::add(Type type, BlobCore *blob) +{ + pair 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 +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 +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 +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 +_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(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 diff --git a/src/ld/ld.cpp b/src/ld/ld.cpp index ce49e73..24e9c48 100644 --- a/src/ld/ld.cpp +++ b/src/ld/ld.cpp @@ -67,6 +67,7 @@ extern "C" double log2 ( double ); #include "InputFiles.h" #include "Resolver.h" #include "OutputFile.h" +#include "Snapshot.h" #include "passes/stubs/make_stubs.h" #include "passes/dtrace_dof.h" @@ -619,6 +620,25 @@ static void getVMInfo(vm_statistics_data_t& info) } } + + +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; @@ -632,6 +652,9 @@ int main(int argc, const char* argv[]) 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); @@ -649,7 +672,7 @@ int main(int argc, const char* argv[]) 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); @@ -727,7 +750,11 @@ int main(int argc, const char* argv[]) // 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); @@ -745,7 +772,10 @@ void __assert_rtn(const char* func, const char* file, int line, const char* fail } 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 diff --git a/src/ld/ld.hpp b/src/ld/ld.hpp index b615046..c33bff6 100644 --- a/src/ld/ld.hpp +++ b/src/ld/ld.hpp @@ -60,21 +60,93 @@ public: 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; }; @@ -82,9 +154,11 @@ private: // 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 { // @@ -92,8 +166,6 @@ 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 @@ -118,10 +190,9 @@ namespace relocatable { 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(); } @@ -149,8 +220,8 @@ namespace dylib { 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), @@ -163,6 +234,7 @@ namespace dylib { 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; } @@ -185,6 +257,7 @@ namespace dylib { 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; @@ -210,8 +283,8 @@ namespace archive { 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; }; @@ -325,6 +398,9 @@ struct Fixup 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 @@ -579,7 +655,9 @@ public: 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; @@ -591,6 +669,13 @@ public: 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; } @@ -681,7 +766,7 @@ public: objcObjectConstraint(ld::File::objcConstraintNone), objcDylibConstraint(ld::File::objcConstraintNone), cpuSubType(0), - allObjectFilesScatterable(true), hasObjcReplacementClasses(false), + allObjectFilesScatterable(true), someObjectFileHasDwarf(false), usingHugeSections(false) { } std::vector sections; @@ -697,7 +782,6 @@ public: ld::File::ObjcConstraint objcDylibConstraint; uint32_t cpuSubType; bool allObjectFilesScatterable; - bool hasObjcReplacementClasses; bool someObjectFileHasDwarf; bool usingHugeSections; }; diff --git a/src/ld/parsers/archive_file.cpp b/src/ld/parsers/archive_file.cpp index cf0a058..78d8912 100644 --- a/src/ld/parsers/archive_file.cpp +++ b/src/ld/parsers/archive_file.cpp @@ -62,7 +62,7 @@ public: return File::validFile(fileContent, fileLength, opts); } static File* 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(fileContent, fileLength, path, mTime, ordinal, opts); } @@ -77,7 +77,7 @@ public: 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 @@ -98,7 +98,7 @@ private: 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; @@ -109,6 +109,9 @@ private: }; + 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: @@ -119,8 +122,6 @@ private: typedef typename A::P P; typedef typename A::P::E E; - struct MemberState { ld::relocatable::File* file; bool logged; bool loaded; }; - typedef std::map MemberToStateMap; const struct ranlib* ranlibHashSearch(const char* name) const; @@ -161,23 +162,21 @@ unsigned int File::Entry::getLongNameSpace() const } template -const char* File::Entry::name() const +void File::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; } } @@ -256,7 +255,8 @@ bool File::validFile(const uint8_t* fileContent, uint64_t fileLength, const m 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; @@ -270,7 +270,7 @@ bool File::validFile(const uint8_t* fileContent, uint64_t fileLength, const m template File::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), @@ -283,7 +283,9 @@ File::File(const uint8_t fileContent[], uint64_t fileLength, const char* pth, 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]; @@ -308,7 +310,8 @@ bool File::memberHasObjCCategories(const Entry* member) const } else { // i386 uses ObjC1 ABI which has .objc_category* global symbols - return false; + // strip -S on i386 pulls out .objc_category_name symbols from static frameworks + return mach_o::relocatable::hasObjC1Categories(member->content()); } } @@ -325,12 +328,37 @@ bool File::memberHasObjCCategories(const Entry* member) const template typename File::MemberState& File::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, "("); @@ -344,23 +372,22 @@ typename File::MemberState& File::makeObjectFileForMember(const Entry* mem 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]; } @@ -373,6 +400,25 @@ typename File::MemberState& File::makeObjectFileForMember(const Entry* mem } +template +bool File::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 bool File::forEachAtom(ld::File::AtomHandler& handler) const { @@ -382,19 +428,12 @@ bool File::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 ) { @@ -403,33 +442,28 @@ bool File::forEachAtom(ld::File::AtomHandler& handler) const 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); } } } @@ -449,15 +483,9 @@ bool File::justInTimeforEachAtom(const char* name, ld::File::AtomHandler& han 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; @@ -499,12 +527,9 @@ bool File::justInTimeDataOnlyforEachAtom(const char* name, ld::File::AtomHand 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); } } } @@ -558,21 +583,27 @@ void File::dumpTableOfContents() // 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::validFile(fileContent, fileLength, opts.objOpts) ) return archive::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); break; +#endif +#if SUPPORT_ARCH_i386 case CPU_TYPE_I386: if ( archive::Parser::validFile(fileContent, fileLength, opts.objOpts) ) return archive::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); break; +#endif +#if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: if ( archive::Parser::validFile(fileContent, fileLength, opts.objOpts) ) return archive::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); break; +#endif } return NULL; } diff --git a/src/ld/parsers/archive_file.h b/src/ld/parsers/archive_file.h index 31bab29..1ed411a 100644 --- a/src/ld/parsers/archive_file.h +++ b/src/ld/parsers/archive_file.h @@ -41,7 +41,7 @@ struct ParserOptions { }; 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 diff --git a/src/ld/parsers/lto_file.cpp b/src/ld/parsers/lto_file.cpp index 3a11c18..0bc1831 100644 --- a/src/ld/parsers/lto_file.cpp +++ b/src/ld/parsers/lto_file.cpp @@ -91,7 +91,7 @@ class File : public ld::relocatable::File { 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 @@ -101,7 +101,6 @@ public: 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 @@ -199,11 +198,10 @@ public: 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& allAtoms, ld::Internal& state, - uint32_t nextInputOrdinal, const OptimizeOptions& options, ld::File::AtomHandler& handler, std::vector& newAtoms, @@ -213,7 +211,7 @@ public: 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 { @@ -246,17 +244,17 @@ std::vector Parser::_s_files; 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 ) { + // 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; } @@ -264,18 +262,17 @@ bool Parser::validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type 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"; } @@ -283,9 +280,9 @@ const char* Parser::fileKind(const uint8_t* p, uint64_t fileLength) } 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); @@ -293,7 +290,7 @@ File* Parser::parse(const uint8_t* fileContent, uint64_t fileLength, const char* } -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; @@ -312,7 +309,7 @@ ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, uint 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"; @@ -320,8 +317,8 @@ ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, uint -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), @@ -399,7 +396,7 @@ File::File(const char* pth, time_t mTime, const uint8_t* content, uint32_t conte 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); } @@ -458,7 +455,6 @@ void Atom::setCompiledAtom(const ld::Atom& atom) bool Parser::optimize( const std::vector& allAtoms, ld::Internal& state, - uint32_t nextInputOrdinal, const OptimizeOptions& options, ld::File::AtomHandler& handler, std::vector& newAtoms, @@ -577,7 +573,7 @@ bool Parser::optimize( const std::vector& allAtoms, // 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); } @@ -680,7 +676,7 @@ bool Parser::optimize( const std::vector& allAtoms, } // 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 ) { @@ -806,12 +802,20 @@ void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom) } +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); } @@ -820,11 +824,12 @@ bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t ar // 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; } @@ -834,6 +839,7 @@ ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, // const char* version() { + Mutex lock; return ::lto_get_version(); } @@ -843,6 +849,7 @@ const char* version() // bool libLTOisLoaded() { + Mutex lock; return (::lto_get_version() != NULL); } @@ -851,6 +858,7 @@ bool libLTOisLoaded() // const char* archName(const uint8_t* fileContent, uint64_t fileLength) { + Mutex lock; return Parser::fileKind(fileContent, fileLength); } @@ -859,13 +867,13 @@ const char* archName(const uint8_t* fileContent, uint64_t fileLength) // bool optimize( const std::vector& allAtoms, ld::Internal& state, - uint32_t nextInputOrdinal, const OptimizeOptions& options, ld::File::AtomHandler& handler, std::vector& newAtoms, std::vector& additionalUndefines) { - return Parser::optimize(allAtoms, state, nextInputOrdinal, options, handler, newAtoms, additionalUndefines); + Mutex lock; + return Parser::optimize(allAtoms, state, options, handler, newAtoms, additionalUndefines); } diff --git a/src/ld/parsers/lto_file.h b/src/ld/parsers/lto_file.h index dbb82a5..492d32c 100644 --- a/src/ld/parsers/lto_file.h +++ b/src/ld/parsers/lto_file.h @@ -38,13 +38,13 @@ extern const char* archName(const uint8_t* fileContent, uint64_t fileLength); 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; @@ -59,7 +59,6 @@ struct OptimizeOptions { extern bool optimize( const std::vector& allAtoms, ld::Internal& state, - uint32_t nextInputOrdinal, const OptimizeOptions& options, ld::File::AtomHandler& handler, std::vector& newAtoms, diff --git a/src/ld/parsers/macho_dylib_file.cpp b/src/ld/parsers/macho_dylib_file.cpp index 39cebfb..11c03ac 100644 --- a/src/ld/parsers/macho_dylib_file.cpp +++ b/src/ld/parsers/macho_dylib_file.cpp @@ -40,7 +40,7 @@ #include "MachOFileAbstraction.hpp" #include "MachOTrie.hpp" #include "macho_dylib_file.h" - +#include "../code-sign-blobs/superblob.h" namespace mach_o { namespace dylib { @@ -144,7 +144,7 @@ class File : public ld::dylib::File 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); @@ -165,6 +165,7 @@ public: 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: @@ -218,6 +219,7 @@ private: NameSet _ignoreExports; const char* _parentUmbrella; ImportAtom* _importAtom; + const void* _codeSignatureDR; bool _noRexports; bool _hasWeakExports; bool _deadStrippable; @@ -240,7 +242,7 @@ template <> const char* File::objCInfoSectionName() { return "__objc_imagei template const char* File::objCInfoSectionName() { return "__image_info"; } template -File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, time_t mTime, uint32_t ord, +File::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) @@ -250,7 +252,8 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, _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) { @@ -281,6 +284,7 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, // pass 1: get pointers, and see if this dylib uses compressed LINKEDIT format const macho_dysymtab_command

* dynamicInfo = NULL; const macho_dyld_info_command

* dyldInfo = NULL; + const macho_linkedit_data_command

* codeSignature = NULL; const macho_nlist

* symbolTable = NULL; const char* strings = NULL; bool compressedLinkEdit = false; @@ -294,6 +298,8 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, symtab = (macho_symtab_command

*)cmd; symbolTable = (const macho_nlist

*)((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

*)cmd; @@ -333,6 +339,9 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, 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

* )cmd; + break; case macho_segment_command

::CMD: // check for Objective-C info if ( strcmp(((macho_segment_command

*)cmd)->segname(), objCInfoSegmentName()) == 0 ) { @@ -460,6 +469,25 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, _importAtom = new ImportAtom(*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 EmbeddedSignatureBlob; + typedef Security::SuperBlob InternalRequirementsBlob; + const EmbeddedSignatureBlob* signature = EmbeddedSignatureBlob::specific(overallSignature); + if ( signature->validateBlob(codeSignature->datasize()) ) { + const InternalRequirementsBlob* ireq = signature->find(Security::cdRequirementsSlot); + if ( (ireq != NULL) && ireq->validateBlob() ) { + const Security::BlobCore* dr = ireq->find(Security::kSecDesignatedRequirementType); + if ( (dr != NULL) && dr->validateBlob(Security::kSecCodeMagicRequirement) ) { + // 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); @@ -577,6 +605,10 @@ void File::addSymbol(const char* name, bool weakDef, bool tlv, pint_t address 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()); } @@ -838,8 +870,8 @@ public: 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(fileContent, fileLength, path, mTime, + ld::File::Ordinal ordinal, const Options& opts, bool indirectDylib) { + return new File(fileContent, fileLength, path, mTime, ordinal, opts.flatNamespace(), opts.linkingMainExecutable(), opts.implicitlyLinkIndirectPublicDylibs(), @@ -942,22 +974,28 @@ bool Parser::validFile(const uint8_t* fileContent, bool executableOrDylibor // 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::validFile(fileContent, bundleLoader) ) return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); break; +#endif +#if SUPPORT_ARCH_i386 case CPU_TYPE_I386: if ( Parser::validFile(fileContent, bundleLoader) ) return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); break; +#endif +#if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: if ( Parser::validFile(fileContent, bundleLoader) ) return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); break; +#endif } return NULL; } diff --git a/src/ld/parsers/macho_dylib_file.h b/src/ld/parsers/macho_dylib_file.h index d26f50e..ad03af1 100644 --- a/src/ld/parsers/macho_dylib_file.h +++ b/src/ld/parsers/macho_dylib_file.h @@ -32,7 +32,7 @@ namespace mach_o { 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 diff --git a/src/ld/parsers/macho_relocatable_file.cpp b/src/ld/parsers/macho_relocatable_file.cpp index e79d261..50f5327 100644 --- a/src/ld/parsers/macho_relocatable_file.cpp +++ b/src/ld/parsers/macho_relocatable_file.cpp @@ -70,7 +70,7 @@ template 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), @@ -80,7 +80,7 @@ public: _dwarfDebugLineSect(NULL), _dwarfDebugStringSect(NULL), _objConstraint(ld::File::objcConstraintNone), _cpuSubType(0), - _ojcReplacmentClass(false), _canScatterAtoms(false) {} + _canScatterAtoms(false) {} virtual ~File(); // overrides of ld::File @@ -89,7 +89,6 @@ public: { 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; } @@ -124,7 +123,6 @@ private: const macho_section

* _dwarfDebugStringSect; ld::File::ObjcConstraint _objConstraint; uint32_t _cpuSubType; - bool _ojcReplacmentClass; bool _canScatterAtoms; }; @@ -861,8 +859,9 @@ public: 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); @@ -972,6 +971,7 @@ public: 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); @@ -1048,7 +1048,7 @@ private: 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(); @@ -1075,7 +1075,7 @@ private: uint32_t _fileLength; const char* _path; time_t _modTime; - uint32_t _ordinal; + ld::File::Ordinal _ordinal; // filled in by parseLoadCommands() File* _file; @@ -1102,6 +1102,7 @@ private: bool _AppleObjc; // FSF has objc that uses different data layout bool _overlappingSymbols; bool _convertUnwindInfo; + bool _hasDataInCodeLabels; unsigned int _stubsSectionNum; const macho_section

* _stubsMachOSection; std::vector _dtraceProviderInfo; @@ -1112,7 +1113,7 @@ private: template Parser::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), @@ -1122,7 +1123,7 @@ Parser::Parser(const uint8_t* fileContent, uint64_t fileLength, const char* p _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) { } @@ -1207,9 +1208,9 @@ const char* Parser::fileKind(const uint8_t* fileContent) 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???"; @@ -1244,6 +1245,35 @@ bool Parser::hasObjC2Categories(const uint8_t* fileContent) return false; } + +template +bool Parser::hasObjC1Categories(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

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

* const cmds = (macho_load_command

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

)); + const macho_load_command

* const cmdsEnd = (macho_load_command

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

) + header->sizeofcmds()); + const macho_load_command

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

::CMD ) { + const macho_segment_command

* segment = (macho_segment_command

*)cmd; + const macho_section

* sectionsStart = (macho_section

*)((char*)segment + sizeof(macho_segment_command

)); + for (uint32_t si=0; si < segment->nsects(); ++si) { + const macho_section

* sect = §ionsStart[si]; + if ( (sect->size() > 0) + && (strcmp(sect->sectname(), "__category") == 0) + && (strcmp(sect->segname(), "__OBJC") == 0) ) { + return true; + } + } + } + cmd = (const macho_load_command

*)(((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 int Parser::pointerSorter(const void* l, const void* r) { @@ -1724,6 +1754,7 @@ void Parser::prescanSymbolTable() _tentativeDefinitionCount = 0; _absoluteSymbolCount = 0; _symbolsInSections = 0; + _hasDataInCodeLabels = false; for (uint32_t i=0; i < this->_symbolCount; ++i) { const macho_nlist

& sym = symbolFromIndex(i); // ignore stabs @@ -1769,9 +1800,12 @@ void Parser::prescanSymbolTable() continue; // 'L' labels do not denote atom breaks - if ( symbolName[0] == 'L' ) + if ( symbolName[0] == 'L' ) { + // 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"; @@ -1986,8 +2020,6 @@ void Parser::makeSections() _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::makeSectionName(sect), sect->size(), _file->path()); @@ -5532,6 +5564,7 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relocati +#if SUPPORT_ARCH_arm_any template <> bool Section::addRelocFixup(class Parser& parser, const macho_relocation_info

* reloc) { @@ -5984,6 +6017,7 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relocati } return result; } +#endif @@ -6116,6 +6150,46 @@ void Section::makeFixups(class Parser& parser, const struct Parser::CFI } } + // track data-in-code + if ( parser.hasDataInCodeLabels() && (this->type() == ld::Section::typeCode) ) { + for (uint32_t i=0; i < parser.symbolCount(); ++i) { + const macho_nlist

& 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* inAtom = parser.findAtomByAddress(sym.n_value()); + typename Parser::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* p = _beginAtoms; p < _endAtoms; ++p) { @@ -6136,21 +6210,27 @@ void Section::makeFixups(class Parser& parser, const struct Parser::CFI // 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::validFile(fileContent) ) return mach_o::relocatable::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); break; +#endif +#if SUPPORT_ARCH_i386 case CPU_TYPE_I386: if ( mach_o::relocatable::Parser::validFile(fileContent) ) return mach_o::relocatable::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); break; +#endif +#if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: if ( mach_o::relocatable::Parser::validFile(fileContent, opts.objSubtypeMustMatch, opts.subType) ) return mach_o::relocatable::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); break; +#endif } return NULL; } @@ -6229,6 +6309,17 @@ bool hasObjC2Categories(const uint8_t* fileContent) return false; } +// +// Used by archive reader when -ObjC option is specified +// +bool hasObjC1Categories(const uint8_t* fileContent) +{ + if ( mach_o::relocatable::Parser::validFile(fileContent, false, 0) ) { + return mach_o::relocatable::Parser::hasObjC1Categories(fileContent); + } + return false; +} + } // namespace relocatable diff --git a/src/ld/parsers/macho_relocatable_file.h b/src/ld/parsers/macho_relocatable_file.h index 6d0e25e..021c3f2 100644 --- a/src/ld/parsers/macho_relocatable_file.h +++ b/src/ld/parsers/macho_relocatable_file.h @@ -40,7 +40,7 @@ struct ParserOptions { }; 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); @@ -49,6 +49,8 @@ extern bool isObjectFile(const uint8_t* fileContent, cpu_type_t* result, cpu_sub extern bool hasObjC2Categories(const uint8_t* fileContent); +extern bool hasObjC1Categories(const uint8_t* fileContent); + extern const char* archName(const uint8_t* fileContent); } // namespace relocatable diff --git a/src/ld/parsers/opaque_section_file.cpp b/src/ld/parsers/opaque_section_file.cpp index 660f66d..4098958 100644 --- a/src/ld/parsers/opaque_section_file.cpp +++ b/src/ld/parsers/opaque_section_file.cpp @@ -61,9 +61,9 @@ class File : public ld::File { 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() { } @@ -90,10 +90,10 @@ Atom::Atom(File& f, const char* n, const uint8_t* content, uint64_t sz) // 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); } diff --git a/src/ld/parsers/opaque_section_file.h b/src/ld/parsers/opaque_section_file.h index 04db805..e105a07 100644 --- a/src/ld/parsers/opaque_section_file.h +++ b/src/ld/parsers/opaque_section_file.h @@ -32,7 +32,7 @@ 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"); diff --git a/src/ld/passes/branch_island.cpp b/src/ld/passes/branch_island.cpp index 773031e..06953a7 100644 --- a/src/ld/passes/branch_island.cpp +++ b/src/ld/passes/branch_island.cpp @@ -412,9 +412,45 @@ void doPass(const Options& opts, ld::Internal& state) 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 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::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::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 AtomToIsland; AtomToIsland* regionsMap[kIslandRegionsCount]; std::vector* regionsIslands[kIslandRegionsCount]; @@ -423,7 +459,6 @@ void doPass(const Options& opts, ld::Internal& state) regionsIslands[i] = new std::vector(); } 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::iterator ait=textSection->atoms.begin(); ait != textSection->atoms.end(); ++ait) { @@ -533,30 +568,25 @@ void doPass(const Options& opts, ld::Internal& state) if ( _s_log ) fprintf(stderr, "ld: %u branch islands required in %u regions\n", islandCount, kIslandRegionsCount); std::vector newAtomList; newAtomList.reserve(textSection->atoms.size()+islandCount); - uint64_t islandRegionAddr = kBetweenRegions;; - int regionIndex = 0; - for (std::vector::iterator it=textSection->atoms.begin(); it != textSection->atoms.end(); it++) { - const ld::Atom* atom = *it; - if ( (atom->sectionOffset()+atom->size()) > islandRegionAddr ) { - std::vector* regionIslands = regionsIslands[regionIndex]; - for (std::vector::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::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* regionIslands = regionsIslands[regionIndex]; for (std::vector::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(); diff --git a/src/ld/passes/branch_shim.cpp b/src/ld/passes/branch_shim.cpp index 7a2bd50..443f28e 100644 --- a/src/ld/passes/branch_shim.cpp +++ b/src/ld/passes/branch_shim.cpp @@ -328,7 +328,7 @@ void doPass(const Options& opts, ld::Internal& state) if ( pos == thumbToAtomMap.end() ) { if ( opts.archSupportsThumb2() ) { // 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); @@ -365,7 +365,7 @@ void doPass(const Options& opts, ld::Internal& state) std::map::iterator pos = atomToThumbMap.find(target); if ( pos == atomToThumbMap.end() ) { // 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); diff --git a/src/ld/passes/compact_unwind.cpp b/src/ld/passes/compact_unwind.cpp index 86c8eec..858b40f 100644 --- a/src/ld/passes/compact_unwind.cpp +++ b/src/ld/passes/compact_unwind.cpp @@ -774,12 +774,16 @@ static void makeFinalLinkedImageCompactUnwindSection(const Options& opts, ld::In // 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(entries, ehFrameSize)); break; +#endif +#if SUPPORT_ARCH_i386 case CPU_TYPE_I386: state.addAtom(*new UnwindInfoAtom(entries, ehFrameSize)); break; +#endif default: assert(0 && "no compact unwind for arch"); } @@ -876,12 +880,16 @@ static void makeCompactUnwindAtom(const Options& opts, ld::Internal& state, cons 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(state, atom, startOffset, endOffset-startOffset, cui)); break; +#endif +#if SUPPORT_ARCH_i386 case CPU_TYPE_I386: state.addAtom(*new CompactUnwindAtom(state, atom, startOffset, endOffset-startOffset, cui)); break; +#endif } } diff --git a/src/ld/passes/dtrace_dof.cpp b/src/ld/passes/dtrace_dof.cpp index 665e3ae..02055f3 100644 --- a/src/ld/passes/dtrace_dof.cpp +++ b/src/ld/passes/dtrace_dof.cpp @@ -77,9 +77,9 @@ class File : public ld::File { 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() {} @@ -299,7 +299,7 @@ void doPass(const Options& opts, ld::Internal& internal) 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); } diff --git a/src/ld/passes/objc.cpp b/src/ld/passes/objc.cpp index b209b1d..cf6f1d4 100644 --- a/src/ld/passes/objc.cpp +++ b/src/ld/passes/objc.cpp @@ -50,7 +50,6 @@ struct objc_image_info { 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) @@ -65,7 +64,7 @@ template 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 @@ -91,15 +90,13 @@ template ld::Section ObjCImageInfoAtom::_s_sectionABI2("__DATA", template ObjCImageInfoAtom::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: @@ -143,7 +140,7 @@ public: 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]; } @@ -775,6 +772,35 @@ private: const std::set& _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 &atoms) { + std::sort(atoms.begin(), atoms.end(), AtomSorter()); + } + template void OptimizeCategories::doit(const Options& opts, ld::Internal& state) { @@ -798,6 +824,7 @@ void OptimizeCategories::doit(const Options& opts, ld::Internal& state) // build map of all classes in this image that have categories on them typedef std::map*> CatMap; CatMap classToCategories; + std::vector classOrder; std::set deadAtoms; ld::Internal::FinalSection* methodListSection = NULL; for (std::vector::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { @@ -813,7 +840,7 @@ void OptimizeCategories::doit(const Options& opts, ld::Internal& state) continue; } assert(categoryAtom != NULL); - assert(categoryAtom->size() == Category::size()); + assert(categoryAtom->size() >= Category::size()); // ignore categories also in __objc_nlcatlist if ( nlcatListAtoms.count(categoryAtom) != 0 ) continue; @@ -824,6 +851,7 @@ void OptimizeCategories::doit(const Options& opts, ld::Internal& state) CatMap::iterator pos = classToCategories.find(categoryOnClassAtom); if ( pos == classToCategories.end() ) { classToCategories[categoryOnClassAtom] = new std::vector(); + classOrder.push_back(categoryOnClassAtom); } classToCategories[categoryOnClassAtom]->push_back(categoryAtom); // mark category atom and catlist atom as dead @@ -840,10 +868,11 @@ void OptimizeCategories::doit(const Options& opts, ld::Internal& state) // 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* categories = it->second; + for (std::vector::iterator it = classOrder.begin(); it != classOrder.end(); it++) { + const ld::Atom* classAtom = *it; + const std::vector* categories = classToCategories[classAtom]; assert(categories->size() != 0); // if any category adds instance methods, generate new merged method list, and replace if ( OptimizeCategories::hasInstanceMethods(state, categories) ) { @@ -1145,17 +1174,21 @@ void doPass(const Options& opts, ld::Internal& state) // add image info atom switch ( opts.architecture() ) { +#if SUPPORT_ARCH_x86_64 case CPU_TYPE_X86_64: state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, - state.hasObjcReplacementClasses, true)); + true)); break; +#endif +#if SUPPORT_ARCH_i386 case CPU_TYPE_I386: state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, - state.hasObjcReplacementClasses, opts.objCABIVersion2POverride() ? true : false)); + opts.objCABIVersion2POverride() ? true : false)); break; +#endif case CPU_TYPE_ARM: state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, - state.hasObjcReplacementClasses, true)); + true)); break; default: assert(0 && "unknown objc arch"); @@ -1165,18 +1198,24 @@ void doPass(const Options& opts, ld::Internal& state) 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::doit(opts, state); break; +#endif +#if SUPPORT_ARCH_i386 case CPU_TYPE_I386: // disable optimization until fully tested - //if ( opts.objCABIVersion2POverride() ) - // OptimizeCategories::doit(opts, state); + if ( opts.objCABIVersion2POverride() ) + OptimizeCategories::doit(opts, state); break; +#endif +#if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: // disable optimization until fully tested - //OptimizeCategories::doit(opts, state); + OptimizeCategories::doit(opts, state); break; +#endif default: assert(0 && "unknown objc arch"); } diff --git a/src/ld/passes/order.cpp b/src/ld/passes/order.cpp index ce17a35..6876d8f 100644 --- a/src/ld/passes/order.cpp +++ b/src/ld/passes/order.cpp @@ -193,8 +193,9 @@ bool Layout::Comparer::operator()(const ld::Atom* left, const ld::Atom* right) // 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; + // 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; diff --git a/src/ld/passes/stubs/stub_arm.hpp b/src/ld/passes/stubs/stub_arm.hpp index e8dedda..4650ddb 100644 --- a/src/ld/passes/stubs/stub_arm.hpp +++ b/src/ld/passes/stubs/stub_arm.hpp @@ -287,6 +287,95 @@ ld::Section LazyPointerAtom::_s_section("__DATA", "__la_symbol_ptr", ld::Section 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: diff --git a/src/ld/passes/stubs/stub_x86_64.hpp b/src/ld/passes/stubs/stub_x86_64.hpp index e74b37d..e3cebca 100644 --- a/src/ld/passes/stubs/stub_x86_64.hpp +++ b/src/ld/passes/stubs/stub_x86_64.hpp @@ -1,6 +1,6 @@ /* -*- 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@ * @@ -362,5 +362,78 @@ ld::Section StubAtom::_s_section("__TEXT", "__stubs", ld::Section::typeStub); + +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 diff --git a/src/ld/passes/stubs/stubs.cpp b/src/ld/passes/stubs/stubs.cpp index aa4aaf1..5ea1b05 100644 --- a/src/ld/passes/stubs/stubs.cpp +++ b/src/ld/passes/stubs/stubs.cpp @@ -36,6 +36,7 @@ #include "Options.h" #include "ld.hpp" +#include "MachOFileAbstraction.hpp" #include "make_stubs.h" @@ -118,6 +119,7 @@ const ld::Atom* Pass::stubableFixup(const ld::Fixup* fixup, ld::Internal& state) 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; @@ -175,20 +177,31 @@ ld::Atom* Pass::makeStub(const ld::Atom& target, bool weakImport) } 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 ) @@ -203,6 +216,7 @@ ld::Atom* Pass::makeStub(const ld::Atom& target, bool weakImport) return new ld::passes::stubs::arm::classic::StubNoPICAtom(*this, target, forLazyDylib, weakImport); } break; +#endif } throw "unsupported arch for stub"; } @@ -226,7 +240,6 @@ void Pass::process(ld::Internal& state) 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: @@ -236,6 +249,12 @@ void Pass::process(ld::Internal& state) 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 @@ -302,6 +321,15 @@ void Pass::process(ld::Internal& state) } } } + + 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; @@ -310,7 +338,7 @@ void Pass::process(ld::Internal& state) return; // 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 @@ -357,6 +385,13 @@ void Pass::process(ld::Internal& state) } } + // 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::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; diff --git a/src/other/ObjectDump.cpp b/src/other/ObjectDump.cpp index 44d8f53..55e1068 100644 --- a/src/other/ObjectDump.cpp +++ b/src/other/ObjectDump.cpp @@ -830,7 +830,25 @@ void dumper::dumpFixup(const ld::Fixup* ref) 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; @@ -1110,12 +1128,12 @@ static ld::relocatable::File* createReader(const char* path) } } - 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; @@ -1183,24 +1201,20 @@ int main(int argc, const char* argv[]) sShowLineInfo = false; } else if ( strcmp(arg, "-arch") == 0 ) { - const char* arch = ++isubTypeName != 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* fInfo; const macho_linkedit_data_command

* fSharedRegionInfo; const macho_linkedit_data_command

* fFunctionStartsInfo; + const macho_linkedit_data_command

* fDataInCode; + const macho_linkedit_data_command

* fDRInfo; uint64_t fBaseAddress; const macho_dysymtab_command

* fDynamicSymbolTable; const macho_segment_command

* fFirstSegment; @@ -166,6 +173,7 @@ bool DyldInfoPrinter::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; @@ -184,6 +192,7 @@ bool DyldInfoPrinter::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; @@ -202,6 +211,7 @@ bool DyldInfoPrinter::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; @@ -220,6 +230,7 @@ bool DyldInfoPrinter::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; @@ -227,6 +238,7 @@ bool DyldInfoPrinter::validFile(const uint8_t* fileContent) return false; } +#if SUPPORT_ARCH_arm_any template <> bool DyldInfoPrinter::validFile(const uint8_t* fileContent) { @@ -238,18 +250,20 @@ bool DyldInfoPrinter::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 DyldInfoPrinter::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) { @@ -332,32 +346,26 @@ DyldInfoPrinter::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLen case LC_FUNCTION_STARTS: fFunctionStartsInfo = (macho_linkedit_data_command

*)cmd; break; + case LC_DATA_IN_CODE: + fDataInCode = (macho_linkedit_data_command

*)cmd; + break; + case LC_DYLIB_CODE_SIGN_DRS: + fDRInfo = (macho_linkedit_data_command

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

*)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(); @@ -400,6 +408,10 @@ DyldInfoPrinter::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLen printFunctionStartsInfo(); if ( printDylibs ) printDylibsInfo(); + if ( printDRs ) + printDRInfo(); + if ( printDataCode ) + printDataInCode(); } static uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end) @@ -432,7 +444,7 @@ static int64_t read_sleb128(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 @@ -1545,6 +1557,7 @@ void DyldInfoPrinter::printSharedRegionInfo() } } +#if SUPPORT_ARCH_arm_any template <> void DyldInfoPrinter::printFunctionStartLine(uint64_t addr) { @@ -1553,6 +1566,7 @@ void DyldInfoPrinter::printFunctionStartLine(uint64_t addr) else printf("0x%0llX %s\n", addr, symbolNameForAddress(addr)); } +#endif template void DyldInfoPrinter::printFunctionStartLine(uint64_t addr) @@ -1617,6 +1631,85 @@ void DyldInfoPrinter::printDylibsInfo() } } +template +void DyldInfoPrinter::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 DRListSuperBlob; + typedef Security::SuperBlob 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 +void DyldInfoPrinter::printDataInCode() +{ + if ( fDataInCode == NULL ) { + printf("no data-in-code info\n"); + } + else { + printf("offset length data-kind\n"); + const macho_data_in_code_entry

* start = (macho_data_in_code_entry

*)((uint8_t*)fHeader + fDataInCode->dataoff()); + const macho_data_in_code_entry

* end = (macho_data_in_code_entry

*)((uint8_t*)fHeader + fDataInCode->dataoff() + fDataInCode->datasize()); + for (const macho_data_in_code_entry

* 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::relocBase() @@ -1652,6 +1745,7 @@ x86_64::P::uint_t DyldInfoPrinter::relocBase() return fFirstWritableSegment->vmaddr(); } +#if SUPPORT_ARCH_arm_any template <> arm::P::uint_t DyldInfoPrinter::relocBase() { @@ -1660,6 +1754,7 @@ arm::P::uint_t DyldInfoPrinter::relocBase() else return fFirstSegment->vmaddr(); } +#endif template <> @@ -1700,6 +1795,7 @@ const char* DyldInfoPrinter::relocTypeName(uint8_t r_type) return "??"; } +#if SUPPORT_ARCH_arm_any template <> const char* DyldInfoPrinter::relocTypeName(uint8_t r_type) { @@ -1710,7 +1806,7 @@ const char* DyldInfoPrinter::relocTypeName(uint8_t r_type) else return "??"; } - +#endif template void DyldInfoPrinter::printRelocRebaseInfo() @@ -2001,12 +2097,14 @@ static void dump(const char* path) 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::validFile(p + offset) ) DyldInfoPrinter::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); } @@ -2025,9 +2123,11 @@ static void dump(const char* path) else if ( DyldInfoPrinter::validFile(p) ) { DyldInfoPrinter::make(p, length, path, false); } +#if SUPPORT_ARCH_arm_any else if ( DyldInfoPrinter::validFile(p) ) { DyldInfoPrinter::make(p, length, path, false); } +#endif else { throw "not a known file type"; } @@ -2041,6 +2141,7 @@ static void usage() { fprintf(stderr, "Usage: dyldinfo [-arch ] \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" @@ -2049,6 +2150,7 @@ static void usage() "\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" ); } @@ -2076,17 +2178,19 @@ int main(int argc, const char* argv[]) 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 ) { @@ -2122,6 +2226,12 @@ int main(int argc, const char* argv[]) 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); } diff --git a/src/other/machochecker.cpp b/src/other/machochecker.cpp index 54dc560..36a71fd 100644 --- a/src/other/machochecker.cpp +++ b/src/other/machochecker.cpp @@ -388,6 +388,7 @@ void MachOChecker::checkLoadCommands() // check that all load commands fit within the load command space file const macho_encryption_info_command

* encryption_info = NULL; const macho_thread_command

* threadInfo = NULL; + const macho_entry_point_command

* entryPoint = NULL; const uint8_t* const endOfFile = (uint8_t*)fHeader + fLength; const uint8_t* const endOfLoadCommands = (uint8_t*)fHeader + sizeof(macho_header

) + fHeader->sizeofcmds(); const uint32_t cmd_count = fHeader->ncmds(); @@ -424,8 +425,12 @@ void MachOChecker::checkLoadCommands() 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: @@ -439,6 +444,11 @@ void MachOChecker::checkLoadCommands() 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

*)cmd; + break; case LC_UNIXTHREAD: if ( fHeader->filetype() != MH_EXECUTE ) throw "LC_UNIXTHREAD can only be used in MH_EXECUTE file types"; @@ -590,7 +600,11 @@ void MachOChecker::checkLoadCommands() 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; @@ -607,7 +621,7 @@ void MachOChecker::checkLoadCommands() cmd = (const macho_load_command

*)(((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"; } } @@ -661,7 +675,7 @@ void MachOChecker::checkLoadCommands() 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

*)cmd; @@ -725,6 +739,32 @@ void MachOChecker::checkLoadCommands() throw "function starts data size not a multiple of pointer size"; } break; + case LC_DATA_IN_CODE: + { + const macho_linkedit_data_command

* info = (macho_linkedit_data_command

*)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

* info = (macho_linkedit_data_command

*)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

*)(((uint8_t*)cmd)+cmd->cmdsize()); } @@ -1039,6 +1079,7 @@ void MachOChecker::checkExternalReloation(const macho_relocation_info

// FIX: check r_symbol } +#if SUPPORT_ARCH_arm_any template <> void MachOChecker::checkExternalReloation(const macho_relocation_info

* reloc) { @@ -1054,6 +1095,7 @@ void MachOChecker::checkExternalReloation(const macho_relocation_info

* r throw "external relocation address not in writable segment"; // FIX: check r_symbol } +#endif template <> @@ -1109,6 +1151,7 @@ void MachOChecker::checkLocalReloation(const macho_relocation_info

* r throw "local relocation address not in writable segment"; } +#if SUPPORT_ARCH_arm_any template <> void MachOChecker::checkLocalReloation(const macho_relocation_info

* reloc) { @@ -1129,6 +1172,7 @@ void MachOChecker::checkLocalReloation(const macho_relocation_info

* relo throw "local relocation address not in writable segment"; } } +#endif template void MachOChecker::checkRelocations() @@ -1273,6 +1317,7 @@ bool MachOChecker::hasTextRelocInRange(pint_t rangeStart, pint_t rangeEnd) } } } + return false; } template @@ -1518,12 +1563,14 @@ static void check(const char* path) 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::validFile(p + offset) ) MachOChecker::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); } @@ -1541,9 +1588,11 @@ static void check(const char* path) else if ( MachOChecker::validFile(p) ) { MachOChecker::make(p, length, path); } +#if SUPPORT_ARCH_arm_any else if ( MachOChecker::validFile(p) ) { MachOChecker::make(p, length, path); } +#endif else { throw "not a known file type"; } diff --git a/src/other/rebase.cpp b/src/other/rebase.cpp index 2255789..d1e3194 100644 --- a/src/other/rebase.cpp +++ b/src/other/rebase.cpp @@ -674,6 +674,7 @@ void Rebaser::doLocalRelocation(const macho_relocation_info

* reloc) } } +#if SUPPORT_ARCH_arm_any template <> void Rebaser::doLocalRelocation(const macho_relocation_info

* reloc) { @@ -693,6 +694,7 @@ void Rebaser::doLocalRelocation(const macho_relocation_info

* reloc) } } } +#endif template void Rebaser::doLocalRelocation(const macho_relocation_info

* reloc) @@ -1008,27 +1010,18 @@ int main(int argc, const char* argv[]) 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(); diff --git a/unit-tests/bin/make-recursive.pl b/unit-tests/bin/make-recursive.pl index f860985..0d681ba 100755 --- a/unit-tests/bin/make-recursive.pl +++ b/unit-tests/bin/make-recursive.pl @@ -4,6 +4,7 @@ use strict; use Data::Dumper; use File::Find; use Cwd qw(realpath); +use English; my @args = @ARGV; @@ -31,6 +32,17 @@ my $keywords = '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) @@ -42,82 +54,117 @@ my $last_keyword = ''; 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, ") - { - &print_line("stdout", "$_"); - } - close(OUT) || die("$!"); - unlink("/tmp/unit-tests-stdout"); - - open(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, ") + { + print $_; + } + close(INFO) || die("$!"); + unlink("/tmp/unit-tests-info.$pid"); + + &print_line(*STDOUT, "exit", "$exit\n"); + + open(OUT, ") + { + &print_line(*STDOUT, "stdout", "$_"); + } + close(OUT) || die("$!"); + unlink("/tmp/unit-tests-stdout.$pid"); + + open(ERR, ") + { + &print_line(*STDOUT, "stderr", "$_"); + } + close(ERR) || die("$!"); + unlink("/tmp/unit-tests-stderr.$pid"); } - close(ERR) || die("$!"); - } - unlink("/tmp/unit-tests-stderr"); } + diff --git a/unit-tests/include/common.makefile b/unit-tests/include/common.makefile index adb5468..97864ca 100644 --- a/unit-tests/include/common.makefile +++ b/unit-tests/include/common.makefile @@ -8,7 +8,6 @@ ARCH ?= $(shell arch) # 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 @@ -56,21 +55,23 @@ ifeq ($(ARCH),ppc) 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) @@ -82,8 +83,8 @@ endif 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) @@ -98,8 +99,8 @@ ifeq ($(ARCH),thumb) 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) @@ -114,8 +115,8 @@ ifeq ($(ARCH),thumb2) 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) diff --git a/unit-tests/run-all-unit-tests b/unit-tests/run-all-unit-tests index c07c947..9285128 100755 --- a/unit-tests/run-all-unit-tests +++ b/unit-tests/run-all-unit-tests @@ -15,7 +15,7 @@ cd `echo "$0" | sed 's/run-all-unit-tests/test-cases/'` 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" diff --git a/unit-tests/test-cases/16-byte-alignment/Makefile b/unit-tests/test-cases/16-byte-alignment/Makefile index e178ddb..533e222 100644 --- a/unit-tests/test-cases/16-byte-alignment/Makefile +++ b/unit-tests/test-cases/16-byte-alignment/Makefile @@ -33,11 +33,11 @@ all: ${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: diff --git a/unit-tests/test-cases/archive-r-ObjC/Makefile b/unit-tests/test-cases/archive-r-ObjC/Makefile new file mode 100644 index 0000000..939d8a9 --- /dev/null +++ b/unit-tests/test-cases/archive-r-ObjC/Makefile @@ -0,0 +1,52 @@ +## +# 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 diff --git a/unit-tests/test-cases/archive-r-ObjC/bar.c b/unit-tests/test-cases/archive-r-ObjC/bar.c new file mode 100644 index 0000000..d9b468b --- /dev/null +++ b/unit-tests/test-cases/archive-r-ObjC/bar.c @@ -0,0 +1,2 @@ + +int bar() { return 0; } diff --git a/unit-tests/test-cases/archive-r-ObjC/baz.m b/unit-tests/test-cases/archive-r-ObjC/baz.m new file mode 100644 index 0000000..90ae0a1 --- /dev/null +++ b/unit-tests/test-cases/archive-r-ObjC/baz.m @@ -0,0 +1,8 @@ +#include + +@interface Baz : NSObject +@end + +@implementation Baz +@end + diff --git a/unit-tests/test-cases/archive-r-ObjC/cat.m b/unit-tests/test-cases/archive-r-ObjC/cat.m new file mode 100644 index 0000000..aaabfe3 --- /dev/null +++ b/unit-tests/test-cases/archive-r-ObjC/cat.m @@ -0,0 +1,16 @@ +#include + +@interface NSObject(Cat) +@end + +@implementation NSObject(Cat) +@end + +@interface NSObject(Dog) +@end + +@implementation NSObject(Dog) +@end + +void mycatfunc() {} + diff --git a/unit-tests/test-cases/archive-r-ObjC/foo.m b/unit-tests/test-cases/archive-r-ObjC/foo.m new file mode 100644 index 0000000..acba7a4 --- /dev/null +++ b/unit-tests/test-cases/archive-r-ObjC/foo.m @@ -0,0 +1,8 @@ +#include + +@interface Foo : NSObject +@end + +@implementation Foo +@end + diff --git a/unit-tests/test-cases/archive-r-ObjC/main.c b/unit-tests/test-cases/archive-r-ObjC/main.c new file mode 100644 index 0000000..dc2fbef --- /dev/null +++ b/unit-tests/test-cases/archive-r-ObjC/main.c @@ -0,0 +1,31 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + + +int main() +{ + fprintf(stdout, "hello\n"); + return 0; +} diff --git a/unit-tests/test-cases/branch-islands/Makefile b/unit-tests/test-cases/branch-islands/Makefile index 471e446..8e1870b 100644 --- a/unit-tests/test-cases/branch-islands/Makefile +++ b/unit-tests/test-cases/branch-islands/Makefile @@ -32,8 +32,12 @@ run: all 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 diff --git a/unit-tests/test-cases/branch-islands/atomic_space.s b/unit-tests/test-cases/branch-islands/atomic_space.s new file mode 100644 index 0000000..4402d66 --- /dev/null +++ b/unit-tests/test-cases/branch-islands/atomic_space.s @@ -0,0 +1,76 @@ + +#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 diff --git a/unit-tests/test-cases/data-in-code/Makefile b/unit-tests/test-cases/data-in-code/Makefile new file mode 100644 index 0000000..0897726 --- /dev/null +++ b/unit-tests/test-cases/data-in-code/Makefile @@ -0,0 +1,37 @@ +## +# 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 diff --git a/unit-tests/test-cases/data-in-code/main.c b/unit-tests/test-cases/data-in-code/main.c new file mode 100644 index 0000000..811449a --- /dev/null +++ b/unit-tests/test-cases/data-in-code/main.c @@ -0,0 +1,49 @@ + +#include + +#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; +} diff --git a/unit-tests/test-cases/data-in-code/test.s b/unit-tests/test-cases/data-in-code/test.s new file mode 100644 index 0000000..8dc92b8 --- /dev/null +++ b/unit-tests/test-cases/data-in-code/test.s @@ -0,0 +1,25 @@ + + .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 diff --git a/unit-tests/test-cases/duplicate_symbols/Makefile b/unit-tests/test-cases/duplicate_symbols/Makefile new file mode 100644 index 0000000..a16e69b --- /dev/null +++ b/unit-tests/test-cases/duplicate_symbols/Makefile @@ -0,0 +1,52 @@ +## +# 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 diff --git a/unit-tests/test-cases/duplicate_symbols/duplicates.c b/unit-tests/test-cases/duplicate_symbols/duplicates.c new file mode 100644 index 0000000..2a62979 --- /dev/null +++ b/unit-tests/test-cases/duplicate_symbols/duplicates.c @@ -0,0 +1,10 @@ +#include + +void a() { +} + +void b() { +} + +void c() { +} diff --git a/unit-tests/test-cases/duplicate_symbols/main_extern.c b/unit-tests/test-cases/duplicate_symbols/main_extern.c new file mode 100644 index 0000000..b78ea48 --- /dev/null +++ b/unit-tests/test-cases/duplicate_symbols/main_extern.c @@ -0,0 +1,16 @@ +/* +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(); +} diff --git a/unit-tests/test-cases/duplicate_symbols/main_no_extern.c b/unit-tests/test-cases/duplicate_symbols/main_no_extern.c new file mode 100644 index 0000000..cbdbeb6 --- /dev/null +++ b/unit-tests/test-cases/duplicate_symbols/main_no_extern.c @@ -0,0 +1,13 @@ +/* +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() { +} diff --git a/unit-tests/test-cases/weak_import3/Makefile b/unit-tests/test-cases/dylib-main/Makefile similarity index 72% rename from unit-tests/test-cases/weak_import3/Makefile rename to unit-tests/test-cases/dylib-main/Makefile index 2c8806d..f01ef41 100644 --- a/unit-tests/test-cases/weak_import3/Makefile +++ b/unit-tests/test-cases/dylib-main/Makefile @@ -1,15 +1,15 @@ ## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# 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, @@ -17,30 +17,22 @@ # 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 +# Test that a program can have is "main" in a dylib # - 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 +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 foo.o foo1.o libfoo.dylib - - -#2>/dev/null \ No newline at end of file + rm -rf libmain.dylib main diff --git a/unit-tests/test-cases/dylib-main/foo.c b/unit-tests/test-cases/dylib-main/foo.c new file mode 100644 index 0000000..85e6cd8 --- /dev/null +++ b/unit-tests/test-cases/dylib-main/foo.c @@ -0,0 +1 @@ +void foo() {} diff --git a/unit-tests/test-cases/dylib-main/main.c b/unit-tests/test-cases/dylib-main/main.c new file mode 100644 index 0000000..a444c65 --- /dev/null +++ b/unit-tests/test-cases/dylib-main/main.c @@ -0,0 +1,7 @@ +#include + +int main() +{ + fprintf(stdout, "hello\n"); + return 0; +} \ No newline at end of file diff --git a/unit-tests/test-cases/force-weak/Makefile b/unit-tests/test-cases/force-weak/Makefile new file mode 100644 index 0000000..4dbf4e9 --- /dev/null +++ b/unit-tests/test-cases/force-weak/Makefile @@ -0,0 +1,38 @@ +## +# 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 diff --git a/unit-tests/test-cases/force-weak/foo.c b/unit-tests/test-cases/force-weak/foo.c new file mode 100644 index 0000000..485aed6 --- /dev/null +++ b/unit-tests/test-cases/force-weak/foo.c @@ -0,0 +1,2 @@ + +int foo = 5; \ No newline at end of file diff --git a/unit-tests/test-cases/force-weak/test.c b/unit-tests/test-cases/force-weak/test.c new file mode 100644 index 0000000..bd0da12 --- /dev/null +++ b/unit-tests/test-cases/force-weak/test.c @@ -0,0 +1,5 @@ + +extern int foo; + +int getfoo() { return foo; } + diff --git a/unit-tests/test-cases/force-weak/weak.exp b/unit-tests/test-cases/force-weak/weak.exp new file mode 100644 index 0000000..33c5f68 --- /dev/null +++ b/unit-tests/test-cases/force-weak/weak.exp @@ -0,0 +1,2 @@ +_foo + diff --git a/unit-tests/test-cases/install-name-override/Makefile b/unit-tests/test-cases/install-name-override/Makefile new file mode 100644 index 0000000..d4cfe56 --- /dev/null +++ b/unit-tests/test-cases/install-name-override/Makefile @@ -0,0 +1,50 @@ +## +# 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 diff --git a/unit-tests/test-cases/install-name-override/foo.c b/unit-tests/test-cases/install-name-override/foo.c new file mode 100644 index 0000000..1a68260 --- /dev/null +++ b/unit-tests/test-cases/install-name-override/foo.c @@ -0,0 +1,18 @@ + +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 diff --git a/unit-tests/test-cases/install-name-override/main.c b/unit-tests/test-cases/install-name-override/main.c new file mode 100644 index 0000000..7f346c1 --- /dev/null +++ b/unit-tests/test-cases/install-name-override/main.c @@ -0,0 +1,9 @@ + +extern int foo(); + +int main() +{ + foo(); + return 0; +} + diff --git a/unit-tests/test-cases/llvm-integration/Makefile b/unit-tests/test-cases/llvm-integration/Makefile index 8f33f36..bd2150c 100644 --- a/unit-tests/test-cases/llvm-integration/Makefile +++ b/unit-tests/test-cases/llvm-integration/Makefile @@ -32,12 +32,6 @@ LLVMAR = /usr/local/bin/llvm-ar # # 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 \ diff --git a/unit-tests/test-cases/lto-dead_strip-inline-asm/Makefile b/unit-tests/test-cases/lto-dead_strip-inline-asm/Makefile new file mode 100644 index 0000000..35a0220 --- /dev/null +++ b/unit-tests/test-cases/lto-dead_strip-inline-asm/Makefile @@ -0,0 +1,41 @@ +## +# 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 + +# +# [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 + diff --git a/unit-tests/test-cases/lto-dead_strip-inline-asm/bar.c b/unit-tests/test-cases/lto-dead_strip-inline-asm/bar.c new file mode 100644 index 0000000..2b49dc1 --- /dev/null +++ b/unit-tests/test-cases/lto-dead_strip-inline-asm/bar.c @@ -0,0 +1,14 @@ + + +void qux() {} + +asm("\t.text\n" + "\t.globl _foo\n" + "_foo:\n" + "\tnop\n"); + +extern void foo(); + +void (*bar())() { + return foo; +} diff --git a/unit-tests/test-cases/lto-dead_strip-unused/Makefile b/unit-tests/test-cases/lto-dead_strip-unused/Makefile index 82192f8..79a193f 100644 --- a/unit-tests/test-cases/lto-dead_strip-unused/Makefile +++ b/unit-tests/test-cases/lto-dead_strip-unused/Makefile @@ -23,6 +23,7 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile +SHELL = bash # use bash shell so we can redirect just stderr # # LTO with 'dead code strip' can't ignore unused functions with undefined references @@ -36,6 +37,8 @@ all: ${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: diff --git a/unit-tests/test-cases/lto-preload-pie/Makefile b/unit-tests/test-cases/lto-preload-pie/Makefile index 063bc33..6f9cc2c 100644 --- a/unit-tests/test-cases/lto-preload-pie/Makefile +++ b/unit-tests/test-cases/lto-preload-pie/Makefile @@ -27,16 +27,13 @@ include ${TESTROOT}/include/common.makefile # verify -preload -pie produces relocations # -LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH} -LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH} - run: all all: - ${LLVMGCC} ${CCFLAGS} --emit-llvm a.c -c -o a.o - ${LLVMGCC} ${CCFLAGS} --emit-llvm b.c -c -o b.o - ${LLVMGCC} ${CCFLAGS} --emit-llvm main.c -c -o main.o - ${LLVMGCC} ${CCFLAGS} main.o a.o b.o -Wl,-preload -Wl,-pie -o main.preload \ + ${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} diff --git a/unit-tests/test-cases/no-uuid/Makefile b/unit-tests/test-cases/no-uuid/Makefile index 7c5304b..dcae189 100644 --- a/unit-tests/test-cases/no-uuid/Makefile +++ b/unit-tests/test-cases/no-uuid/Makefile @@ -35,29 +35,7 @@ all: ${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 diff --git a/unit-tests/test-cases/objc-category-archive/Makefile b/unit-tests/test-cases/objc-category-archive/Makefile index b56bacf..7bcd9a4 100644 --- a/unit-tests/test-cases/objc-category-archive/Makefile +++ b/unit-tests/test-cases/objc-category-archive/Makefile @@ -12,16 +12,19 @@ run: all 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 diff --git a/unit-tests/test-cases/objc-category-archive/test2.m b/unit-tests/test-cases/objc-category-archive/test2.m new file mode 100644 index 0000000..e1e5653 --- /dev/null +++ b/unit-tests/test-cases/objc-category-archive/test2.m @@ -0,0 +1,11 @@ + +#include + +@interface MyClass : NSObject +- (void) mymethod2; +@end + +@implementation MyClass +- (void) mymethod2 { } +@end + diff --git a/unit-tests/test-cases/objc-category-debug-notes/Makefile b/unit-tests/test-cases/objc-category-debug-notes/Makefile index 44023e6..9d719ee 100644 --- a/unit-tests/test-cases/objc-category-debug-notes/Makefile +++ b/unit-tests/test-cases/objc-category-debug-notes/Makefile @@ -32,7 +32,7 @@ include ${TESTROOT}/include/common.makefile 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 diff --git a/unit-tests/test-cases/objc-category-optimize-load/Makefile b/unit-tests/test-cases/objc-category-optimize-load/Makefile index 5652312..2bbc5dd 100644 --- a/unit-tests/test-cases/objc-category-optimize-load/Makefile +++ b/unit-tests/test-cases/objc-category-optimize-load/Makefile @@ -29,7 +29,7 @@ include ${TESTROOT}/include/common.makefile 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} @@ -40,6 +40,7 @@ all-ppc: all-i386: all-rest all-x86_64: all-rest all-armv6: all-rest +all-armv7: all-rest all-rest: # check optimzation of category methods diff --git a/unit-tests/test-cases/objc-category-optimize/Makefile b/unit-tests/test-cases/objc-category-optimize/Makefile index c452bec..c395988 100644 --- a/unit-tests/test-cases/objc-category-optimize/Makefile +++ b/unit-tests/test-cases/objc-category-optimize/Makefile @@ -29,7 +29,11 @@ include ${TESTROOT}/include/common.makefile 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} @@ -40,6 +44,7 @@ all-ppc: 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 diff --git a/unit-tests/test-cases/objc-category-warning/Makefile b/unit-tests/test-cases/objc-category-warning/Makefile index 95cc91d..5481ba0 100644 --- a/unit-tests/test-cases/objc-category-warning/Makefile +++ b/unit-tests/test-cases/objc-category-warning/Makefile @@ -30,7 +30,7 @@ SHELL = bash # use bash shell so we can redirect just stderr 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} @@ -52,4 +52,4 @@ all-rest: ${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 diff --git a/unit-tests/test-cases/order_file/Makefile b/unit-tests/test-cases/order_file/Makefile index 1aec6f1..3e88763 100644 --- a/unit-tests/test-cases/order_file/Makefile +++ b/unit-tests/test-cases/order_file/Makefile @@ -51,7 +51,7 @@ all: ${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 diff --git a/unit-tests/test-cases/pipelined-linking/Makefile b/unit-tests/test-cases/pipelined-linking/Makefile new file mode 100644 index 0000000..fe9197f --- /dev/null +++ b/unit-tests/test-cases/pipelined-linking/Makefile @@ -0,0 +1,74 @@ +## +# 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 + diff --git a/unit-tests/test-cases/pipelined-linking/bar.c b/unit-tests/test-cases/pipelined-linking/bar.c new file mode 100644 index 0000000..320c246 --- /dev/null +++ b/unit-tests/test-cases/pipelined-linking/bar.c @@ -0,0 +1,4 @@ +void bar() +{ +} + diff --git a/unit-tests/test-cases/pipelined-linking/cat.c b/unit-tests/test-cases/pipelined-linking/cat.c new file mode 100644 index 0000000..0e32166 --- /dev/null +++ b/unit-tests/test-cases/pipelined-linking/cat.c @@ -0,0 +1,4 @@ +void cat() +{ +} + diff --git a/unit-tests/test-cases/pipelined-linking/foo.c b/unit-tests/test-cases/pipelined-linking/foo.c new file mode 100644 index 0000000..6e0d320 --- /dev/null +++ b/unit-tests/test-cases/pipelined-linking/foo.c @@ -0,0 +1,10 @@ + +void foo() +{ +} + +int main() +{ +return 0; +} + diff --git a/unit-tests/test-cases/static-executable-pie/Makefile b/unit-tests/test-cases/static-executable-pie/Makefile new file mode 100644 index 0000000..5854b0f --- /dev/null +++ b/unit-tests/test-cases/static-executable-pie/Makefile @@ -0,0 +1,50 @@ +## +# 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 + + diff --git a/unit-tests/test-cases/static-executable-pie/bad.c b/unit-tests/test-cases/static-executable-pie/bad.c new file mode 100644 index 0000000..40f0614 --- /dev/null +++ b/unit-tests/test-cases/static-executable-pie/bad.c @@ -0,0 +1,9 @@ +static int my; + +int getmy() +{ +#if __x86_64__ + __asm(" .quad _my"); +#endif + return my; +} diff --git a/unit-tests/test-cases/static-executable-pie/test.c b/unit-tests/test-cases/static-executable-pie/test.c new file mode 100644 index 0000000..4d09a3a --- /dev/null +++ b/unit-tests/test-cases/static-executable-pie/test.c @@ -0,0 +1,19 @@ + +int a; +int b = 5; +int* pa = &a; +int* pb = &b; + +int foo() +{ + *pa = 4; + return a+b; +} + + +int entry() +{ + return foo(); +} + + diff --git a/unit-tests/test-cases/tentative-and-archive-code/Makefile b/unit-tests/test-cases/tentative-and-archive-code/Makefile index 7c2c28f..ba343cc 100644 --- a/unit-tests/test-cases/tentative-and-archive-code/Makefile +++ b/unit-tests/test-cases/tentative-and-archive-code/Makefile @@ -55,8 +55,8 @@ all: ${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: diff --git a/unit-tests/test-cases/tlv-dead_strip/Makefile b/unit-tests/test-cases/tlv-dead_strip/Makefile index 568ebc6..2793f3e 100644 --- a/unit-tests/test-cases/tlv-dead_strip/Makefile +++ b/unit-tests/test-cases/tlv-dead_strip/Makefile @@ -30,7 +30,7 @@ include ${TESTROOT}/include/common.makefile 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 diff --git a/unit-tests/test-cases/weak_import-undefined/Makefile b/unit-tests/test-cases/weak_import-undefined/Makefile new file mode 100644 index 0000000..56e888f --- /dev/null +++ b/unit-tests/test-cases/weak_import-undefined/Makefile @@ -0,0 +1,40 @@ +## +# 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 diff --git a/unit-tests/test-cases/weak_import-undefined/weak.c b/unit-tests/test-cases/weak_import-undefined/weak.c new file mode 100644 index 0000000..f8e103b --- /dev/null +++ b/unit-tests/test-cases/weak_import-undefined/weak.c @@ -0,0 +1,13 @@ +#include + +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; +} + diff --git a/unit-tests/test-cases/weak_import3/comment.txt b/unit-tests/test-cases/weak_import3/comment.txt deleted file mode 100644 index 5be42d8..0000000 --- a/unit-tests/test-cases/weak_import3/comment.txt +++ /dev/null @@ -1 +0,0 @@ -Test the weak_import attribute works diff --git a/unit-tests/test-cases/weak_import3/foo.c b/unit-tests/test-cases/weak_import3/foo.c deleted file mode 100644 index 900b052..0000000 --- a/unit-tests/test-cases/weak_import3/foo.c +++ /dev/null @@ -1,17 +0,0 @@ - - -#include "foo.h" - -void func1() {} -void func2() {} -void func3() {} -void func4() {} - - -int data1 = 0; -int data2 = 0; // weak_import initialized -int data3; -int data4; // weak_import uninitialized -int data5 = 0; -int data6 = 0; // weak_import - diff --git a/unit-tests/test-cases/weak_import3/foo.h b/unit-tests/test-cases/weak_import3/foo.h deleted file mode 100644 index f455515..0000000 --- a/unit-tests/test-cases/weak_import3/foo.h +++ /dev/null @@ -1,16 +0,0 @@ - - -extern void func1(); -extern void func2() __attribute__((weak_import)); -extern void func3(); -extern void func4() __attribute__((weak_import)); - -extern int data1; -extern int data2 __attribute__((weak_import)); -extern int data3; -extern int data4 __attribute__((weak_import)); -extern int data5; -extern int data6 __attribute__((weak_import)); - - - diff --git a/unit-tests/test-cases/weak_import3/foo1.c b/unit-tests/test-cases/weak_import3/foo1.c deleted file mode 100644 index 392a5b7..0000000 --- a/unit-tests/test-cases/weak_import3/foo1.c +++ /dev/null @@ -1,4 +0,0 @@ -#include "foo.h" - -int data4; // foo.c also has weak_import uninitialized - diff --git a/unit-tests/test-cases/weak_import3/main.c b/unit-tests/test-cases/weak_import3/main.c deleted file mode 100644 index 3266aed..0000000 --- a/unit-tests/test-cases/weak_import3/main.c +++ /dev/null @@ -1,20 +0,0 @@ - -#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; -} - -- 2.45.2