From afe874b1634377ecb27057ee76deb04915bb34d7 Mon Sep 17 00:00:00 2001 From: Apple Date: Mon, 15 Aug 2011 22:56:20 +0000 Subject: [PATCH] ld64-127.2.tar.gz --- ChangeLog | 405 +++- doc/man/man1/ld.1 | 10 +- ld64.xcodeproj/project.pbxproj | 23 +- src/abstraction/MachOFileAbstraction.hpp | 64 + src/ld/HeaderAndLoadCommands.hpp | 18 +- src/ld/InputFiles.cpp | 64 +- src/ld/InputFiles.h | 7 +- src/ld/LinkEdit.hpp | 68 +- src/ld/LinkEditClassic.hpp | 28 +- src/ld/Options.cpp | 300 +-- src/ld/Options.h | 18 +- src/ld/OutputFile.cpp | 312 ++- src/ld/OutputFile.h | 7 +- src/ld/Resolver.cpp | 76 +- src/ld/SymbolTable.cpp | 43 +- src/ld/debugline.c | 10 +- src/ld/dwarf2.h | 219 +++ src/ld/ld.cpp | 19 +- src/ld/ld.hpp | 53 +- src/ld/parsers/archive_file.cpp | 156 +- src/ld/parsers/archive_file.h | 5 +- src/ld/parsers/libunwind/AddressSpace.hpp | 439 +++++ .../parsers/libunwind/DwarfInstructions.hpp | 1726 +++++++++++++++++ src/ld/parsers/libunwind/DwarfParser.hpp | 819 ++++++++ src/ld/parsers/libunwind/InternalMacros.h | 105 + src/ld/parsers/libunwind/Registers.hpp | 1050 ++++++++++ src/ld/parsers/lto_file.cpp | 19 +- src/ld/parsers/macho_dylib_file.cpp | 105 +- src/ld/parsers/macho_dylib_file.h | 5 +- src/ld/parsers/macho_relocatable_file.cpp | 617 ++++-- src/ld/passes/branch_island.cpp | 6 +- src/ld/passes/branch_shim.cpp | 258 ++- src/ld/passes/compact_unwind.cpp | 155 +- src/ld/passes/dylibs.cpp | 55 +- src/ld/passes/got.cpp | 23 +- src/ld/passes/objc.cpp | 32 +- src/ld/passes/order_file.cpp | 21 +- src/ld/passes/stubs/stubs.cpp | 40 +- src/ld/passes/tlvp.cpp | 8 - src/other/ObjectDump.cpp | 93 +- src/other/dyldinfo.cpp | 176 +- src/other/machochecker.cpp | 470 ++++- src/other/rebase.cpp | 18 +- src/other/unwinddump.cpp | 224 ++- unit-tests/include/common.makefile | 52 +- unit-tests/run-all-unit-tests | 12 +- .../test-cases/absolute-symbol/Makefile | 6 +- unit-tests/test-cases/absolute-symbol/abs.s | 2 +- .../test-cases/alias-command-line/aliases.s | 3 +- unit-tests/test-cases/alias-objects/aliases.s | 3 +- .../test-cases/archive-image_info/Makefile | 2 +- unit-tests/test-cases/archive-order/Makefile | 47 + unit-tests/test-cases/archive-order/bar.c | 1 + unit-tests/test-cases/archive-order/bar2.c | 1 + unit-tests/test-cases/archive-order/bar3.c | 1 + .../test-cases/archive-order/expected.order | 6 + unit-tests/test-cases/archive-order/foo.c | 1 + unit-tests/test-cases/archive-order/foo2.c | 1 + unit-tests/test-cases/archive-order/foo3.c | 1 + unit-tests/test-cases/archive-order/main.c | 41 + unit-tests/test-cases/auto-arch/Makefile | 12 +- unit-tests/test-cases/auto-arch/hello.c | 1 + unit-tests/test-cases/bind_at_load/Makefile | 4 +- unit-tests/test-cases/branch-distance/bar.s | 2 + unit-tests/test-cases/branch-distance/foo.s | 3 +- .../test-cases/branch-interworking/Makefile | 1 + .../test-cases/branch-interworking/myarm.s | 1 + .../test-cases/branch-interworking/mythumb.s | 6 + unit-tests/test-cases/branch-islands/hello.c | 1 + .../test-cases/check-init-abs/.mod_init_func | 2 + unit-tests/test-cases/check-init-abs/Makefile | 21 + unit-tests/test-cases/check-init-abs/init.s | 10 + .../{prebound-main => check-init-abs}/main.c | 1 - unit-tests/test-cases/check-init-abs/term.s | 10 + .../test-cases/check-init-bind/.mod_init_func | 2 + .../test-cases/check-init-bind/Makefile | 21 + unit-tests/test-cases/check-init-bind/init.s | 10 + unit-tests/test-cases/check-init-bind/main.c | 2 + unit-tests/test-cases/check-init-bind/term.s | 10 + .../check-init-no-rebase/.mod_init_func | 2 + .../test-cases/check-init-no-rebase/Makefile | 21 + .../test-cases/check-init-no-rebase/init.s | 10 + .../test-cases/check-init-no-rebase/main.c | 2 + .../test-cases/check-init-no-rebase/term.s | 10 + .../coalesce_weak_def_in_dylib/Makefile | 6 +- .../test-cases/commons-alignment/Makefile | 7 +- unit-tests/test-cases/commons-alignment/foo.s | 1 + .../test-cases/compact-unwind-basic/Makefile | 47 + .../test-cases/compact-unwind-basic/test.s | 99 + .../cpu-sub-types-preference/Makefile | 23 +- unit-tests/test-cases/cpu-sub-types/Makefile | 66 +- unit-tests/test-cases/cstring-labels/Makefile | 7 +- .../test-cases/custom-segment-layout/Makefile | 7 +- .../test-cases/custom-segment-layout/zero.s | 6 +- .../test-cases/dead_strip-archive/Makefile | 2 +- .../dead_strip-entry-archive/Makefile | 4 +- unit-tests/test-cases/demangle/Makefile | 4 +- .../test-cases/dependency-logging/Makefile | 8 +- .../test-cases/dwarf-debug-notes/Makefile | 6 +- .../dwarf-debug-notes/expected-stabs | 2 +- .../test-cases/dwarf-debug-notes/hello.order | 16 + .../test-cases/dwarf-debug-notes/other.cxx | 8 +- unit-tests/test-cases/dwarf-ignore/hello.c | 1 + .../test-cases/dwarf-strip-objc/Makefile | 2 +- unit-tests/test-cases/dwarf-strip/hello.c | 1 + .../test-cases/dylib-re-export-cycle/Makefile | 13 +- unit-tests/test-cases/efi-basic/Makefile | 2 +- unit-tests/test-cases/efi-basic/MtocTest.c | 2 +- .../test-cases/eh-stripped-symbols/Makefile | 15 +- unit-tests/test-cases/filelist/hello.c | 1 + unit-tests/test-cases/flat-dylib/main.c | 1 + unit-tests/test-cases/flat-main/main.c | 1 + .../test-cases/function-starts/Makefile | 9 +- unit-tests/test-cases/function-starts/main.c | 4 + .../test-cases/got-elimination/Makefile | 4 +- unit-tests/test-cases/header-pad/hello.c | 1 + unit-tests/test-cases/hello-world/hello.c | 1 + unit-tests/test-cases/implicit_dylib/Makefile | 2 +- unit-tests/test-cases/kext-basic/Makefile | 14 +- .../test-cases/kext-undefined-export/Makefile | 35 + .../kext-undefined-export/mykext-i386.exp | 1 + .../test-cases/kext-undefined-export/mykext.c | 27 + .../kext-undefined-export/mykext.exp | 3 + .../kext-undefined-export/mykextinfo.c | 12 + .../label-on-end-of-section-order/Makefile | 37 + .../label-on-end-of-section-order/foo.s | 24 + .../test-cases/llvm-integration/Makefile | 136 +- .../test-cases/lto-archive-dylib/Makefile | 7 +- .../lto-dead_strip-all-hidden/Makefile | 6 +- .../lto-dead_strip-coalesce/Makefile | 39 + .../test-cases/lto-dead_strip-coalesce/foo.c | 21 + .../test-cases/lto-dead_strip-coalesce/main.c | 34 + .../test-cases/lto-dead_strip-objc/Makefile | 6 +- .../lto-dead_strip-some-hidden/Makefile | 6 +- .../lto-dead_strip-tentative/Makefile | 12 +- .../test-cases/lto-dead_strip-unused/Makefile | 9 +- .../test-cases/lto-llvm-options/Makefile | 9 +- .../test-cases/lto-objc-archive/Makefile | 6 +- .../test-cases/lto-objc-image-info/Makefile | 9 +- .../test-cases/lto-object_path/Makefile | 5 +- .../lto-weak-native-override/Makefile | 7 +- .../lto-weak-native-override/main.c | 2 +- .../test-cases/lto-weak_import/Makefile | 7 +- .../merge_zero_fill_sections/Makefile | 40 + .../merge_zero_fill_sections/main.c | 41 + .../test-cases/non-lazy-sections-r/foo.s | 1 + .../test-cases/objc-category-warning/Makefile | 55 + .../test-cases/objc-category-warning/cat.m | 24 + .../objc-category-warning/copycat.m | 16 + .../test-cases/objc-category-warning/foo.m | 13 + .../test-cases/objc-class-alias/Makefile | 3 + unit-tests/test-cases/objc-gc-checks/Makefile | 11 +- .../test-cases/objc-properties/Makefile | 1 - unit-tests/test-cases/order_file/extra.s | 1 + unit-tests/test-cases/prebound-main/Makefile | 31 - .../test-cases/prebound-split-seg/Makefile | 17 +- .../test-cases/re-export-and-use/Makefile | 12 +- .../test-cases/re-export-cases/Makefile | 161 +- .../test-cases/re-export-layers/Makefile | 32 +- .../re-export-optimizations-indirect/Makefile | 25 +- .../re-export-optimizations/Makefile | 25 +- .../test-cases/read-only-relocs/Makefile | 6 +- unit-tests/test-cases/relocs-asm/relocs-asm.s | 34 +- .../test-cases/shared-cache-dylib/foo.c | 5 +- .../test-cases/stabs-directory-slash/Makefile | 4 +- .../test-cases/stabs-directory-slash/main.c | 3 +- .../test-cases/stack_addr_size/Makefile | 11 +- .../test-cases/stack_size_no_addr/Makefile | 4 +- unit-tests/test-cases/strip_local/hello.c | 1 + .../test-cases/switch-jump-table/Makefile | 23 +- .../test-cases/switch-jump-table/switch.s | 2 + .../tentative-and-archive-code/Makefile | 64 + .../tentative-and-archive-code/foo_code.c | 3 + .../tentative-and-archive-code/foo_data.c | 2 + .../tentative-and-archive-code/foo_tent.c | 2 + .../tentative-and-archive-code/junk.c | 1 + .../tentative-and-archive-code/main.c | 8 + unit-tests/test-cases/tlv-dead_strip/Makefile | 39 + unit-tests/test-cases/tlv-dead_strip/main.c | 33 + unit-tests/test-cases/umbrella-dylib/Makefile | 4 +- .../test-cases/weak-def-auto-hide/other.s | 3 +- .../weak-def-hidden-and-global/Makefile | 46 + .../weak-def-hidden-and-global/myglobal.c | 4 + .../weak-def-hidden-and-global/myhidden.s | 45 + unit-tests/test-cases/weak_dylib/Makefile | 28 +- unit-tests/test-cases/weak_dylib/data.c | 15 + .../test-cases/weak_import-addend/Makefile | 52 + .../test-cases/weak_import-addend/test.s | 12 + .../test-cases/weak_import-local/Makefile | 2 +- unit-tests/test-cases/weak_import3/Makefile | 15 +- unit-tests/test-cases/zero-fill3/Makefile | 10 +- 191 files changed, 9195 insertions(+), 1362 deletions(-) create mode 100644 src/ld/parsers/libunwind/AddressSpace.hpp create mode 100644 src/ld/parsers/libunwind/DwarfInstructions.hpp create mode 100644 src/ld/parsers/libunwind/DwarfParser.hpp create mode 100644 src/ld/parsers/libunwind/InternalMacros.h create mode 100644 src/ld/parsers/libunwind/Registers.hpp create mode 100644 unit-tests/test-cases/archive-order/Makefile create mode 100644 unit-tests/test-cases/archive-order/bar.c create mode 100644 unit-tests/test-cases/archive-order/bar2.c create mode 100644 unit-tests/test-cases/archive-order/bar3.c create mode 100644 unit-tests/test-cases/archive-order/expected.order create mode 100644 unit-tests/test-cases/archive-order/foo.c create mode 100644 unit-tests/test-cases/archive-order/foo2.c create mode 100644 unit-tests/test-cases/archive-order/foo3.c create mode 100644 unit-tests/test-cases/archive-order/main.c create mode 100644 unit-tests/test-cases/check-init-abs/.mod_init_func create mode 100644 unit-tests/test-cases/check-init-abs/Makefile create mode 100644 unit-tests/test-cases/check-init-abs/init.s rename unit-tests/test-cases/{prebound-main => check-init-abs}/main.c (96%) create mode 100644 unit-tests/test-cases/check-init-abs/term.s create mode 100644 unit-tests/test-cases/check-init-bind/.mod_init_func create mode 100644 unit-tests/test-cases/check-init-bind/Makefile create mode 100644 unit-tests/test-cases/check-init-bind/init.s create mode 100644 unit-tests/test-cases/check-init-bind/main.c create mode 100644 unit-tests/test-cases/check-init-bind/term.s create mode 100644 unit-tests/test-cases/check-init-no-rebase/.mod_init_func create mode 100644 unit-tests/test-cases/check-init-no-rebase/Makefile create mode 100644 unit-tests/test-cases/check-init-no-rebase/init.s create mode 100644 unit-tests/test-cases/check-init-no-rebase/main.c create mode 100644 unit-tests/test-cases/check-init-no-rebase/term.s create mode 100644 unit-tests/test-cases/compact-unwind-basic/Makefile create mode 100644 unit-tests/test-cases/compact-unwind-basic/test.s create mode 100644 unit-tests/test-cases/dwarf-debug-notes/hello.order create mode 100644 unit-tests/test-cases/kext-undefined-export/Makefile create mode 100644 unit-tests/test-cases/kext-undefined-export/mykext-i386.exp create mode 100644 unit-tests/test-cases/kext-undefined-export/mykext.c create mode 100644 unit-tests/test-cases/kext-undefined-export/mykext.exp create mode 100644 unit-tests/test-cases/kext-undefined-export/mykextinfo.c create mode 100755 unit-tests/test-cases/label-on-end-of-section-order/Makefile create mode 100755 unit-tests/test-cases/label-on-end-of-section-order/foo.s create mode 100644 unit-tests/test-cases/lto-dead_strip-coalesce/Makefile create mode 100644 unit-tests/test-cases/lto-dead_strip-coalesce/foo.c create mode 100644 unit-tests/test-cases/lto-dead_strip-coalesce/main.c create mode 100755 unit-tests/test-cases/merge_zero_fill_sections/Makefile create mode 100755 unit-tests/test-cases/merge_zero_fill_sections/main.c create mode 100644 unit-tests/test-cases/objc-category-warning/Makefile create mode 100644 unit-tests/test-cases/objc-category-warning/cat.m create mode 100644 unit-tests/test-cases/objc-category-warning/copycat.m create mode 100644 unit-tests/test-cases/objc-category-warning/foo.m delete mode 100644 unit-tests/test-cases/prebound-main/Makefile create mode 100644 unit-tests/test-cases/tentative-and-archive-code/Makefile create mode 100644 unit-tests/test-cases/tentative-and-archive-code/foo_code.c create mode 100644 unit-tests/test-cases/tentative-and-archive-code/foo_data.c create mode 100644 unit-tests/test-cases/tentative-and-archive-code/foo_tent.c create mode 100644 unit-tests/test-cases/tentative-and-archive-code/junk.c create mode 100644 unit-tests/test-cases/tentative-and-archive-code/main.c create mode 100644 unit-tests/test-cases/tlv-dead_strip/Makefile create mode 100644 unit-tests/test-cases/tlv-dead_strip/main.c create mode 100644 unit-tests/test-cases/weak-def-hidden-and-global/Makefile create mode 100644 unit-tests/test-cases/weak-def-hidden-and-global/myglobal.c create mode 100644 unit-tests/test-cases/weak-def-hidden-and-global/myhidden.s create mode 100644 unit-tests/test-cases/weak_dylib/data.c create mode 100644 unit-tests/test-cases/weak_import-addend/Makefile create mode 100644 unit-tests/test-cases/weak_import-addend/test.s diff --git a/ChangeLog b/ChangeLog index e2d1d2c..fd449c8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,398 @@ +-------- 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 -2010-03-07 Nick Kledzik +2011-03-07 Nick Kledzik enable i386 ASLR @@ -79,7 +470,7 @@ 2010-11-01 Nick Kledzik - + iOS is missing dof sections for armv7 slice -------- tagged ld64-120.3 @@ -402,7 +793,7 @@ 2010-08-20 Nick Kledzik - SWB: ld64-117.1 on Durango8F54: Assertion failed: + 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. @@ -444,17 +835,17 @@ 2010-08-14 Nick Kledzik - SWB: ld64-117.1 on Durango8F54: Assertion failed: (categoryAtom->size() == Category::size()) + 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 Durango8F54: bad category optimization + 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 Durango8F54: address not in any section + 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 @@ -552,7 +943,7 @@ 2010-06-09 Nick Kledzik - + 'rebase' makes timestamps invalid/unreadable for GDB 2010-06-09 Nick Kledzik diff --git a/doc/man/man1/ld.1 b/doc/man/man1/ld.1 index c1aa2bc..03f257a 100644 --- a/doc/man/man1/ld.1 +++ b/doc/man/man1/ld.1 @@ -295,6 +295,8 @@ need to be listed. By default the linker moves all zero fill sections to the end of the __DATA segment and configures them to use no space on disk. This option suppresses that optimization, so zero-filled data occupies space on disk in a final linked image. +.It Fl merge_zero_fill_sections +Causes all zero-fill sections in the __DATA segment to be merged into one __zerofill section. .El .Ss Options when creating a dynamic library (dylib) .Bl -tag @@ -509,10 +511,6 @@ of wildcards. .Bl -tag .It Fl v Prints the version of the linker. -.It Fl no_compact_linkedit -Normally when targeting Mac OS X 10.6, the linker will generate compact information -in the __LINKEDIT segment. -This option causes the linker to instead produce traditional relocation information. .It Fl allow_heap_execute Normally i386 main executables will be marked so that the Mac OS X 10.7 and later kernel will only allow pages with the x-bit to execute instructions. This option overrides that @@ -687,6 +685,10 @@ option is used, the temporary file will be stored at the specified path and rema is complete. Without the option, the linker picks a path and deletes the object file before the linker tool completes, thus tools such as the debugger or dsymutil will not be able to access the DWARF debug info in the temporary object file. +.It Fl page_align_data_atoms +During development, this option can be used to space out all global variables so each is on a separate page. +This is useful when analyzing dirty and resident pages. The information can then be used to create an +order file to cluster commonly used/dirty globals onto the same page(s). .El .Ss Obsolete Options .Bl -tag diff --git a/ld64.xcodeproj/project.pbxproj b/ld64.xcodeproj/project.pbxproj index cfb54eb..ad73394 100644 --- a/ld64.xcodeproj/project.pbxproj +++ b/ld64.xcodeproj/project.pbxproj @@ -28,6 +28,7 @@ isa = PBXAggregateTarget; buildConfigurationList = F9B1A26C0A3A568700DA8FAB /* Build configuration list for PBXAggregateTarget "all" */; buildPhases = ( + F9871A3413340B4600DB3F24 /* Platform install */, ); dependencies = ( F9B1A2690A3A568200DA8FAB /* PBXTargetDependency */, @@ -686,7 +687,22 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/csh; - shellScript = "# Let tests set MACOSX_DEPLOYMENT_TARGET as they need\nunsetenv MACOSX_DEPLOYMENT_TARGET\n\n# always use new linker\nsetenv LD_NO_CLASSIC_LINKER\nsetenv LD_NO_CLASSIC_LINKER_STATIC\n\n# run full test suite\n\"$SRCROOT\"/unit-tests/run-all-unit-tests\n\nexit 0"; + 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"; + showEnvVarsInLog = 0; + }; + F9871A3413340B4600DB3F24 /* Platform install */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + name = "Platform install"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "\nif [ -n \"${RC_PURPLE}\" ]; then\n\techo \"here\"\n\tmkdir -p ${DSTROOT}/Developer/Platforms/iPhoneOS.platform/Developer/\n\tmv ${DSTROOT}/usr ${DSTROOT}/Developer/Platforms/iPhoneOS.platform/Developer\nfi\n"; showEnvVarsInLog = 0; }; F9E8DB4D11921594007B4D6A /* make config.h */ = { @@ -977,6 +993,7 @@ GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; + GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; @@ -1169,6 +1186,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_MODEL_TUNING = G5; + GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; HEADER_SEARCH_PATHS = ""; INSTALL_PATH = /usr/bin; @@ -1189,6 +1207,7 @@ GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = s; GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; HEADER_SEARCH_PATHS = ( "$(SRCROOT)/src/ld", "$(DEVELOPER_DIR)/usr/local/include", @@ -1323,6 +1342,7 @@ GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; + GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; INSTALL_PATH = "$(HOME)/bin"; PREBINDING = NO; @@ -1337,6 +1357,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_MODEL_TUNING = G5; + GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; HEADER_SEARCH_PATHS = ""; INSTALL_PATH = /usr/bin; diff --git a/src/abstraction/MachOFileAbstraction.hpp b/src/abstraction/MachOFileAbstraction.hpp index a9a69a7..f5f073b 100644 --- a/src/abstraction/MachOFileAbstraction.hpp +++ b/src/abstraction/MachOFileAbstraction.hpp @@ -35,6 +35,7 @@ #include #include #include +#include #include "FileAbstraction.hpp" @@ -53,6 +54,7 @@ #ifndef CPU_SUBTYPE_ARM_V7 #define CPU_SUBTYPE_ARM_V7 ((cpu_subtype_t) 9) #endif + #ifndef ARM_THUMB_32BIT_BRANCH #define ARM_THUMB_32BIT_BRANCH 7 #endif @@ -209,6 +211,32 @@ #define ARM_RELOC_HALF 8 #define ARM_RELOC_HALF_SECTDIFF 9 +#ifndef CPU_SUBTYPE_ARM_V7F + #define CPU_SUBTYPE_ARM_V7F ((cpu_subtype_t) 10) +#endif +#ifndef CPU_SUBTYPE_ARM_V7K + #define CPU_SUBTYPE_ARM_V7K ((cpu_subtype_t) 12) +#endif + +struct ARMSubType { + const char* subTypeName; + const char* llvmTriplePrefix; + cpu_subtype_t subType; + 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 } +}; + + + // // This abstraction layer makes every mach-o file look like a 64-bit mach-o file with native endianness @@ -1260,6 +1288,42 @@ private: }; +// +// mach-o __LD, __compact_unwind section in object files +// +template +class macho_compact_unwind_entry { +public: + typedef typename P::E E; + typedef typename P::uint_t pint_t; + + pint_t codeStart() const INLINE { return P::getP(_codeStart); } + void set_codeStart(pint_t value) INLINE { P::setP(_codeStart, value); } + + uint32_t codeLen() const INLINE { return E::get32(_codeLen); } + void set_codeLen(uint32_t value) INLINE { E::set32(_codeLen, value); } + + uint32_t compactUnwindInfo() const INLINE { return E::get32(_compactUnwindInfo); } + void set_compactUnwindInfo(uint32_t value) INLINE { E::set32(_compactUnwindInfo, value); } + + pint_t personality() const INLINE { return P::getP(_personality); } + void set_personality(pint_t value) INLINE { P::setP(_personality, value); } + + pint_t lsda() const INLINE { return P::getP(_lsda); } + void set_lsda(pint_t value) INLINE { P::setP(_lsda, value); } + + static uint32_t codeStartFieldOffset() INLINE { return offsetof(macho_compact_unwind_entry

,_codeStart); } + static uint32_t personalityFieldOffset() INLINE { return offsetof(macho_compact_unwind_entry

,_personality); } + static uint32_t lsdaFieldOffset() INLINE { return offsetof(macho_compact_unwind_entry

,_lsda); } + +private: + pint_t _codeStart; + uint32_t _codeLen; + uint32_t _compactUnwindInfo; + pint_t _personality; + pint_t _lsda; +}; + #endif // __MACH_O_FILE_ABSTRACTION__ diff --git a/src/ld/HeaderAndLoadCommands.hpp b/src/ld/HeaderAndLoadCommands.hpp index 2c97dba..395fc99 100644 --- a/src/ld/HeaderAndLoadCommands.hpp +++ b/src/ld/HeaderAndLoadCommands.hpp @@ -647,6 +647,8 @@ uint32_t HeaderAndLoadCommandsAtom::sectionFlags(ld::Internal::FinalSection* return S_REGULAR | S_ATTR_NO_DEAD_STRIP; else if ( (strncmp(sect->sectionName(), "__objc_superrefs", 16) == 0) && (strcmp(sect->segmentName(), "__DATA") == 0) ) return S_REGULAR | S_ATTR_NO_DEAD_STRIP; + else if ( (strncmp(sect->sectionName(), "__objc_nlclslist", 16) == 0) && (strcmp(sect->segmentName(), "__DATA") == 0) ) + return S_REGULAR | S_ATTR_NO_DEAD_STRIP; else return S_REGULAR; case ld::Section::typeCode: @@ -750,6 +752,8 @@ uint32_t HeaderAndLoadCommandsAtom::sectionFlags(ld::Internal::FinalSection* case ld::Section::typeLastSection: assert(0 && "typeLastSection should not make it to final linked image"); return S_REGULAR; + case ld::Section::typeDebug: + return S_REGULAR | S_ATTR_DEBUG; } return S_REGULAR; } @@ -1013,9 +1017,9 @@ template uint8_t* HeaderAndLoadCommandsAtom::copyVersionLoadCommand(uint8_t* p) const { macho_version_min_command

* cmd = (macho_version_min_command

*)p; - ld::MacVersionMin macVersion = _options.macosxVersionMin(); - ld::IPhoneVersionMin iphoneOSVersion = _options.iphoneOSVersionMin(); - assert( (macVersion != ld::macVersionUnset) || (iphoneOSVersion != ld::iPhoneVersionUnset) ); + ld::MacVersionMin macVersion = _options.macosxVersionMin(); + ld::IOSVersionMin iOSVersion = _options.iOSVersionMin(); + assert( (macVersion != ld::macVersionUnset) || (iOSVersion != ld::iOSVersionUnset) ); if ( macVersion != ld::macVersionUnset ) { cmd->set_cmd(LC_VERSION_MIN_MACOSX); cmd->set_cmdsize(sizeof(macho_version_min_command

)); @@ -1025,7 +1029,7 @@ uint8_t* HeaderAndLoadCommandsAtom::copyVersionLoadCommand(uint8_t* p) const else { cmd->set_cmd(LC_VERSION_MIN_IPHONEOS); cmd->set_cmdsize(sizeof(macho_version_min_command

)); - cmd->set_version((uint32_t)iphoneOSVersion); + cmd->set_version((uint32_t)iOSVersion); cmd->set_reserved(0); } return p + sizeof(macho_version_min_command

); @@ -1177,13 +1181,9 @@ uint8_t* HeaderAndLoadCommandsAtom::copyDylibLoadCommand(uint8_t* p, const ld { uint32_t sz = alignedSize(sizeof(macho_dylib_command

) + strlen(dylib->installPath()) + 1); macho_dylib_command

* cmd = (macho_dylib_command

*)p; - // If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB - bool autoWeakLoadDylib = false; // FIX - //( (fWriter.fDylibReadersWithWeakImports.count(fInfo.reader) > 0) - //&& (fWriter.fDylibReadersWithNonWeakImports.count(fInfo.reader) == 0) ); if ( dylib->willBeLazyLoadedDylib() ) cmd->set_cmd(LC_LAZY_LOAD_DYLIB); - else if ( dylib->willBeWeakLinked() || autoWeakLoadDylib ) + else if ( dylib->forcedWeakLinked() || dylib->allSymbolsAreWeakImported() ) cmd->set_cmd(LC_LOAD_WEAK_DYLIB); else if ( dylib->willBeReExported() && _options.useSimplifiedDylibReExports() ) cmd->set_cmd(LC_REEXPORT_DYLIB); diff --git a/src/ld/InputFiles.cpp b/src/ld/InputFiles.cpp index bc292bb..84a2297 100644 --- a/src/ld/InputFiles.cpp +++ b/src/ld/InputFiles.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* * - * Copyright (c) 2009-2010 Apple Inc. All rights reserved. + * Copyright (c) 2009-2011 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -186,7 +186,7 @@ const char* InputFiles::fileArch(const uint8_t* p, unsigned len) } -ld::File* InputFiles::makeFile(const Options::FileInfo& info) +ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib) { // map in whole file uint64_t len = info.fileLen; @@ -256,7 +256,7 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info) // see if it is an object file mach_o::relocatable::ParserOptions objOpts; objOpts.architecture = _options.architecture(); - objOpts.objSubtypeMustMatch = _options.preferSubArchitecture(); + objOpts.objSubtypeMustMatch = !_options.allowSubArchitectureMismatches(); objOpts.logAllFiles = _options.logAllFiles(); objOpts.convertUnwindInfo = _options.needsUnwindInfoSection(); objOpts.subType = _options.subArchitecture(); @@ -270,22 +270,27 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info) return this->addObject(objResult, info, len); // 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); + 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); // see if it is a static library - archive::ParserOptions archOpts; + ::archive::ParserOptions archOpts; archOpts.objOpts = objOpts; archOpts.forceLoadThisArchive = info.options.fForceLoad; archOpts.forceLoadAll = _options.fullyLoadArchives(); archOpts.forceLoadObjC = _options.loadAllObjcObjectsFromArchives(); + archOpts.objcABI2 = _options.objCABIVersion2POverride(); archOpts.verboseLoad = _options.whyLoad(); archOpts.logAllFiles = _options.logAllFiles(); - ld::File* archiveResult = archive::parse(p, len, info.path, info.modTime, _nextInputOrdinal, archOpts); - if ( archiveResult != NULL ) + ld::archive::File* archiveResult = ::archive::parse(p, len, info.path, info.modTime, _nextInputOrdinal, 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); - + } + // 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() ) { @@ -404,7 +409,7 @@ 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); - ld::File* reader = this->makeFile(info); + ld::File* reader = this->makeFile(info, true); ld::dylib::File* dylibReader = dynamic_cast(reader); if ( dylibReader != NULL ) { //_installPathToDylibs[strdup(installPath)] = dylibReader; @@ -434,7 +439,7 @@ ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* from // search for dylib using -F and -L paths Options::FileInfo info = _options.findFileUsingPaths(installPath); try { - ld::File* reader = this->makeFile(info); + ld::File* reader = this->makeFile(info, true); ld::dylib::File* dylibReader = dynamic_cast(reader); if ( dylibReader != NULL ) { //assert(_installPathToDylibs.find(installPath) != _installPathToDylibs.end()); @@ -465,7 +470,7 @@ void InputFiles::createIndirectDylibs() // keep processing dylibs until no more dylibs are added unsigned long lastMapSize = 0; - std::set dylibsProcessed; + std::set dylibsProcessed; while ( lastMapSize != _allDylibs.size() ) { lastMapSize = _allDylibs.size(); // can't iterator _installPathToDylibs while modifying it, so use temp buffer @@ -659,7 +664,7 @@ InputFiles::InputFiles(Options& opts, const char** archName) for (std::vector::const_iterator it = files.begin(); it != files.end(); ++it) { const Options::FileInfo& entry = *it; try { - _inputFiles.push_back(this->makeFile(entry)); + _inputFiles.push_back(this->makeFile(entry, false)); } catch (const char* msg) { if ( (strstr(msg, "architecture") != NULL) && !_options.errorOnOtherArchFiles() ) { @@ -717,7 +722,7 @@ ld::File* InputFiles::addDylib(ld::dylib::File* reader, const Options::FileInfo& } // store options about how dylib will be used in dylib itself if ( info.options.fWeakImport ) - reader->setWillBeWeakLinked(); + reader->setForcedWeakLinked(); if ( info.options.fReExport ) reader->setWillBeReExported(); if ( info.options.fUpward ) { @@ -778,6 +783,7 @@ bool InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler) const } if ( didSomething || true ) { switch ( _options.outputKind() ) { + case Options::kStaticExecutable: case Options::kDynamicExecutable: // add implicit __dso_handle label handler.doAtom(DSOHandleAtom::_s_atomExecutable); @@ -802,11 +808,6 @@ bool InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler) const handler.doAtom(DSOHandleAtom::_s_atomDyld); handler.doAtom(DSOHandleAtom::_s_atomAll); break; - case Options::kStaticExecutable: - // add implicit __dso_handle label - handler.doAtom(DSOHandleAtom::_s_atomExecutable); - handler.doAtom(DSOHandleAtom::_s_atomAll); - break; case Options::kPreload: // add implicit __mh_preload_header label handler.doAtom(DSOHandleAtom::_s_atomPreload); @@ -824,7 +825,7 @@ bool InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler) const } -bool InputFiles::searchLibraries(const char* name, bool searchDylibs, bool searchArchives, ld::File::AtomHandler& handler) const +bool InputFiles::searchLibraries(const char* name, bool searchDylibs, bool searchArchives, bool dataSymbolOnly, ld::File::AtomHandler& handler) const { // check each input file for (std::vector::const_iterator it=_inputFiles.begin(); it != _inputFiles.end(); ++it) { @@ -832,6 +833,7 @@ bool InputFiles::searchLibraries(const char* name, bool searchDylibs, bool searc // 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) ) { @@ -842,12 +844,22 @@ bool InputFiles::searchLibraries(const char* name, bool searchDylibs, bool searc // else continue search for a non-weak definition } } - else if ( searchArchives && (dylibFile == NULL) ) { - if ( file->justInTimeforEachAtom(name, handler) ) { - if ( _options.traceArchives() ) - logArchive(file); - // found definition in static library, done - return true; + 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; + } + } + else { + if ( archiveFile->justInTimeforEachAtom(name, handler) ) { + if ( _options.traceArchives() ) + logArchive(file); + // found definition in static library, done + return true; + } } } } @@ -898,7 +910,7 @@ bool InputFiles::searchWeakDefInDylib(const char* name) const void InputFiles::dylibs(ld::Internal& state) { - bool dylibsOK; + bool dylibsOK = false; switch ( _options.outputKind() ) { case Options::kDynamicExecutable: case Options::kDynamicLibrary: diff --git a/src/ld/InputFiles.h b/src/ld/InputFiles.h index 1565515..1842c8e 100644 --- a/src/ld/InputFiles.h +++ b/src/ld/InputFiles.h @@ -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@ * @@ -60,7 +60,8 @@ public: // iterates all atoms in initial files bool forEachInitialAtom(ld::File::AtomHandler&) const; // searches libraries for name - bool searchLibraries(const char* name, bool searchDylibs, bool searchArchives, ld::File::AtomHandler&) const; + bool searchLibraries(const char* name, bool searchDylibs, bool searchArchives, + bool dataSymbolOnly, ld::File::AtomHandler&) const; // see if any linked dylibs export a weak def of symbol bool searchWeakDefInDylib(const char* name) const; // copy dylibs to link with in command line order @@ -81,7 +82,7 @@ public: private: void inferArchitecture(Options& opts, const char** archName); const char* fileArch(const uint8_t* p, unsigned len); - ld::File* makeFile(const Options::FileInfo& info); + 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); diff --git a/src/ld/LinkEdit.hpp b/src/ld/LinkEdit.hpp index d905a3d..bf9026b 100644 --- a/src/ld/LinkEdit.hpp +++ b/src/ld/LinkEdit.hpp @@ -1058,12 +1058,16 @@ private: typedef typename A::P::E E; typedef typename A::P::uint_t pint_t; - void addSplitSegInfo(uint64_t address, ld::Fixup::Kind k) const; + void addSplitSegInfo(uint64_t address, ld::Fixup::Kind k, uint32_t) const; void uleb128EncodeAddresses(const std::vector& locations) const; mutable std::vector _32bitPointerLocations; mutable std::vector _64bitPointerLocations; mutable std::vector _ppcHi16Locations; + mutable std::vector _thumbLo16Locations; + mutable std::vector _thumbHi16Locations[16]; + mutable std::vector _armLo16Locations; + mutable std::vector _armHi16Locations[16]; static ld::Section _s_section; @@ -1073,7 +1077,7 @@ template ld::Section SplitSegInfoAtom::_s_section("__LINKEDIT", "__splitSegInfo", ld::Section::typeLinkEdit, true); template <> -void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind) const +void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const { switch (kind) { case ld::Fixup::kindStoreX86PCRel32: @@ -1101,7 +1105,7 @@ void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind } template <> -void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind) const +void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const { switch (kind) { case ld::Fixup::kindStoreLittleEndian32: @@ -1115,12 +1119,26 @@ void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind ki } template <> -void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind) const +void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const { switch (kind) { case ld::Fixup::kindStoreLittleEndian32: _32bitPointerLocations.push_back(address); break; + case ld::Fixup::kindStoreARMLow16: + _armLo16Locations.push_back(address); + break; + case ld::Fixup::kindStoreThumbLow16: + _thumbLo16Locations.push_back(address); + break; + case ld::Fixup::kindStoreARMHigh16: + assert(extra < 16); + _armHi16Locations[extra].push_back(address); + break; + case ld::Fixup::kindStoreThumbHigh16: + assert(extra < 16); + _thumbHi16Locations[extra].push_back(address); + break; default: warning("codegen at address 0x%08llX prevents image from working in dyld shared cache", address); break; @@ -1129,7 +1147,7 @@ void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind ki template <> -void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind) const +void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const { switch (kind) { case ld::Fixup::kindStorePPCPicHigh16AddLow: @@ -1146,7 +1164,7 @@ void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind ki template <> -void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind) const +void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const { switch (kind) { case ld::Fixup::kindStorePPCPicHigh16AddLow: @@ -1192,7 +1210,7 @@ void SplitSegInfoAtom::encode() const // sort into group by pointer adjustment kind std::vector& info = this->_writer._splitSegInfos; for (std::vector::const_iterator it = info.begin(); it != info.end(); ++it) { - this->addSplitSegInfo(it->address, it->kind); + this->addSplitSegInfo(it->address, it->kind, it->extra); } // delta compress runs of addresses @@ -1221,6 +1239,42 @@ void SplitSegInfoAtom::encode() const this->_encodedData.append_byte(0); // terminator } + if ( _thumbLo16Locations.size() != 0 ) { + this->_encodedData.append_byte(5); + //fprintf(stderr, "type 5:\n"); + std::sort(_thumbLo16Locations.begin(), _thumbLo16Locations.end()); + this->uleb128EncodeAddresses(_thumbLo16Locations); + this->_encodedData.append_byte(0); // terminator + } + + if ( _armLo16Locations.size() != 0 ) { + this->_encodedData.append_byte(6); + //fprintf(stderr, "type 6:\n"); + std::sort(_armLo16Locations.begin(), _armLo16Locations.end()); + this->uleb128EncodeAddresses(_armLo16Locations); + this->_encodedData.append_byte(0); // terminator + } + + for (uint32_t i=0; i < 16; ++i) { + if ( _thumbHi16Locations[i].size() != 0 ) { + this->_encodedData.append_byte(16+i); + //fprintf(stderr, "type 16+%d:\n", i); + std::sort(_thumbHi16Locations[i].begin(), _thumbHi16Locations[i].end()); + this->uleb128EncodeAddresses(_thumbHi16Locations[i]); + this->_encodedData.append_byte(0); // terminator + } + } + + for (uint32_t i=0; i < 16; ++i) { + if ( _armHi16Locations[i].size() != 0 ) { + this->_encodedData.append_byte(32+i); + //fprintf(stderr, "type 32+%d:\n", i); + std::sort(_armHi16Locations[i].begin(), _armHi16Locations[i].end()); + this->uleb128EncodeAddresses(_armHi16Locations[i]); + this->_encodedData.append_byte(0); // terminator + } + } + // always add zero byte to mark end this->_encodedData.append_byte(0); diff --git a/src/ld/LinkEditClassic.hpp b/src/ld/LinkEditClassic.hpp index d5438ac..1429d95 100644 --- a/src/ld/LinkEditClassic.hpp +++ b/src/ld/LinkEditClassic.hpp @@ -485,7 +485,8 @@ void SymbolTableAtom::addImport(const ld::Atom* atom, StringPoolAtom* pool) && (atom->combine() == ld::Atom::combineByName) ) { desc |= N_REF_TO_WEAK; } - if ( atom->weakImported() ) + const ld::dylib::File* dylib = dynamic_cast(atom->file()); + if ( atom->weakImported() || ((dylib != NULL) && dylib->forcedWeakLinked()) ) desc |= N_WEAK_REF; entry.set_n_desc(desc); @@ -1384,8 +1385,12 @@ void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* else sreloc1->set_r_type(GENERIC_RELOC_SECTDIFF); sreloc1->set_r_address(address); - if ( entry.toTarget == entry.inAtom ) - sreloc1->set_r_value(entry.toTarget->finalAddress()+entry.toAddend); + if ( entry.toTarget == entry.inAtom ) { + if ( entry.toAddend > entry.toTarget->size() ) + sreloc1->set_r_value(entry.toTarget->finalAddress()+entry.offsetInAtom); + else + sreloc1->set_r_value(entry.toTarget->finalAddress()+entry.toAddend); + } else sreloc1->set_r_value(entry.toTarget->finalAddress()); sreloc2->set_r_scattered(true); @@ -1513,10 +1518,15 @@ void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* else sreloc1->set_r_type(ARM_RELOC_SECTDIFF); sreloc1->set_r_address(address); - if ( entry.toTarget == entry.inAtom ) - sreloc1->set_r_value(entry.toTarget->finalAddress()+entry.toAddend); - else + if ( entry.toTarget == entry.inAtom ) { + if ( entry.toAddend > entry.toTarget->size() ) + sreloc1->set_r_value(entry.toTarget->finalAddress()+entry.offsetInAtom); + else + sreloc1->set_r_value(entry.toTarget->finalAddress()+entry.toAddend); + } + else { sreloc1->set_r_value(entry.toTarget->finalAddress()); + } sreloc2->set_r_scattered(true); sreloc2->set_r_pcrel(false); sreloc2->set_r_length(2); @@ -1632,7 +1642,7 @@ void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* reloc1.set_r_symbolnum(symbolNum); reloc1.set_r_pcrel(false); reloc1.set_r_length(len); - reloc1.set_r_extern(false); + reloc1.set_r_extern(external); reloc1.set_r_type(ARM_RELOC_HALF); reloc2.set_r_address(otherHalf); // other half reloc2.set_r_symbolnum(0); @@ -2393,10 +2403,6 @@ uint32_t IndirectSymbolTableAtom::symIndexOfNonLazyPointerAtom(const ld::Atom default: throw "internal error: unexpected non-lazy pointer binding"; } - // Special case non-lazy-pointer slot used to point to "dyld_stub_binder" - // That slot is never bound using indirect symbol table - if ( target == _state.compressedFastBinderProxy ) - return INDIRECT_SYMBOL_ABS; bool targetIsGlobal = (target->scope() == ld::Atom::scopeGlobal); switch ( target->definition() ) { case ld::Atom::definitionRegular: diff --git a/src/ld/Options.cpp b/src/ld/Options.cpp index c06e1f3..3f5b054 100644 --- a/src/ld/Options.cpp +++ b/src/ld/Options.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2005-2010 Apple Inc. All rights reserved. + * Copyright (c) 2005-2011 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -87,7 +87,7 @@ void throwf(const char* format, ...) Options::Options(int argc, const char* argv[]) : fOutputFile("a.out"), fArchitecture(0), fSubArchitecture(0), fArchitectureName("unknown"), fOutputKind(kDynamicExecutable), - fHasPreferredSubType(false), fPrebind(false), fBindAtLoad(false), fKeepPrivateExterns(false), + fHasPreferredSubType(false), fArchSupportsThumb2(false), fPrebind(false), fBindAtLoad(false), fKeepPrivateExterns(false), fNeedsModuleTable(false), fIgnoreOtherArchFiles(false), fErrorOnOtherArchFiles(false), fForceSubtypeAll(false), fInterposeMode(kInterposeNone), fDeadStrip(false), fNameSpace(kTwoLevelNameSpace), fDylibCompatVersion(0), fDylibCurrentVersion(0), fDylibInstallName(NULL), fFinalName(NULL), fEntryName("start"), @@ -120,19 +120,17 @@ Options::Options(int argc, const char* argv[]) fForDyld(false), fMakeTentativeDefinitionsReal(false), fWhyLoad(false), fRootSafe(false), fSetuidSafe(false), fImplicitlyLinkPublicDylibs(true), fAddCompactUnwindEncoding(true), fWarnCompactUnwind(false), fRemoveDwarfUnwindIfCompactExists(false), - fAutoOrderInitializers(true), fOptimizeZeroFill(true), fLogObjectFiles(false), + fAutoOrderInitializers(true), fOptimizeZeroFill(true), fMergeZeroFill(false), fLogObjectFiles(false), fLogAllFiles(false), fTraceDylibs(false), fTraceIndirectDylibs(false), fTraceArchives(false), fOutputSlidable(false), fWarnWeakExports(false), fObjcGcCompaction(false), fObjCGc(false), fObjCGcOnly(false), fDemangle(false), fTLVSupport(false), -#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1070 - fVersionLoadCommand(false), fFunctionStartsLoadCommand(false), -#else - fVersionLoadCommand(true), fFunctionStartsLoadCommand(true), -#endif - fCanReExportSymbols(false), fObjcCategoryMerging(true), + fVersionLoadCommand(false), fVersionLoadCommandForcedOn(false), + fVersionLoadCommandForcedOff(false), fFunctionStartsLoadCommand(false), + fFunctionStartsForcedOn(false), fFunctionStartsForcedOff(false), + fCanReExportSymbols(false), fObjcCategoryMerging(true), fPageAlignDataAtoms(false), fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL), - fMacVersionMin(ld::macVersionUnset), fIPhoneVersionMin(ld::iPhoneVersionUnset), + fMacVersionMin(ld::macVersionUnset), fIOSVersionMin(ld::iOSVersionUnset), fSaveTempFiles(false) { this->checkForClassic(argc, argv); @@ -175,8 +173,6 @@ bool Options::interposable(const char* name) const } - - bool Options::printWhyLive(const char* symbolName) const { return ( fWhyLive.find(symbolName) != fWhyLive.end() ); @@ -264,7 +260,7 @@ uint32_t Options::initialSegProtection(const char* segName) const uint32_t Options::maxSegProtection(const char* segName) const { // iPhoneOS always uses same protection for max and initial - if ( fIPhoneVersionMin != ld::iPhoneVersionUnset ) + if ( fIOSVersionMin != ld::iOSVersionUnset ) return initialSegProtection(segName); for(std::vector::const_iterator it = fCustomSegmentProtections.begin(); it != fCustomSegmentProtections.end(); ++it) { @@ -474,7 +470,6 @@ bool Options::keepLocalSymbol(const char* symbolName) const throw "internal error"; } - void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype) { fArchitecture = type; @@ -553,32 +548,19 @@ void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype) fMakeCompressedDyldInfo = true; break; case CPU_TYPE_ARM: - switch ( subtype ) { - case CPU_SUBTYPE_ARM_V4T: - fArchitectureName = "armv4t"; - fHasPreferredSubType = true; - break; - case CPU_SUBTYPE_ARM_V5TEJ: - fArchitectureName = "armv5"; - fHasPreferredSubType = true; - break; - case CPU_SUBTYPE_ARM_V6: - fArchitectureName = "armv6"; - fHasPreferredSubType = true; - break; - case CPU_SUBTYPE_ARM_V7: - fArchitectureName = "armv7"; - fHasPreferredSubType = true; - break; - default: - assert(0 && "unknown arm subtype"); - fArchitectureName = "arm"; - break; + fHasPreferredSubType = true; + for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) { + if ( t->subType == subtype ) { + fArchitectureName = t->subTypeName; + fArchSupportsThumb2 = t->supportsThumb2; + break; + } } - if ( (fMacVersionMin == ld::macVersionUnset) && (fIPhoneVersionMin == ld::iPhoneVersionUnset) && (fOutputKind != Options::kObjectFile) ) { + 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); - setIPhoneVersionMin(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); @@ -587,7 +569,7 @@ void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype) fMacVersionMin = ld::mac10_6; #endif } - if ( !fMakeCompressedDyldInfo && minOS(ld::mac10_6, ld::iPhone3_1) && !fMakeCompressedDyldInfoForceOff ) + if ( !fMakeCompressedDyldInfo && minOS(ld::mac10_6, ld::iOS_3_1) && !fMakeCompressedDyldInfoForceOff ) fMakeCompressedDyldInfo = true; break; default: @@ -642,33 +624,18 @@ void Options::parseArch(const char* arch) fSubArchitecture = CPU_SUBTYPE_POWERPC_970; fHasPreferredSubType = true; } - else if ( strcmp(arch, "armv6") == 0 ) { - fArchitecture = CPU_TYPE_ARM; - fSubArchitecture = CPU_SUBTYPE_ARM_V6; - fHasPreferredSubType = true; - } - else if ( strcmp(arch, "armv5") == 0 ) { - fArchitecture = CPU_TYPE_ARM; - fSubArchitecture = CPU_SUBTYPE_ARM_V5TEJ; - fHasPreferredSubType = true; - } - else if ( strcmp(arch, "armv4t") == 0 ) { - fArchitecture = CPU_TYPE_ARM; - fSubArchitecture = CPU_SUBTYPE_ARM_V4T; - fHasPreferredSubType = true; - } - else if ( strcmp(arch, "xscale") == 0 ) { - fArchitecture = CPU_TYPE_ARM; - fSubArchitecture = CPU_SUBTYPE_ARM_XSCALE; - fHasPreferredSubType = true; - } - else if ( strcmp(arch, "armv7") == 0 ) { - fArchitecture = CPU_TYPE_ARM; - fSubArchitecture = CPU_SUBTYPE_ARM_V7; - fHasPreferredSubType = true; - } - else + 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; + } + } throwf("unknown/unsupported architecture name for: -arch %s", arch); + } } bool Options::checkForFile(const char* format, const char* dir, const char* rootName, FileInfo& result) const @@ -1346,7 +1313,7 @@ void Options::setMacOSXVersionMin(const char* version) } } -void Options::setIPhoneVersionMin(const char* version) +void Options::setIOSVersionMin(const char* version) { if ( version == NULL ) throw "-ios_version_min argument missing"; @@ -1359,16 +1326,16 @@ void Options::setIPhoneVersionMin(const char* version) unsigned int majorVersion = version[0] - '0'; unsigned int minorVersion = version[2] - '0'; - fIPhoneVersionMin = (ld::IPhoneVersionMin)((majorVersion << 16) | (minorVersion << 8)); + fIOSVersionMin = (ld::IOSVersionMin)((majorVersion << 16) | (minorVersion << 8)); } -bool Options::minOS(ld::MacVersionMin requiredMacMin, ld::IPhoneVersionMin requirediPhoneOSMin) +bool Options::minOS(ld::MacVersionMin requiredMacMin, ld::IOSVersionMin requirediPhoneOSMin) { if ( fMacVersionMin != ld::macVersionUnset ) { return ( fMacVersionMin >= requiredMacMin ); } else { - return ( fIPhoneVersionMin >= requirediPhoneOSMin); + return ( fIOSVersionMin >= requirediPhoneOSMin); } } @@ -2020,7 +1987,8 @@ void Options::parse(int argc, const char* argv[]) fIgnoreOtherArchFiles = true; } else if ( strcmp(arg, "-force_cpusubtype_ALL") == 0 ) { - fForceSubtypeAll = true; + fForceSubtypeAll = true; + fAllowCpuSubtypeMismatches = true; } // Similar to -weak-l but uses the absolute path name to the library. else if ( strcmp(arg, "-weak_library") == 0 ) { @@ -2255,8 +2223,11 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-macosx_version_min") == 0 ) { setMacOSXVersionMin(argv[++i]); } - else if ( (strcmp(arg, "-iphoneos_version_min") == 0) || (strcmp(arg, "-ios_version_min") == 0) ) { - setIPhoneVersionMin(argv[++i]); + else if ( (strcmp(arg, "-ios_version_min") == 0) || (strcmp(arg, "-iphoneos_version_min") == 0) ) { + setIOSVersionMin(argv[++i]); + } + else if ( strcmp(arg, "-ios_simulator_version_min") == 0 ) { + setIOSVersionMin(argv[++i]); } else if ( strcmp(arg, "-multiply_defined") == 0 ) { //warnObsolete(arg); @@ -2589,8 +2560,7 @@ void Options::parse(int argc, const char* argv[]) loadSymbolOrderFile(argv[++i], fExportSymbolsOrder); } else if ( strcmp(arg, "-no_compact_linkedit") == 0 ) { - fMakeCompressedDyldInfo = false; - fMakeCompressedDyldInfoForceOff = true; + warnObsolete("-no_compact_linkedit"); } else if ( strcmp(arg, "-no_eh_labels") == 0 ) { fNoEHLabels = true; @@ -2604,6 +2574,9 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-no_zero_fill_sections") == 0 ) { fOptimizeZeroFill = false; } + else if ( strcmp(arg, "-merge_zero_fill_sections") == 0 ) { + fMergeZeroFill = true; + } else if ( strcmp(arg, "-objc_abi_version") == 0 ) { const char* version = argv[++i]; if ( version == NULL ) @@ -2643,16 +2616,20 @@ void Options::parse(int argc, const char* argv[]) fDemangle = true; } else if ( strcmp(arg, "-version_load_command") == 0 ) { - fVersionLoadCommand = true; + fVersionLoadCommandForcedOn = true; + fVersionLoadCommandForcedOff = false; } else if ( strcmp(arg, "-no_version_load_command") == 0 ) { - fVersionLoadCommand = false; + fVersionLoadCommandForcedOff = true; + fVersionLoadCommandForcedOn = false; } else if ( strcmp(arg, "-function_starts") == 0 ) { - fFunctionStartsLoadCommand = true; + fFunctionStartsForcedOn = true; + fFunctionStartsForcedOff = false; } else if ( strcmp(arg, "-no_function_starts") == 0 ) { - fFunctionStartsLoadCommand = false; + fFunctionStartsForcedOff = true; + fFunctionStartsForcedOn = false; } else if ( strcmp(arg, "-object_path_lto") == 0 ) { fTempLtoObjectPath = argv[++i]; @@ -2693,6 +2670,9 @@ void Options::parse(int argc, const char* argv[]) throw "-dyld_env missing ENV=VALUE"; fDyldEnvironExtras.push_back(envarg); } + else if ( strcmp(arg, "-page_align_data_atoms") == 0 ) { + fPageAlignDataAtoms = true; + } else { throwf("unknown option: %s", arg); } @@ -2954,12 +2934,6 @@ void Options::parsePreCommandLineEnvironmentSettings() if (getenv("LD_ALLOW_CPU_SUBTYPE_MISMATCHES") != NULL) fAllowCpuSubtypeMismatches = true; - // for now disable compressed linkedit functionality - if ( getenv("LD_NO_COMPACT_LINKEDIT") != NULL ) { - fMakeCompressedDyldInfo = false; - fMakeCompressedDyldInfoForceOff = true; - } - sWarningsSideFilePath = getenv("LD_WARN_FILE"); const char* customDyldPath = getenv("LD_DYLD_PATH"); @@ -3039,17 +3013,20 @@ void Options::reconfigureDefaults() // set default min OS version if ( (fMacVersionMin == ld::macVersionUnset) - && (fIPhoneVersionMin == ld::iPhoneVersionUnset) ) { + && (fIOSVersionMin == ld::iOSVersionUnset) ) { // if neither -macosx_version_min nor -iphoneos_version_min used, try environment variables const char* macVers = getenv("MACOSX_DEPLOYMENT_TARGET"); const char* iPhoneVers = getenv("IPHONEOS_DEPLOYMENT_TARGET"); const char* iOSVers = getenv("IOS_DEPLOYMENT_TARGET"); + const char* iOSSimulatorVers = getenv("IOS_SIMULATOR_DEPLOYMENT_TARGET"); if ( macVers != NULL ) setMacOSXVersionMin(macVers); else if ( iPhoneVers != NULL ) - setIPhoneVersionMin(iPhoneVers); + setIOSVersionMin(iPhoneVers); else if ( iOSVers != NULL ) - setIPhoneVersionMin(iOSVers); + setIOSVersionMin(iOSVers); + else if ( iOSSimulatorVers != NULL ) + setIOSVersionMin(iOSSimulatorVers); else { // if still nothing, set default based on architecture switch ( fArchitecture ) { @@ -3070,7 +3047,7 @@ void Options::reconfigureDefaults() if ( (fOutputKind != Options::kObjectFile) && (fOutputKind != Options::kPreload) ) { #if defined(DEFAULT_IPHONEOS_MIN_VERSION) warning("-ios_version_min not specificed, assuming " DEFAULT_IPHONEOS_MIN_VERSION); - setIPhoneVersionMin(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); @@ -3091,7 +3068,7 @@ void Options::reconfigureDefaults() // adjust min based on architecture switch ( fArchitecture ) { case CPU_TYPE_I386: - if ( (fMacVersionMin < ld::mac10_4) && (fIPhoneVersionMin == ld::iPhoneVersionUnset) ) { + if ( (fMacVersionMin < ld::mac10_4) && (fIOSVersionMin == ld::iOSVersionUnset) ) { //warning("-macosx_version_min should be 10.4 or later for i386"); fMacVersionMin = ld::mac10_4; } @@ -3120,9 +3097,18 @@ void Options::reconfigureDefaults() fAllowTextRelocs = true; fUndefinedTreatment = kUndefinedDynamicLookup; break; + case CPU_TYPE_ARM: + if ( fIOSVersionMin >= ld::iOS_5_0 ) { + // iOS 5.0 and later use new MH_KEXT_BUNDLE type + fMakeCompressedDyldInfo = false; + fMakeCompressedDyldInfoForceOff = true; + fAllowTextRelocs = true; + fUndefinedTreatment = kUndefinedDynamicLookup; + break; + } + // else use object file case CPU_TYPE_POWERPC: case CPU_TYPE_I386: - case CPU_TYPE_ARM: // use .o files fOutputKind = kObjectFile; break; @@ -3131,7 +3117,7 @@ void Options::reconfigureDefaults() // disable implicit dylibs when targeting 10.3 // add option to disable implicit load commands for indirectly used public dylibs - if ( !minOS(ld::mac10_4, ld::iPhone2_0) ) + if ( !minOS(ld::mac10_4, ld::iOS_2_0) ) fImplicitlyLinkPublicDylibs = false; @@ -3242,6 +3228,10 @@ void Options::reconfigureDefaults() // in 10.5 nothing is prebound fPrebind = false; } + else if ( fIOSVersionMin != ld::iOSVersionUnset ) { + // nothing in simulator is prebound + fPrebind = false; + } else { // in 10.3 and earlier only dylibs and main executables could be prebound switch ( fOutputKind ) { @@ -3291,7 +3281,7 @@ void Options::reconfigureDefaults() // determine if info for shared region should be added if ( fOutputKind == Options::kDynamicLibrary ) { - if ( minOS(ld::mac10_5, ld::iPhone3_1) ) + if ( minOS(ld::mac10_5, ld::iOS_3_1) ) if ( !fPrebind ) if ( (strncmp(this->installPath(), "/usr/lib/", 9) == 0) || (strncmp(this->installPath(), "/System/Library/", 16) == 0) ) @@ -3301,8 +3291,10 @@ void Options::reconfigureDefaults() // figure out if module table is needed for compatibility with old ld/dyld if ( fOutputKind == Options::kDynamicLibrary ) { switch ( fArchitecture ) { + case CPU_TYPE_I386: + if ( fIOSVersionMin != ld::iOSVersionUnset ) // simulator never needs modules + break; case CPU_TYPE_POWERPC: // 10.3 and earlier dyld requires a module table - case CPU_TYPE_I386: // ld_classic for 10.4.x requires a module table if ( fMacVersionMin <= ld::mac10_5 ) fNeedsModuleTable = true; break; @@ -3400,12 +3392,14 @@ void Options::reconfigureDefaults() 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::iPhone3_1) ) + if ( !minOS(ld::mac10_6, ld::iOS_3_1) ) fMakeCompressedDyldInfo = false; break; case CPU_TYPE_POWERPC: @@ -3446,14 +3440,14 @@ void Options::reconfigureDefaults() } // Mac OS X 10.5 and iPhoneOS 2.0 support LC_REEXPORT_DYLIB - if ( minOS(ld::mac10_5, ld::iPhone2_0) ) + if ( minOS(ld::mac10_5, ld::iOS_2_0) ) fUseSimplifiedDylibReExports = true; // Mac OS X 10.7 and iOS 4.2 support LC_LOAD_UPWARD_DYLIB - if ( minOS(ld::mac10_7, ld::iPhone4_2) && (fOutputKind == kDynamicLibrary) ) + if ( minOS(ld::mac10_7, ld::iOS_4_2) && (fOutputKind == kDynamicLibrary) ) fCanUseUpwardDylib = true; - // x86_64 for MacOSX 10.7 defaults to PIE + // MacOSX 10.7 defaults to PIE if ( ((fArchitecture == CPU_TYPE_X86_64) || (fArchitecture == CPU_TYPE_I386)) && (fOutputKind == kDynamicExecutable) && (fMacVersionMin >= ld::mac10_7) ) { @@ -3462,9 +3456,9 @@ void Options::reconfigureDefaults() // armv7 for iOS4.3 defaults to PIE if ( (fArchitecture == CPU_TYPE_ARM) - && (fSubArchitecture == CPU_SUBTYPE_ARM_V7) + && fArchSupportsThumb2 && (fOutputKind == kDynamicExecutable) - && (fIPhoneVersionMin >= ld::iPhone4_3) ) { + && (fIOSVersionMin >= ld::iOS_4_3) ) { fPositionIndependentExecutable = true; } @@ -3497,24 +3491,55 @@ void Options::reconfigureDefaults() fTLVSupport = true; } - // version load command is only in some kinds of output files + // default to adding version load command for dynamic code, static code must opt-in switch ( fOutputKind ) { case Options::kObjectFile: + fVersionLoadCommand = false; + break; case Options::kStaticExecutable: case Options::kPreload: case Options::kKextBundle: - fVersionLoadCommand = false; - fFunctionStartsLoadCommand = false; + if ( fVersionLoadCommandForcedOn ) + fVersionLoadCommand = true; break; case Options::kDynamicExecutable: case Options::kDyld: case Options::kDynamicLibrary: case Options::kDynamicBundle: + if ( !fVersionLoadCommandForcedOff ) + fVersionLoadCommand = true; + // for now, don't create version load commands for iOS simulator builds + if ( fVersionLoadCommand && (fArchitecture == CPU_TYPE_I386) ) { + for (std::vector::iterator sdkit = fSDKPaths.begin(); sdkit != fSDKPaths.end(); sdkit++) { + if ( strstr(*sdkit, "/iPhoneSimulator.platform/") != NULL ) + fVersionLoadCommand = false; + } + } break; } + // default to adding functions start for dynamic code, static code must opt-in + switch ( fOutputKind ) { + case Options::kObjectFile: + fFunctionStartsLoadCommand = false; + break; + case Options::kPreload: + case Options::kStaticExecutable: + case Options::kKextBundle: + if ( fFunctionStartsForcedOn ) + fFunctionStartsLoadCommand = true; + break; + case Options::kDynamicExecutable: + case Options::kDyld: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + if ( !fFunctionStartsForcedOff ) + fFunctionStartsLoadCommand = true; + break; + } + // support re-export of individual symbols in MacOSX 10.7 and iOS 4.2 - if ( (fOutputKind == kDynamicLibrary) && minOS(ld::mac10_7, ld::iPhone4_2) ) + if ( (fOutputKind == kDynamicLibrary) && minOS(ld::mac10_7, ld::iOS_4_2) ) fCanReExportSymbols = true; // ObjC optimization is only in dynamic final linked images @@ -3794,10 +3819,25 @@ void Options::checkIllegalOptionCombinations() fInitialUndefines.push_back(*it); } - // make sure that -init symbol exist + // make sure that -init symbol exists if ( fInitFunctionName != NULL ) fInitialUndefines.push_back(fInitFunctionName); + // make sure that entry symbol exists + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + case Options::kDyld: + case Options::kPreload: + fInitialUndefines.push_back(fEntryName); + break; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kObjectFile: + case Options::kKextBundle: + break; + } + // make sure every alias base exists for (std::vector::iterator it=fAliases.begin(); it != fAliases.end(); ++it) { fInitialUndefines.push_back(it->realName); @@ -3880,7 +3920,7 @@ void Options::checkIllegalOptionCombinations() // can't use -rpath unless targeting 10.5 or later if ( fRPaths.size() > 0 ) { - if ( !minOS(ld::mac10_5, ld::iPhone2_0) ) + if ( !minOS(ld::mac10_5, ld::iOS_2_0) ) throw "-rpath can only be used when targeting Mac OS X 10.5 or later"; switch ( fOutputKind ) { case Options::kDynamicExecutable: @@ -3900,8 +3940,8 @@ void Options::checkIllegalOptionCombinations() if ( fPositionIndependentExecutable ) { switch ( fOutputKind ) { case Options::kDynamicExecutable: - if ( !minOS(ld::mac10_5, ld::iPhone4_2) ) { - if ( fIPhoneVersionMin == ld::iPhoneVersionUnset ) + 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"; else throw "-pie can only be used when targeting iOS 4.2 or later"; @@ -3949,7 +3989,7 @@ void Options::checkIllegalOptionCombinations() if ( !fReExportSymbols.empty() ) { if ( fOutputKind != Options::kDynamicLibrary ) throw "-reexported_symbols_list can only used used when created dynamic libraries"; - if ( !minOS(ld::mac10_7, ld::iPhone4_2) ) + if ( !minOS(ld::mac10_7, ld::iOS_4_2) ) throw "targeted OS version does not support -reexported_symbols_list"; } @@ -4031,33 +4071,9 @@ void Options::checkForClassic(int argc, const char* argv[]) switch ( fArchitecture ) { case CPU_TYPE_I386: case CPU_TYPE_POWERPC: - case CPU_TYPE_ARM: if ( (staticFound || kextFound) && !newLinker ) { // this environment variable will disable use of ld_classic for -static links if ( getenv("LD_NO_CLASSIC_LINKER_STATIC") == NULL ) { - // ld_classic does not support -iphoneos_version_min, so change - for(int j=0; j < argc; ++j) { - if ( (strcmp(argv[j], "-iphoneos_version_min") == 0) || (strcmp(argv[j], "-ios_version_min") == 0) ) { - argv[j] = "-macosx_version_min"; - if ( j < argc-1 ) - argv[j+1] = "10.5"; - break; - } - } - // ld classic does not understand -kext (change to -static -r) - if ( kextFound ) { - for(int j=0; j < argc; ++j) { - if ( strcmp(argv[j], "-kext") == 0) - argv[j] = "-r"; - else if ( strcmp(argv[j], "-dynamic") == 0) - argv[j] = "-static"; - } - } - // ld classic does not understand -demangle - for(int j=0; j < argc; ++j) { - if ( strcmp(argv[j], "-demangle") == 0) - argv[j] = "-noprebind"; - } this->gotoClassicLinker(argc, argv); } } @@ -4074,7 +4090,29 @@ void Options::checkForClassic(int argc, const char* argv[]) void Options::gotoClassicLinker(int argc, const char* argv[]) { + warning("using ld_classic"); argv[0] = "ld_classic"; + // ld_classic does not support -iphoneos_version_min, so change + for(int j=0; j < argc; ++j) { + if ( (strcmp(argv[j], "-iphoneos_version_min") == 0) || (strcmp(argv[j], "-ios_version_min") == 0) ) { + argv[j] = "-macosx_version_min"; + if ( j < argc-1 ) + argv[j+1] = "10.5"; + break; + } + } + // ld classic does not understand -kext (change to -static -r) + for(int j=0; j < argc; ++j) { + if ( strcmp(argv[j], "-kext") == 0) + argv[j] = "-r"; + else if ( strcmp(argv[j], "-dynamic") == 0) + argv[j] = "-static"; + } + // ld classic does not understand -demangle + for(int j=0; j < argc; ++j) { + if ( strcmp(argv[j], "-demangle") == 0) + argv[j] = "-noprebind"; + } // in -v mode, print command line passed to ld_classic for(int i=0; i < argc; ++i) { if ( strcmp(argv[i], "-v") == 0 ) { diff --git a/src/ld/Options.h b/src/ld/Options.h index 7ce5766..d9becf3 100644 --- a/src/ld/Options.h +++ b/src/ld/Options.h @@ -150,6 +150,7 @@ public: bool forceCpuSubtypeAll() const { return fForceSubtypeAll; } const char* architectureName() const { return fArchitectureName; } void setArchitecture(cpu_type_t, cpu_subtype_t subtype); + bool archSupportsThumb2() const { return fArchSupportsThumb2; } OutputKind outputKind() const { return fOutputKind; } bool prebind() const { return fPrebind; } bool bindAtLoad() const { return fBindAtLoad; } @@ -178,8 +179,8 @@ public: bool deadCodeStrip() const { return fDeadStrip; } UndefinedTreatment undefinedTreatment() const { return fUndefinedTreatment; } ld::MacVersionMin macosxVersionMin() const { return fMacVersionMin; } - ld::IPhoneVersionMin iphoneOSVersionMin() const { return fIPhoneVersionMin; } - bool minOS(ld::MacVersionMin mac, ld::IPhoneVersionMin iPhoneOS); + ld::IOSVersionMin iOSVersionMin() const { return fIOSVersionMin; } + bool minOS(ld::MacVersionMin mac, ld::IOSVersionMin iPhoneOS); bool messagesPrefixedWithArchitecture(); Treatment picTreatment(); WeakReferenceMismatchTreatment weakReferenceMismatchTreatment() const { return fWeakReferenceMismatchTreatment; } @@ -262,6 +263,7 @@ public: bool loadAllObjcObjectsFromArchives() const { return fLoadAllObjcObjectsFromArchives; } bool autoOrderInitializers() const { return fAutoOrderInitializers; } bool optimizeZeroFill() const { return fOptimizeZeroFill; } + bool mergeZeroFill() const { return fMergeZeroFill; } bool logAllFiles() const { return fLogAllFiles; } DebugInfoStripping debugInfoStripping() const { return fDebugInfoStripping; } bool flatNamespace() const { return fFlatNamespace; } @@ -285,6 +287,7 @@ public: bool canReExportSymbols() const { return fCanReExportSymbols; } const char* tempLtoObjectPath() const { return fTempLtoObjectPath; } bool objcCategoryMerging() const { return fObjcCategoryMerging; } + bool pageAlignDataAtoms() const { return fPageAlignDataAtoms; } bool hasWeakBitTweaks() const; bool forceWeak(const char* symbolName) const; bool forceNotWeak(const char* symbolName) const; @@ -345,7 +348,7 @@ private: void parsePostCommandLineEnvironmentSettings(); void setUndefinedTreatment(const char* treatment); void setMacOSXVersionMin(const char* version); - void setIPhoneVersionMin(const char* version); + void setIOSVersionMin(const char* version); void setWeakReferenceMismatchTreatment(const char* treatment); void addDylibOverride(const char* paths); void addSectionAlignment(const char* segment, const char* section, const char* alignment); @@ -369,6 +372,7 @@ private: const char* fArchitectureName; OutputKind fOutputKind; bool fHasPreferredSubType; + bool fArchSupportsThumb2; bool fPrebind; bool fBindAtLoad; bool fKeepPrivateExterns; @@ -476,6 +480,7 @@ private: bool fRemoveDwarfUnwindIfCompactExists; bool fAutoOrderInitializers; bool fOptimizeZeroFill; + bool fMergeZeroFill; bool fLogObjectFiles; bool fLogAllFiles; bool fTraceDylibs; @@ -489,13 +494,18 @@ private: bool fDemangle; bool fTLVSupport; bool fVersionLoadCommand; + bool fVersionLoadCommandForcedOn; + bool fVersionLoadCommandForcedOff; bool fFunctionStartsLoadCommand; + bool fFunctionStartsForcedOn; + bool fFunctionStartsForcedOff; bool fCanReExportSymbols; bool fObjcCategoryMerging; + bool fPageAlignDataAtoms; DebugInfoStripping fDebugInfoStripping; const char* fTraceOutputFile; ld::MacVersionMin fMacVersionMin; - ld::IPhoneVersionMin fIPhoneVersionMin; + ld::IOSVersionMin fIOSVersionMin; std::vector fAliases; std::vector fInitialUndefines; NameSet fAllowedUndefined; diff --git a/src/ld/OutputFile.cpp b/src/ld/OutputFile.cpp index 963e9e5..65e8b8c 100644 --- a/src/ld/OutputFile.cpp +++ b/src/ld/OutputFile.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* * - * Copyright (c) 2009-2010 Apple Inc. All rights reserved. + * Copyright (c) 2009-2011 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -320,10 +320,25 @@ void OutputFile::setSectionSizesAndAlignments(ld::Internal& state) uint64_t offset = 0; for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { const ld::Atom* atom = *ait; - if ( atom->alignment().powerOf2 > maxAlignment ) - maxAlignment = atom->alignment().powerOf2; + bool pagePerAtom = false; + uint32_t atomAlignmentPowerOf2 = atom->alignment().powerOf2; + if ( _options.pageAlignDataAtoms() && ( strcmp(atom->section().segmentName(), "__DATA") == 0) ) { + switch ( atom->section().type() ) { + case ld::Section::typeUnclassified: + case ld::Section::typeTentativeDefs: + case ld::Section::typeZeroFill: + pagePerAtom = true; + if ( atomAlignmentPowerOf2 < 12 ) + atomAlignmentPowerOf2 = 12; + break; + default: + break; + } + } + if ( atomAlignmentPowerOf2 > maxAlignment ) + maxAlignment = atomAlignmentPowerOf2; // calculate section offset for this atom - uint64_t alignment = 1 << atom->alignment().powerOf2; + uint64_t alignment = 1 << atomAlignmentPowerOf2; uint64_t currentModulus = (offset % alignment); uint64_t requiredModulus = atom->alignment().modulus; if ( currentModulus != requiredModulus ) { @@ -336,6 +351,9 @@ void OutputFile::setSectionSizesAndAlignments(ld::Internal& state) if ( sect->type() != ld::Section::typeLinkEdit ) { (const_cast(atom))->setSectionOffset(offset); offset += atom->size(); + if ( pagePerAtom ) { + offset = (offset + 4095) & (-4096); // round up to end of page + } } if ( (atom->scope() == ld::Atom::scopeGlobal) && (atom->definition() == ld::Atom::definitionRegular) @@ -581,7 +599,7 @@ void OutputFile::assignFileOffsets(ld::Internal& state) for (std::vector::iterator it = state.sections.begin(); it != state.sections.end(); ++it) { ld::Internal::FinalSection* sect = *it; if ( strcmp(sect->segmentName(), "__TEXT") == 0 ) { - _encryptedTEXTendOffset = pageAlign(sect->fileOffset); + _encryptedTEXTendOffset = pageAlign(sect->fileOffset + sect->size); } } } @@ -779,10 +797,43 @@ void OutputFile::rangeCheckBranch32(int64_t displacement, ld::Internal& state, c } } + +void OutputFile::rangeCheckAbsolute32(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) +{ + const int64_t fourGigLimit = 0xFFFFFFFF; + if ( displacement > fourGigLimit ) { + // cannot enforce 32-bit range checks on 32-bit archs because assembler loses sign information + // .long _foo - 0xC0000000 + // is encoded in mach-o the same as: + // .long _foo + 0x40000000 + // so if _foo lays out to 0xC0000100, the first is ok, but the second is not. + if ( (_options.architecture() == CPU_TYPE_ARM) || (_options.architecture() == CPU_TYPE_I386) ) { + // Unlikely userland code does funky stuff like this, so warn for them, but not warn for -preload + if ( _options.outputKind() != Options::kPreload ) { + warning("32-bit absolute address out of range (0x%08llX max is 4GB): from %s + 0x%08X (0x%08llX) to 0x%08llX", + displacement, atom->name(), fixup->offsetInAtom, atom->finalAddress(), displacement); + } + return; + } + // show layout of final image + printSectionLayout(state); + + const ld::Atom* target; + if ( fixup->binding == ld::Fixup::bindingNone ) + throwf("32-bit absolute address out of range (0x%08llX max is 4GB): from %s + 0x%08X (0x%08llX) to 0x%08llX", + displacement, atom->name(), fixup->offsetInAtom, atom->finalAddress(), displacement); + else + throwf("32-bit absolute address out of range (0x%08llX max is 4GB): from %s + 0x%08X (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), fixup->offsetInAtom, atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); + } +} + + void OutputFile::rangeCheckRIP32(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) { const int64_t twoGigLimit = 0x7FFFFFFF; - if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) { + if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) { // show layout of final image printSectionLayout(state); @@ -822,8 +873,8 @@ void OutputFile::rangeCheckARMBranch24(int64_t displacement, ld::Internal& state void OutputFile::rangeCheckThumbBranch22(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) { - // armv7 supports a larger displacement - if ( _options.preferSubArchitecture() && (_options.subArchitecture() == CPU_SUBTYPE_ARM_V7) ) { + // thumb2 supports a larger displacement + if ( _options.preferSubArchitecture() && _options.archSupportsThumb2() ) { if ( (displacement > 16777214LL) || (displacement < (-16777216LL)) ) { // show layout of final image printSectionLayout(state); @@ -967,6 +1018,7 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: set32LE(fixUpLocation, (get32LE(fixUpLocation) & 0xFF000000) | (accumulator & 0x00FFFFFF) ); break; case ld::Fixup::kindStoreLittleEndian32: + rangeCheckAbsolute32(accumulator, state, atom, fit); set32LE(fixUpLocation, accumulator); break; case ld::Fixup::kindStoreLittleEndian64: @@ -979,6 +1031,7 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: set32BE(fixUpLocation, (get32BE(fixUpLocation) & 0xFF000000) | (accumulator & 0x00FFFFFF) ); break; case ld::Fixup::kindStoreBigEndian32: + rangeCheckAbsolute32(accumulator, state, atom, fit); set32BE(fixUpLocation, accumulator); break; case ld::Fixup::kindStoreBigEndian64: @@ -1198,6 +1251,7 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: accumulator |= 1; if ( fit->contentAddendOnly ) accumulator = 0; + rangeCheckAbsolute32(accumulator, state, atom, fit); set32LE(fixUpLocation, accumulator); break; case ld::Fixup::kindStoreTargetAddressLittleEndian64: @@ -1287,6 +1341,7 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: // Make sure we are calling arm with bl, thumb with blx is_bl = ((instruction & 0xFF000000) == 0xEB000000); is_blx = ((instruction & 0xFE000000) == 0xFA000000); + is_b = !is_blx && ((instruction & 0x0F000000) == 0x0A000000); if ( is_bl && thumbTarget ) { uint32_t opcode = 0xFA000000; uint32_t disp = (uint32_t)(delta >> 2) & 0x00FFFFFF; @@ -1298,6 +1353,13 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: uint32_t disp = (uint32_t)(delta >> 2) & 0x00FFFFFF; newInstruction = opcode | disp; } + else if ( is_b && thumbTarget ) { + if ( fit->contentDetlaToAddendOnly ) + newInstruction = (instruction & 0xFF000000) | ((uint32_t)(delta >> 2) & 0x00FFFFFF); + else + throwf("no pc-rel bx arm instruction. Can't fix up branch to %s in %s", + referenceTargetAtomName(state, fit), atom->name()); + } else if ( !is_bl && !is_blx && thumbTarget ) { throwf("don't know how to convert instruction %x referencing %s to thumb", instruction, referenceTargetAtomName(state, fit)); @@ -1324,14 +1386,14 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: // Since blx cannot have the low bit set, set bit[1] of the target to // bit[1] of the base address, so that the difference is a multiple of // 4 bytes. - if ( !thumbTarget ) { + if ( !thumbTarget && !fit->contentDetlaToAddendOnly ) { accumulator &= -3ULL; accumulator |= ((atom->finalAddress() + fit->offsetInAtom ) & 2LL); } // The pc added will be +4 from the pc delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 4); rangeCheckThumbBranch22(delta, state, atom, fit); - if ( _options.preferSubArchitecture() && _options.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) { + if ( _options.preferSubArchitecture() && _options.archSupportsThumb2() ) { // The instruction is really two instructions: // The lower 16 bits are the first instruction, which contains the high // 11 bits of the displacement. @@ -1357,12 +1419,13 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: instruction = 0xC000F000; // keep blx } else if ( is_b ) { - if ( !thumbTarget ) - throwf("don't know how to convert instruction %x referencing %s to arm", - instruction, referenceTargetAtomName(state, fit)); instruction = 0x9000F000; // keep b + if ( !thumbTarget && !fit->contentDetlaToAddendOnly ) { + throwf("armv7 has no pc-rel bx thumb instruction. Can't fix up branch to %s in %s", + referenceTargetAtomName(state, fit), atom->name()); + } } - else if ( is_b ) { + else { if ( !thumbTarget ) throwf("don't know how to convert branch instruction %x referencing %s to bx", instruction, referenceTargetAtomName(state, fit)); @@ -1389,10 +1452,13 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: else if ( is_blx && thumbTarget ) { instruction = 0xF800F000; } - else if ( !is_bl && !is_blx && !thumbTarget ) { - throwf("don't know how to convert instruction %x referencing %s to arm", - instruction, referenceTargetAtomName(state, fit)); - } + else if ( is_b ) { + instruction = 0x9000F000; // keep b + if ( !thumbTarget && !fit->contentDetlaToAddendOnly ) { + throwf("armv6 has no pc-rel bx thumb instruction. Can't fix up branch to %s in %s", + referenceTargetAtomName(state, fit), atom->name()); + } + } else { instruction = instruction & 0xF800F800; } @@ -1456,7 +1522,7 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: } } -void OutputFile::copyNoOps(uint8_t* from, uint8_t* to) +void OutputFile::copyNoOps(uint8_t* from, uint8_t* to, bool thumb) { switch ( _options.architecture() ) { case CPU_TYPE_POWERPC: @@ -1469,9 +1535,14 @@ void OutputFile::copyNoOps(uint8_t* from, uint8_t* to) *p = 0x90; break; case CPU_TYPE_ARM: - // fixme: need thumb nop? - for (uint8_t* p=from; p < to; p += 4) - OSWriteBigInt32((uint32_t*)p, 0, 0xe1a00000); + if ( thumb ) { + for (uint8_t* p=from; p < to; p += 2) + OSWriteLittleInt16((uint16_t*)p, 0, 0x46c0); + } + else { + for (uint8_t* p=from; p < to; p += 4) + OSWriteLittleInt32((uint32_t*)p, 0, 0xe1a00000); + } break; default: for (uint8_t* p=from; p < to; ++p) @@ -1555,6 +1626,7 @@ void OutputFile::writeOutputFile(ld::Internal& state) const bool sectionUsesNops = (sect->type() == ld::Section::typeCode); //fprintf(stderr, "file offset=0x%08llX, section %s\n", sect->fileOffset, sect->sectionName()); std::vector& atoms = sect->atoms; + bool lastAtomWasThumb = false; for (std::vector::iterator ait = atoms.begin(); ait != atoms.end(); ++ait) { const ld::Atom* atom = *ait; if ( atom->definition() == ld::Atom::definitionProxy ) @@ -1563,7 +1635,7 @@ void OutputFile::writeOutputFile(ld::Internal& state) uint64_t fileOffset = atom->finalAddress() - sect->address + sect->fileOffset; // check for alignment padding between atoms if ( (fileOffset != fileOffsetOfEndOfLastAtom) && lastAtomUsesNoOps ) { - this->copyNoOps(&wholeBuffer[fileOffsetOfEndOfLastAtom], &wholeBuffer[fileOffset]); + this->copyNoOps(&wholeBuffer[fileOffsetOfEndOfLastAtom], &wholeBuffer[fileOffset], lastAtomWasThumb); } // copy atom content atom->copyRawContent(&wholeBuffer[fileOffset]); @@ -1571,6 +1643,7 @@ void OutputFile::writeOutputFile(ld::Internal& state) this->applyFixUps(state, mhAddress, atom, &wholeBuffer[fileOffset]); fileOffsetOfEndOfLastAtom = fileOffset+atom->size(); lastAtomUsesNoOps = sectionUsesNops; + lastAtomWasThumb = atom->isThumb(); } catch (const char* msg) { if ( atom->file() != NULL ) @@ -1658,6 +1731,19 @@ struct AtomByNameSorter } }; +class NotInSet +{ +public: + NotInSet(const std::set& theSet) : _set(theSet) {} + + bool operator()(const ld::Atom* atom) const { + return ( _set.count(atom) == 0 ); + } +private: + const std::set& _set; +}; + + void OutputFile::buildSymbolTable(ld::Internal& state) { unsigned int machoSectionIndex = 0; @@ -1800,7 +1886,11 @@ void OutputFile::buildSymbolTable(ld::Internal& state) } } else { - if ( _options.keepLocalSymbol(atom->name()) ) + if ( _options.keepLocalSymbol(atom->name()) ) + _localAtoms.push_back(atom); + // ld should never have a symbol in the non-lazy indirect symbol table with index 0 + // this works by making __mh_execute_header be a local symbol which takes symbol index 0 + else if ( (atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip) && !_options.makeCompressedDyldInfo() ) _localAtoms.push_back(atom); else (const_cast(atom))->setSymbolTableInclusion(ld::Atom::symbolTableNotIn); @@ -1810,10 +1900,35 @@ void OutputFile::buildSymbolTable(ld::Internal& state) } } + // ld adds undefined symbol from .exp file to binary + if ( (_options.outputKind() == Options::kKextBundle) && _options.hasExportRestrictList() ) { + // search for referenced undefines + std::set referencedProxyAtoms; + for (std::vector::iterator sit=state.sections.begin(); sit != state.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; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + switch ( fit->binding ) { + case ld::Fixup::bindingsIndirectlyBound: + referencedProxyAtoms.insert(state.indirectBindingTable[fit->u.bindingIndex]); + break; + case ld::Fixup::bindingDirectlyBound: + referencedProxyAtoms.insert(fit->u.target); + break; + default: + break; + } + } + } + } + // remove any unreferenced _importedAtoms + _importedAtoms.erase(std::remove_if(_importedAtoms.begin(), _importedAtoms.end(), NotInSet(referencedProxyAtoms)), _importedAtoms.end()); + } + // sort by name std::sort(_exportedAtoms.begin(), _exportedAtoms.end(), AtomByNameSorter()); std::sort(_importedAtoms.begin(), _importedAtoms.end(), AtomByNameSorter()); - } void OutputFile::addPreloadLinkEdit(ld::Internal& state) @@ -2583,7 +2698,7 @@ 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.iphoneOSVersionMin() >= ld::iPhone4_3) || (_options.macosxVersionMin() >= ld::mac10_7)) ) { + else if ( _options.positionIndependentExecutable() && ((_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. " @@ -2592,8 +2707,11 @@ void OutputFile::noteTextReloc(const ld::Atom* atom, const ld::Atom* target) } this->pieDisabled = true; } + else if ( (target->scope() == ld::Atom::scopeGlobal) && (target->combine() == ld::Atom::combineByName) ) { + throwf("illegal text-relocoation (direct reference) to (global,weak) %s in %s from %s in %s", target->name(), target->file()->path(), atom->name(), atom->file()->path()); + } else { - throwf("illegal text reloc to %s from %s in %s", target->name(), target->file()->path(), atom->name()); + throwf("illegal text-relocation to %s in %s from %s in %s", target->name(), target->file()->path(), atom->name(), atom->file()->path()); } } @@ -2608,8 +2726,22 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s // no need to rebase or bind PCRel stores if ( this->isPcRelStore(fixupWithStore->kind) ) { // as long as target is in same linkage unit - if ( (target == NULL) || (target->definition() != ld::Atom::definitionProxy) ) + if ( (target == NULL) || (target->definition() != ld::Atom::definitionProxy) ) { + // make sure target is not global and weak + if ( (target->scope() == ld::Atom::scopeGlobal) && (target->combine() == ld::Atom::combineByName) && (target->definition() == ld::Atom::definitionRegular)) { + if ( (atom->section().type() == ld::Section::typeCFI) + || (atom->section().type() == ld::Section::typeDtraceDOF) + || (atom->section().type() == ld::Section::typeUnwindInfo) ) { + // ok for __eh_frame and __uwind_info to use pointer diffs to global weak symbols + return; + } + // Have direct reference to weak-global. This should be an indrect reference + 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()); + } return; + } } // no need to rebase or bind PIC internal pointer diff @@ -2623,13 +2755,18 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s return; } - // make sure target is not global and weak - if ( (target->scope() == ld::Atom::scopeGlobal) && (target->combine() == ld::Atom::combineByName) - && (atom->section().type() != ld::Section::typeCFI) - && (atom->section().type() != ld::Section::typeDtraceDOF) - && (atom->section().type() != ld::Section::typeUnwindInfo) ) { - // ok for __eh_frame and __uwind_info to use pointer diffs to global weak symbols - throwf("bad codegen, pointer diff in %s to global weak symbol %s", atom->name(), target->name()); + // check if target of pointer-diff is global and weak + if ( (target->scope() == ld::Atom::scopeGlobal) && (target->combine() == ld::Atom::combineByName) && (target->definition() == ld::Atom::definitionRegular) ) { + if ( (atom->section().type() == ld::Section::typeCFI) + || (atom->section().type() == ld::Section::typeDtraceDOF) + || (atom->section().type() == ld::Section::typeUnwindInfo) ) { + // ok for __eh_frame and __uwind_info to use pointer diffs to global weak symbols + return; + } + // Have direct reference to weak-global. This should be an indrect reference + 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()); } return; } @@ -2651,7 +2788,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->willBeWeakLinked())); + bool weak_import = ((dylib != NULL) && (fixupWithTarget->weakImport || dylib->forcedWeakLinked())); uint64_t address = atom->finalAddress() + fixupWithTarget->offsetInAtom; uint64_t addend = targetAddend - minusTargetAddend; @@ -2764,10 +2901,6 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s } if ( needsWeakBinding ) _weakBindingInfo.push_back(BindingInfo(type, 0, target->name(), false, address, addend)); - - // record if weak imported - if ( weak_import && (target->definition() == ld::Atom::definitionProxy) ) - (const_cast(target))->setWeakImported(); } @@ -2783,10 +2916,6 @@ void OutputFile::addClassicRelocs(ld::Internal& state, ld::Internal::FinalSectio if ( (sect->type() == ld::Section::typeNonLazyPointer) && (_options.outputKind() != Options::kKextBundle) ) { assert(target != NULL); assert(fixupWithTarget != NULL); - // record if weak imported - const ld::dylib::File* dylib = dynamic_cast(target->file()); - if ( (dylib != NULL) && (fixupWithTarget->weakImport || dylib->willBeWeakLinked()) ) - (const_cast(target))->setWeakImported(); return; } @@ -2828,12 +2957,7 @@ void OutputFile::addClassicRelocs(ld::Internal& state, ld::Internal::FinalSectio switch ( fixupWithStore->kind ) { case ld::Fixup::kindLazyTarget: - { - // lazy pointers don't need relocs, but might need weak_import bit set - const ld::dylib::File* dylib = dynamic_cast(target->file()); - if ( (dylib != NULL) && (fixupWithTarget->weakImport || dylib->willBeWeakLinked()) ) - (const_cast(target))->setWeakImported(); - } + // lazy pointers don't need relocs break; case ld::Fixup::kindStoreLittleEndian32: case ld::Fixup::kindStoreLittleEndian64: @@ -2893,9 +3017,6 @@ void OutputFile::addClassicRelocs(ld::Internal& state, ld::Internal::FinalSectio _externalRelocsAtom->addExternalPointerReloc(relocAddress, target); sect->hasExternalRelocs = true; fixupWithTarget->contentAddendOnly = true; - // record if weak imported - if ( (dylib != NULL) && (fixupWithTarget->weakImport || dylib->willBeWeakLinked()) ) - (const_cast(target))->setWeakImported(); } else if ( needsLocalReloc ) { assert(target != NULL); @@ -2933,6 +3054,21 @@ void OutputFile::addClassicRelocs(ld::Internal& state, ld::Internal::FinalSectio } } break; + + case ld::Fixup::kindStoreARMLow16: + case ld::Fixup::kindStoreThumbLow16: + // no way to encode rebasing of binding for these instructions + if ( _options.outputSlidable() || (target->definition() == ld::Atom::definitionProxy) ) + throwf("no supported runtime lo16 relocation in %s from %s to %s", atom->name(), atom->file()->path(), target->name()); + break; + + case ld::Fixup::kindStoreARMHigh16: + case ld::Fixup::kindStoreThumbHigh16: + // no way to encode rebasing of binding for these instructions + if ( _options.outputSlidable() || (target->definition() == ld::Atom::definitionProxy) ) + throwf("no supported runtime hi16 relocation in %s from %s to %s", atom->name(), atom->file()->path(), target->name()); + break; + default: break; } @@ -2945,6 +3081,22 @@ bool OutputFile::useExternalSectionReloc(const ld::Atom* atom, const ld::Atom* t // x86_64 uses external relocations for everthing that has a symbol return ( target->symbolTableInclusion() != ld::Atom::symbolTableNotIn ); } + + // support arm branch interworking in -r mode + if ( (_options.architecture() == CPU_TYPE_ARM) && (_options.outputKind() == Options::kObjectFile) ) { + if ( atom->isThumb() != target->isThumb() ) { + switch ( fixupWithTarget->kind ) { + // have branch that switches mode, then might be 'b' not 'bl' + // Force external relocation, since no way to do local reloc for 'b' + case ld::Fixup::kindStoreTargetAddressThumbBranch22 : + case ld::Fixup::kindStoreTargetAddressARMBranch24: + return true; + default: + break; + } + } + } + // most architectures use external relocations only for references // to a symbol in another translation unit or for references to "weak symbols" or tentative definitions assert(target != NULL); @@ -3035,27 +3187,30 @@ void OutputFile::makeSplitSegInfo(ld::Internal& state) for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { const ld::Atom* atom = *ait; const ld::Atom* target = NULL; + const ld::Atom* fromTarget = NULL; + uint64_t accumulator = 0; + bool thumbTarget; bool hadSubtract = false; for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { if ( fit->firstInCluster() ) target = NULL; - if ( fit->kind == ld::Fixup::kindSubtractTargetAddress ) { - hadSubtract = true; - continue; + if ( this->setsTarget(fit->kind) ) { + accumulator = addressOf(state, fit, &target); + thumbTarget = targetIsThumb(state, fit); + if ( thumbTarget ) + accumulator |= 1; } - switch ( fit->binding ) { - case ld::Fixup::bindingNone: - case ld::Fixup::bindingByNameUnbound: + switch ( fit->kind ) { + case ld::Fixup::kindSubtractTargetAddress: + accumulator -= addressOf(state, fit, &fromTarget); + hadSubtract = true; break; - case ld::Fixup::bindingByContentBound: - case ld::Fixup::bindingDirectlyBound: - target = fit->u.target; + case ld::Fixup::kindAddAddend: + accumulator += fit->u.addend; break; - case ld::Fixup::bindingsIndirectlyBound: - target = state.indirectBindingTable[fit->u.bindingIndex]; + case ld::Fixup::kindSubtractAddend: + accumulator -= fit->u.addend; break; - } - switch ( fit->kind ) { case ld::Fixup::kindStoreBigEndian32: case ld::Fixup::kindStoreLittleEndian32: case ld::Fixup::kindStoreLittleEndian64: @@ -3065,6 +3220,7 @@ void OutputFile::makeSplitSegInfo(ld::Internal& state) // there is also a text reloc which update_dyld_shared_cache will use. if ( ! hadSubtract ) break; + // fall through case ld::Fixup::kindStoreX86PCRel32: case ld::Fixup::kindStoreX86PCRel32_1: case ld::Fixup::kindStoreX86PCRel32_2: @@ -3076,11 +3232,27 @@ void OutputFile::makeSplitSegInfo(ld::Internal& state) case ld::Fixup::kindStoreTargetAddressX86PCRel32: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: + case ld::Fixup::kindStoreARMLow16: + case ld::Fixup::kindStoreThumbLow16: assert(target != NULL); if ( strcmp(sect->segmentName(), target->section().segmentName()) != 0 ) { _splitSegInfos.push_back(SplitSegInfoEntry(atom->finalAddress()+fit->offsetInAtom,fit->kind)); } break; + case ld::Fixup::kindStoreARMHigh16: + case ld::Fixup::kindStoreThumbHigh16: + assert(target != NULL); + if ( strcmp(sect->segmentName(), target->section().segmentName()) != 0 ) { + // hi16 needs to know upper 4-bits of low16 to compute carry + uint32_t extra = (accumulator >> 12) & 0xF; + _splitSegInfos.push_back(SplitSegInfoEntry(atom->finalAddress()+fit->offsetInAtom,fit->kind, extra)); + } + break; + case ld::Fixup::kindSetTargetImageOffset: + accumulator = addressOf(state, fit, &target); + assert(target != NULL); + hadSubtract = true; + break; default: break; } @@ -3218,8 +3390,8 @@ public: return (leftFileOrdinal < rightFileOrdinal); // then sort by atom objectAddress - uint64_t leftAddr = left->objectAddress(); - uint64_t rightAddr = right->objectAddress(); + uint64_t leftAddr = left->finalAddress(); + uint64_t rightAddr = right->finalAddress(); return leftAddr < rightAddr; } }; @@ -3315,7 +3487,7 @@ void OutputFile::synthesizeDebugNotes(ld::Internal& state) const ld::relocatable::File* atomObjFile = dynamic_cast(atomFile); const char* newDirPath; const char* newFilename; - //fprintf(stderr, "debug note for %s\n", atom->getDisplayName()); + //fprintf(stderr, "debug note for %s\n", atom->name()); if ( atom->translationUnitSource(&newDirPath, &newFilename) ) { // need SO's whenever the translation unit source file changes if ( newFilename != filename ) { @@ -3370,7 +3542,7 @@ void OutputFile::synthesizeDebugNotes(ld::Internal& state) // add the source file path to seenFiles so it does not show up in SOLs seenFiles.insert(newFilename); char* fullFilePath; - asprintf(&fullFilePath, "%s/%s", newDirPath, newFilename); + asprintf(&fullFilePath, "%s%s", newDirPath, newFilename); // add both leaf path and full path seenFiles.insert(fullFilePath); } diff --git a/src/ld/OutputFile.h b/src/ld/OutputFile.h index 9b49289..415d24b 100644 --- a/src/ld/OutputFile.h +++ b/src/ld/OutputFile.h @@ -130,9 +130,10 @@ public: }; struct SplitSegInfoEntry { - SplitSegInfoEntry(uint64_t a, ld::Fixup::Kind k) : address(a), kind(k) {} + SplitSegInfoEntry(uint64_t a, ld::Fixup::Kind k, uint32_t e=0) : address(a), kind(k), extra(e) {} uint64_t address; ld::Fixup::Kind kind; + uint32_t extra; }; private: @@ -175,7 +176,7 @@ private: uint64_t addressOf(const ld::Internal& state, const ld::Fixup* fixup, const ld::Atom** target); bool targetIsThumb(ld::Internal& state, const ld::Fixup* fixup); uint32_t lazyBindingInfoOffsetForLazyPointerAddress(uint64_t lpAddress); - void copyNoOps(uint8_t* from, uint8_t* to); + void copyNoOps(uint8_t* from, uint8_t* to, bool thumb); bool isPointerToTarget(ld::Fixup::Kind kind); bool isPointerFromTarget(ld::Fixup::Kind kind); bool isPcRelStore(ld::Fixup::Kind kind); @@ -199,6 +200,8 @@ private: const ld::Fixup* fixup); void rangeCheckBranch32(int64_t delta, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup); + void rangeCheckAbsolute32(int64_t delta, ld::Internal& state, const ld::Atom* atom, + const ld::Fixup* fixup); void rangeCheckRIP32(int64_t delta, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup); void rangeCheckARM12(int64_t delta, ld::Internal& state, const ld::Atom* atom, diff --git a/src/ld/Resolver.cpp b/src/ld/Resolver.cpp index e17e632..a56cefe 100644 --- a/src/ld/Resolver.cpp +++ b/src/ld/Resolver.cpp @@ -394,6 +394,10 @@ void Resolver::doFile(const ld::File& file) else if ( nextObjectSubType == CPU_SUBTYPE_ARM_ALL ) { warning("CPU_SUBTYPE_ARM_ALL subtype is deprecated: %s", file.path()); } + else if ( _options.allowSubArchitectureMismatches() ) { + //warning("object file %s was built for different arm sub-type (%d) than link command line (%d)", + // file.path(), nextObjectSubType, _options.subArchitecture()); + } else { throwf("object file %s was built for different arm sub-type (%d) than link command line (%d)", file.path(), nextObjectSubType, _options.subArchitecture()); @@ -467,11 +471,11 @@ void Resolver::doAtom(const ld::Atom& atom) // marking proxy atom as global triggers the re-export (const_cast(&atom))->setScope(ld::Atom::scopeGlobal); } - else { + else if ( _options.outputKind() == Options::kDynamicLibrary ) { if ( atom.file() != NULL ) - warning("cannot re-export symbol %s from %s\n", SymbolTable::demangle(name), atom.file()->path()); + warning("target OS does not support re-exporting symbol %s from %s\n", SymbolTable::demangle(name), atom.file()->path()); else - warning("cannot re-export symbol %s\n", SymbolTable::demangle(name)); + warning("target OS does not support re-exporting symbol %s\n", SymbolTable::demangle(name)); } } else { @@ -639,7 +643,7 @@ void Resolver::resolveUndefines() const char* undef = *it; // load for previous undefine may also have loaded this undefine, so check again if ( ! _symbolTable.hasName(undef) ) { - _inputFiles.searchLibraries(undef, true, true, *this); + _inputFiles.searchLibraries(undef, true, true, false, *this); if ( !_symbolTable.hasName(undef) && (_options.outputKind() != Options::kObjectFile) ) { if ( strncmp(undef, "section$", 8) == 0 ) { if ( strncmp(undef, "section$start$", 14) == 0 ) { @@ -686,7 +690,7 @@ void Resolver::resolveUndefines() const ld::Atom* curAtom = _symbolTable.atomForSlot(_symbolTable.findSlotForName(*it)); assert(curAtom != NULL); if ( curAtom->definition() == ld::Atom::definitionTentative ) { - _inputFiles.searchLibraries(*it, searchDylibs, true, *this); + _inputFiles.searchLibraries(*it, searchDylibs, true, true, *this); } } } @@ -760,6 +764,10 @@ void Resolver::markLive(const ld::Atom& atom, WhyLiveBackChain* previous) case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoad: + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoadNowLEA: + case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoad: + case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoadNowLEA: case ld::Fixup::kindStoreTargetAddressARMBranch24: case ld::Fixup::kindStoreTargetAddressThumbBranch22: case ld::Fixup::kindStoreTargetAddressPPCBranch24: @@ -798,14 +806,14 @@ void Resolver::markLive(const ld::Atom& atom, WhyLiveBackChain* previous) target = _internal.indirectBindingTable[fit->u.bindingIndex]; if ( target == NULL ) { const char* targetName = _symbolTable.indirectName(fit->u.bindingIndex); - _inputFiles.searchLibraries(targetName, true, true, *this); + _inputFiles.searchLibraries(targetName, true, true, false, *this); target = _internal.indirectBindingTable[fit->u.bindingIndex]; } if ( target != NULL ) { if ( target->definition() == ld::Atom::definitionTentative ) { // need to search archives for overrides of common symbols bool searchDylibs = (_options.commonsMode() == Options::kCommonsOverriddenByDylibs); - _inputFiles.searchLibraries(target->name(), searchDylibs, true, *this); + _inputFiles.searchLibraries(target->name(), searchDylibs, true, true, *this); // recompute target since it may have been overridden by searchLibraries() target = _internal.indirectBindingTable[fit->u.bindingIndex]; } @@ -826,6 +834,21 @@ void Resolver::markLive(const ld::Atom& atom, WhyLiveBackChain* previous) } +class NotLiveLTO { +public: + bool operator()(const ld::Atom* atom) const { + if (atom->live() || atom->dontDeadStrip() ) + return false; + // don't kill combinable atoms in first pass + switch ( atom->combine() ) { + case ld::Atom::combineByNameAndContent: + case ld::Atom::combineByNameAndReferences: + return false; + default: + return true; + } + } +}; void Resolver::deadStripOptimize() { @@ -842,7 +865,7 @@ void Resolver::deadStripOptimize() for (Options::UndefinesIterator uit=_options.initialUndefinesBegin(); uit != _options.initialUndefinesEnd(); ++uit) { SymbolTable::IndirectBindingSlot slot = _symbolTable.findSlotForName(*uit); if ( _internal.indirectBindingTable[slot] == NULL ) { - _inputFiles.searchLibraries(*uit, false, true, *this); + _inputFiles.searchLibraries(*uit, false, true, false, *this); } if ( _internal.indirectBindingTable[slot] != NULL ) _deadStripRoots.insert(_internal.indirectBindingTable[slot]); @@ -887,7 +910,14 @@ void Resolver::deadStripOptimize() fprintf(stderr, " live=%d name=%s\n", (*it)->live(), (*it)->name()); } } - _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), NotLive()), _atoms.end()); + + if ( _haveLLVMObjs ) { + // don't remove combinable atoms, they may come back in lto output + _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), NotLiveLTO()), _atoms.end()); + } + else { + _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), NotLive()), _atoms.end()); + } } @@ -897,7 +927,8 @@ void Resolver::liveUndefines(std::vector& undefs) // search all live atoms for references that are unbound for (std::vector::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) { const ld::Atom* atom = *it; - assert(atom->live()); + if ( ! atom->live() ) + continue; for (ld::Fixup::iterator fit=atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { switch ( (ld::Fixup::TargetBinding)fit->binding ) { case ld::Fixup::bindingByNameUnbound: @@ -1107,7 +1138,7 @@ void Resolver::checkUndefines(bool force) bool printedStart = false; for (SymbolTable::byNameIterator sit=_symbolTable.begin(); sit != _symbolTable.end(); sit++) { const ld::Atom* atom = *sit; - if ( (atom != NULL) && (strstr(atom->name(), name) != NULL) ) { + if ( (atom != NULL) && (atom->symbolTableInclusion() == ld::Atom::symbolTableIn) && (strstr(atom->name(), name) != NULL) ) { if ( ! printedStart ) { fprintf(stderr, " (maybe you meant: %s", atom->name()); printedStart = true; @@ -1119,6 +1150,10 @@ void Resolver::checkUndefines(bool force) } if ( printedStart ) fprintf(stderr, ")\n"); + // Add comment to error message when __ZTV symbols are undefined + if ( strncmp(name, "__ZTV", 5) == 0 ) { + fprintf(stderr, " NOTE: a missing vtable usually means the first non-inline virtual member function has no definition.\n"); + } } } if ( doError ) @@ -1139,7 +1174,7 @@ void Resolver::checkDylibSymbolCollisions() // No warning about tentative definition conflicting with dylib definition // for each tentative definition in symbol table look for dylib that exports same symbol name if ( atom->definition() == ld::Atom::definitionTentative ) { - _inputFiles.searchLibraries(atom->name(), true, false, *this); + _inputFiles.searchLibraries(atom->name(), true, false, false, *this); } // record any overrides of weak symbols in any linked dylib if ( (atom->definition() == ld::Atom::definitionRegular) && (atom->symbolTableInclusion() == ld::Atom::symbolTableIn) ) { @@ -1154,6 +1189,7 @@ void Resolver::checkDylibSymbolCollisions() const ld::Atom* Resolver::entryPoint(bool searchArchives) { const char* symbolName = NULL; + bool makingDylib = false; switch ( _options.outputKind() ) { case Options::kDynamicExecutable: case Options::kStaticExecutable: @@ -1163,6 +1199,7 @@ const ld::Atom* Resolver::entryPoint(bool searchArchives) break; case Options::kDynamicLibrary: symbolName = _options.initFunctionName(); + makingDylib = true; break; case Options::kObjectFile: case Options::kDynamicBundle: @@ -1174,7 +1211,7 @@ const ld::Atom* Resolver::entryPoint(bool searchArchives) SymbolTable::IndirectBindingSlot slot = _symbolTable.findSlotForName(symbolName); if ( (_internal.indirectBindingTable[slot] == NULL) && searchArchives ) { // ld64 can not find a -e entry point from an archive - _inputFiles.searchLibraries(symbolName, false, true, *this); + _inputFiles.searchLibraries(symbolName, false, true, false, *this); } if ( _internal.indirectBindingTable[slot] == NULL ) { if ( strcmp(symbolName, "start") == 0 ) @@ -1182,6 +1219,12 @@ const ld::Atom* Resolver::entryPoint(bool searchArchives) else throwf("entry point (%s) undefined.", symbolName); } + 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]; } return NULL; @@ -1231,7 +1274,7 @@ void Resolver::fillInHelpersInInternalState() if ( needsStubHelper && _options.makeCompressedDyldInfo() ) { // "dyld_stub_binder" comes from libSystem.dylib so will need to manually resolve if ( !_symbolTable.hasName("dyld_stub_binder") ) { - _inputFiles.searchLibraries("dyld_stub_binder", true, false, *this); + _inputFiles.searchLibraries("dyld_stub_binder", true, false, false, *this); } if ( _symbolTable.hasName("dyld_stub_binder") ) { SymbolTable::IndirectBindingSlot slot = _symbolTable.findSlotForName("dyld_stub_binder"); @@ -1243,9 +1286,6 @@ void Resolver::fillInHelpersInInternalState() _internal.compressedFastBinderProxy = new UndefinedProxyAtom("dyld_stub_binder"); this->doAtom(*_internal.compressedFastBinderProxy); } - else { - warning("symbol dyld_stub_binder not found, normally in libSystem.dylib"); - } } } } @@ -1341,7 +1381,7 @@ void Resolver::linkTimeOptimize() const char *targetName = *uit; // these symbols may or may not already be in linker's symbol table if ( ! _symbolTable.hasName(targetName) ) { - _inputFiles.searchLibraries(targetName, true, true, *this); + _inputFiles.searchLibraries(targetName, true, true, false, *this); } } _addToFinalSection = false; diff --git a/src/ld/SymbolTable.cpp b/src/ld/SymbolTable.cpp index f9ed04c..2716ccf 100644 --- a/src/ld/SymbolTable.cpp +++ b/src/ld/SymbolTable.cpp @@ -137,26 +137,34 @@ bool SymbolTable::addByName(const ld::Atom& newAtom, bool ignoreDuplicates) case ld::Atom::definitionRegular: if ( existingAtom->combine() == ld::Atom::combineByName ) { if ( newAtom.combine() == ld::Atom::combineByName ) { - // 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() ); + // 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 { - // neither auto-hide, check visibility - if ( newAtom.scope() != existingAtom->scope() ) { - // use more visible weak def symbol - useNew = (newAtom.scope() == ld::Atom::scopeGlobal); + // 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 { - // both have same visibility, use one with greater alignment + 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 { @@ -221,6 +229,11 @@ bool SymbolTable::addByName(const ld::Atom& newAtom, bool ignoreDuplicates) 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()); + } break; case ld::Atom::definitionTentative: // new and existing are both tentative definitions, use largest diff --git a/src/ld/debugline.c b/src/ld/debugline.c index c367d00..c4e151d 100644 --- a/src/ld/debugline.c +++ b/src/ld/debugline.c @@ -152,6 +152,7 @@ line_file (struct line_reader_data *lnd, uint64_t n) size_t filelen, dirlen; uint64_t dir; char * result; + const char * dirpath; /* I'm not sure if this is actually an error. */ if (n == 0 @@ -168,9 +169,14 @@ line_file (struct line_reader_data *lnd, uint64_t n) else if (dir > lnd->numdir) return NULL; - dirlen = strlen ((const char *) lnd->dirnames[dir - 1]); + dirpath = (const char *)lnd->dirnames[dir - 1]; + dirlen = strlen (dirpath); + if ( dirpath[dirlen-1] == '/' ) + --dirlen; + if ( (dirpath[dirlen-1] == '.') && (dirpath[dirlen-2] == '/') ) + dirlen -= 2; result = malloc (dirlen + filelen + 2); - memcpy (result, lnd->dirnames[dir - 1], dirlen); + memcpy (result, dirpath, dirlen); result[dirlen] = '/'; memcpy (result + dirlen + 1, lnd->filenames[n - 1], filelen); result[dirlen + 1 + filelen] = '\0'; diff --git a/src/ld/dwarf2.h b/src/ld/dwarf2.h index 7a7b4f2..411dbbc 100644 --- a/src/ld/dwarf2.h +++ b/src/ld/dwarf2.h @@ -87,4 +87,223 @@ enum { DW_LNE_define_file }; + +// dwarf unwind instructions +enum { + DW_CFA_nop = 0x0, + DW_CFA_set_loc = 0x1, + DW_CFA_advance_loc1 = 0x2, + DW_CFA_advance_loc2 = 0x3, + DW_CFA_advance_loc4 = 0x4, + DW_CFA_offset_extended = 0x5, + DW_CFA_restore_extended = 0x6, + DW_CFA_undefined = 0x7, + DW_CFA_same_value = 0x8, + DW_CFA_register = 0x9, + DW_CFA_remember_state = 0xA, + DW_CFA_restore_state = 0xB, + DW_CFA_def_cfa = 0xC, + DW_CFA_def_cfa_register = 0xD, + DW_CFA_def_cfa_offset = 0xE, + DW_CFA_def_cfa_expression = 0xF, + DW_CFA_expression = 0x10, + DW_CFA_offset_extended_sf = 0x11, + DW_CFA_def_cfa_sf = 0x12, + DW_CFA_def_cfa_offset_sf = 0x13, + DW_CFA_val_offset = 0x14, + DW_CFA_val_offset_sf = 0x15, + DW_CFA_val_expression = 0x16, + DW_CFA_advance_loc = 0x40, // high 2 bits are 0x1, lower 6 bits are delta + DW_CFA_offset = 0x80, // high 2 bits are 0x2, lower 6 bits are register + DW_CFA_restore = 0xC0, // high 2 bits are 0x3, lower 6 bits are register + + // GNU extensions + DW_CFA_GNU_window_save = 0x2D, + DW_CFA_GNU_args_size = 0x2E, + DW_CFA_GNU_negative_offset_extended = 0x2F +}; + + +// FSF exception handling Pointer-Encoding constants +// Used in CFI augmentation by gcc compiler +enum { + DW_EH_PE_ptr = 0x00, + DW_EH_PE_uleb128 = 0x01, + DW_EH_PE_udata2 = 0x02, + DW_EH_PE_udata4 = 0x03, + DW_EH_PE_udata8 = 0x04, + DW_EH_PE_signed = 0x08, + DW_EH_PE_sleb128 = 0x09, + DW_EH_PE_sdata2 = 0x0A, + DW_EH_PE_sdata4 = 0x0B, + DW_EH_PE_sdata8 = 0x0C, + DW_EH_PE_absptr = 0x00, + DW_EH_PE_pcrel = 0x10, + DW_EH_PE_textrel = 0x20, + DW_EH_PE_datarel = 0x30, + DW_EH_PE_funcrel = 0x40, + DW_EH_PE_aligned = 0x50, + DW_EH_PE_indirect = 0x80, + DW_EH_PE_omit = 0xFF +}; + + +// DWARF expressions +enum { + DW_OP_addr = 0x03, // constant address (size target specific) + DW_OP_deref = 0x06, + DW_OP_const1u = 0x08, // 1-byte constant + DW_OP_const1s = 0x09, // 1-byte constant + DW_OP_const2u = 0x0A, // 2-byte constant + DW_OP_const2s = 0x0B, // 2-byte constant + DW_OP_const4u = 0x0C, // 4-byte constant + DW_OP_const4s = 0x0D, // 4-byte constant + DW_OP_const8u = 0x0E, // 8-byte constant + DW_OP_const8s = 0x0F, // 8-byte constant + DW_OP_constu = 0x10, // ULEB128 constant + DW_OP_consts = 0x11, // SLEB128 constant + DW_OP_dup = 0x12, + DW_OP_drop = 0x13, + DW_OP_over = 0x14, + DW_OP_pick = 0x15, // 1-byte stack index + DW_OP_swap = 0x16, + DW_OP_rot = 0x17, + DW_OP_xderef = 0x18, + DW_OP_abs = 0x19, + DW_OP_and = 0x1A, + DW_OP_div = 0x1B, + DW_OP_minus = 0x1C, + DW_OP_mod = 0x1D, + DW_OP_mul = 0x1E, + DW_OP_neg = 0x1F, + DW_OP_not = 0x20, + DW_OP_or = 0x21, + DW_OP_plus = 0x22, + DW_OP_plus_uconst = 0x23, // ULEB128 addend + DW_OP_shl = 0x24, + DW_OP_shr = 0x25, + DW_OP_shra = 0x26, + DW_OP_xor = 0x27, + DW_OP_skip = 0x2F, // signed 2-byte constant + DW_OP_bra = 0x28, // signed 2-byte constant + DW_OP_eq = 0x29, + DW_OP_ge = 0x2A, + DW_OP_gt = 0x2B, + DW_OP_le = 0x2C, + DW_OP_lt = 0x2D, + DW_OP_ne = 0x2E, + DW_OP_lit0 = 0x30, // Literal 0 + DW_OP_lit1 = 0x31, // Literal 1 + DW_OP_lit2 = 0x32, // Literal 2 + DW_OP_lit3 = 0x33, // Literal 3 + DW_OP_lit4 = 0x34, // Literal 4 + DW_OP_lit5 = 0x35, // Literal 5 + DW_OP_lit6 = 0x36, // Literal 6 + DW_OP_lit7 = 0x37, // Literal 7 + DW_OP_lit8 = 0x38, // Literal 8 + DW_OP_lit9 = 0x39, // Literal 9 + DW_OP_lit10 = 0x3A, // Literal 10 + DW_OP_lit11 = 0x3B, // Literal 11 + DW_OP_lit12 = 0x3C, // Literal 12 + DW_OP_lit13 = 0x3D, // Literal 13 + DW_OP_lit14 = 0x3E, // Literal 14 + DW_OP_lit15 = 0x3F, // Literal 15 + DW_OP_lit16 = 0x40, // Literal 16 + DW_OP_lit17 = 0x41, // Literal 17 + DW_OP_lit18 = 0x42, // Literal 18 + DW_OP_lit19 = 0x43, // Literal 19 + DW_OP_lit20 = 0x44, // Literal 20 + DW_OP_lit21 = 0x45, // Literal 21 + DW_OP_lit22 = 0x46, // Literal 22 + DW_OP_lit23 = 0x47, // Literal 23 + DW_OP_lit24 = 0x48, // Literal 24 + DW_OP_lit25 = 0x49, // Literal 25 + DW_OP_lit26 = 0x4A, // Literal 26 + DW_OP_lit27 = 0x4B, // Literal 27 + DW_OP_lit28 = 0x4C, // Literal 28 + DW_OP_lit29 = 0x4D, // Literal 29 + DW_OP_lit30 = 0x4E, // Literal 30 + DW_OP_lit31 = 0x4F, // Literal 31 + DW_OP_reg0 = 0x50, // Contents of reg0 + DW_OP_reg1 = 0x51, // Contents of reg1 + DW_OP_reg2 = 0x52, // Contents of reg2 + DW_OP_reg3 = 0x53, // Contents of reg3 + DW_OP_reg4 = 0x54, // Contents of reg4 + DW_OP_reg5 = 0x55, // Contents of reg5 + DW_OP_reg6 = 0x56, // Contents of reg6 + DW_OP_reg7 = 0x57, // Contents of reg7 + DW_OP_reg8 = 0x58, // Contents of reg8 + DW_OP_reg9 = 0x59, // Contents of reg9 + DW_OP_reg10 = 0x5A, // Contents of reg10 + DW_OP_reg11 = 0x5B, // Contents of reg11 + DW_OP_reg12 = 0x5C, // Contents of reg12 + DW_OP_reg13 = 0x5D, // Contents of reg13 + DW_OP_reg14 = 0x5E, // Contents of reg14 + DW_OP_reg15 = 0x5F, // Contents of reg15 + DW_OP_reg16 = 0x60, // Contents of reg16 + DW_OP_reg17 = 0x61, // Contents of reg17 + DW_OP_reg18 = 0x62, // Contents of reg18 + DW_OP_reg19 = 0x63, // Contents of reg19 + DW_OP_reg20 = 0x64, // Contents of reg20 + DW_OP_reg21 = 0x65, // Contents of reg21 + DW_OP_reg22 = 0x66, // Contents of reg22 + DW_OP_reg23 = 0x67, // Contents of reg23 + DW_OP_reg24 = 0x68, // Contents of reg24 + DW_OP_reg25 = 0x69, // Contents of reg25 + DW_OP_reg26 = 0x6A, // Contents of reg26 + DW_OP_reg27 = 0x6B, // Contents of reg27 + DW_OP_reg28 = 0x6C, // Contents of reg28 + DW_OP_reg29 = 0x6D, // Contents of reg29 + DW_OP_reg30 = 0x6E, // Contents of reg30 + DW_OP_reg31 = 0x6F, // Contents of reg31 + DW_OP_breg0 = 0x70, // base register 0 + SLEB128 offset + DW_OP_breg1 = 0x71, // base register 1 + SLEB128 offset + DW_OP_breg2 = 0x72, // base register 2 + SLEB128 offset + DW_OP_breg3 = 0x73, // base register 3 + SLEB128 offset + DW_OP_breg4 = 0x74, // base register 4 + SLEB128 offset + DW_OP_breg5 = 0x75, // base register 5 + SLEB128 offset + DW_OP_breg6 = 0x76, // base register 6 + SLEB128 offset + DW_OP_breg7 = 0x77, // base register 7 + SLEB128 offset + DW_OP_breg8 = 0x78, // base register 8 + SLEB128 offset + DW_OP_breg9 = 0x79, // base register 9 + SLEB128 offset + DW_OP_breg10 = 0x7A, // base register 10 + SLEB128 offset + DW_OP_breg11 = 0x7B, // base register 11 + SLEB128 offset + DW_OP_breg12 = 0x7C, // base register 12 + SLEB128 offset + DW_OP_breg13 = 0x7D, // base register 13 + SLEB128 offset + DW_OP_breg14 = 0x7E, // base register 14 + SLEB128 offset + DW_OP_breg15 = 0x7F, // base register 15 + SLEB128 offset + DW_OP_breg16 = 0x80, // base register 16 + SLEB128 offset + DW_OP_breg17 = 0x81, // base register 17 + SLEB128 offset + DW_OP_breg18 = 0x82, // base register 18 + SLEB128 offset + DW_OP_breg19 = 0x83, // base register 19 + SLEB128 offset + DW_OP_breg20 = 0x84, // base register 20 + SLEB128 offset + DW_OP_breg21 = 0x85, // base register 21 + SLEB128 offset + DW_OP_breg22 = 0x86, // base register 22 + SLEB128 offset + DW_OP_breg23 = 0x87, // base register 23 + SLEB128 offset + DW_OP_breg24 = 0x88, // base register 24 + SLEB128 offset + DW_OP_breg25 = 0x89, // base register 25 + SLEB128 offset + DW_OP_breg26 = 0x8A, // base register 26 + SLEB128 offset + DW_OP_breg27 = 0x8B, // base register 27 + SLEB128 offset + DW_OP_breg28 = 0x8C, // base register 28 + SLEB128 offset + DW_OP_breg29 = 0x8D, // base register 29 + SLEB128 offset + DW_OP_breg30 = 0x8E, // base register 30 + SLEB128 offset + DW_OP_breg31 = 0x8F, // base register 31 + SLEB128 offset + DW_OP_regx = 0x90, // ULEB128 register + DW_OP_fbreg = 0x91, // SLEB128 offset + DW_OP_bregx = 0x92, // ULEB128 register followed by SLEB128 offset + DW_OP_piece = 0x93, // ULEB128 size of piece addressed + DW_OP_deref_size = 0x94, // 1-byte size of data retrieved + DW_OP_xderef_size = 0x95, // 1-byte size of data retrieved + DW_OP_nop = 0x96, + DW_OP_push_object_addres = 0x97, + DW_OP_call2 = 0x98, // 2-byte offset of DIE + DW_OP_call4 = 0x99, // 4-byte offset of DIE + DW_OP_call_ref = 0x9A, // 4- or 8-byte offset of DIE + DW_OP_lo_user = 0xE0, + DW_OP_APPLE_uninit = 0xF0, + DW_OP_hi_user = 0xFF +}; + + + #endif diff --git a/src/ld/ld.cpp b/src/ld/ld.cpp index d9d66a6..5b21c63 100644 --- a/src/ld/ld.cpp +++ b/src/ld/ld.cpp @@ -103,7 +103,7 @@ private: public: FinalSection(const ld::Section& sect, uint32_t sectionsSeen, bool objFile); static int sectionComparer(const void* l, const void* r); - static const ld::Section& outputSection(const ld::Section& sect); + static const ld::Section& outputSection(const ld::Section& sect, bool mergeZeroFill); static const ld::Section& objectOutputSection(const ld::Section& sect, bool makeTentativeDefsReal); private: friend class InternalState; @@ -119,6 +119,7 @@ private: static ld::Section _s_TEXT_const; static ld::Section _s_DATA_nl_symbol_ptr; static ld::Section _s_DATA_common; + static ld::Section _s_DATA_zerofill; }; @@ -141,6 +142,7 @@ ld::Section InternalState::FinalSection::_s_TEXT_text( "__TEXT", "__text", ld:: ld::Section InternalState::FinalSection::_s_TEXT_const("__TEXT", "__const", ld::Section::typeUnclassified); ld::Section InternalState::FinalSection::_s_DATA_nl_symbol_ptr("__DATA", "__nl_symbol_ptr", ld::Section::typeNonLazyPointer); ld::Section InternalState::FinalSection::_s_DATA_common("__DATA", "__common", ld::Section::typeZeroFill); +ld::Section InternalState::FinalSection::_s_DATA_zerofill("__DATA", "__zerofill", ld::Section::typeZeroFill); std::vector InternalState::FinalSection::_s_segmentsSeen; @@ -168,7 +170,7 @@ InternalState::FinalSection::FinalSection(const ld::Section& sect, uint32_t sect // this->segmentName(), this->sectionName(), _segmentOrder, _sectionOrder); } -const ld::Section& InternalState::FinalSection::outputSection(const ld::Section& sect) +const ld::Section& InternalState::FinalSection::outputSection(const ld::Section& sect, bool mergeZeroFill) { // merge sections in final linked image switch ( sect.type() ) { @@ -188,6 +190,10 @@ const ld::Section& InternalState::FinalSection::outputSection(const ld::Section& return _s_TEXT_const; } break; + case ld::Section::typeZeroFill: + if ( mergeZeroFill ) + return _s_DATA_zerofill; + break; case ld::Section::typeCode: if ( strcmp(sect.segmentName(), "__TEXT") == 0 ) { if ( strcmp(sect.sectionName(), "__textcoal_nt") == 0 ) @@ -207,7 +213,10 @@ const ld::Section& InternalState::FinalSection::outputSection(const ld::Section& } break; case ld::Section::typeTentativeDefs: - return _s_DATA_common; + if ( mergeZeroFill ) + return _s_DATA_zerofill; + else + return _s_DATA_common; break; // FIX ME: more default: @@ -436,7 +445,7 @@ ld::Internal::FinalSection* InternalState::addAtom(const ld::Atom& atom) switch ( atom.section().type() ) { case ld::Section::typeZeroFill: case ld::Section::typeTentativeDefs: - if ( (_options.outputKind() == Options::kDyld) && (atom.symbolTableInclusion() == ld::Atom::symbolTableIn) + if ( (atom.symbolTableInclusion() == ld::Atom::symbolTableIn) && (atom.size() <= 512) && (_options.orderedSymbolsCount() != 0) ) { for(Options::OrderedSymbolsIterator it = _options.orderedSymbolsBegin(); it != _options.orderedSymbolsEnd(); ++it) { if ( (it->objectFileName == NULL) && (strcmp(it->symbolName, atom.name()) == 0) ) { @@ -482,7 +491,7 @@ ld::Internal::FinalSection* InternalState::getFinalSection(const ld::Section& in case Options::kPreload: { // coalesce some sections - const ld::Section& outSect = FinalSection::outputSection(inputSection); + const ld::Section& outSect = FinalSection::outputSection(inputSection, _options.mergeZeroFill()); pos = _sectionInToFinalMap.find(&outSect); if ( pos != _sectionInToFinalMap.end() ) { _sectionInToFinalMap[&inputSection] = pos->second; diff --git a/src/ld/ld.hpp b/src/ld/ld.hpp index e5319bb..96075a7 100644 --- a/src/ld/ld.hpp +++ b/src/ld/ld.hpp @@ -83,8 +83,8 @@ private: // enum MacVersionMin { macVersionUnset=0, mac10_4=0x000A0400, mac10_5=0x000A0500, mac10_6=0x000A0600, mac10_7=0x000A0700 }; -enum IPhoneVersionMin { iPhoneVersionUnset=0, iPhone2_0=0x00020000, iPhone3_1=0x00030100, - iPhone4_2=0x00040200, iPhone4_3=0x00040300 }; +enum IOSVersionMin { iOSVersionUnset=0, iOS_2_0=0x00020000, iOS_3_1=0x00030100, + iOS_4_2=0x00040200, iOS_4_3=0x00040300, iOS_5_0=0x00050000 }; namespace relocatable { // @@ -153,9 +153,8 @@ namespace dylib { : ld::File(pth, modTime, ord), _dylibInstallPath(NULL), _dylibTimeStamp(0), _dylibCurrentVersion(0), _dylibCompatibilityVersion(0), _explicitlyLinked(false), _implicitlyLinked(false), - _lazyLoadedDylib(false), _weakLinked(false), _reExported(false), - _upward(false), _hasNonWeakImportedSymbols(false), - _hasWeakImportedSymbols(false), _dead(false) { } + _lazyLoadedDylib(false), _forcedWeakLinked(false), _reExported(false), + _upward(false), _dead(false) { } const char* installPath() const { return _dylibInstallPath; } uint32_t timestamp() const { return _dylibTimeStamp; } uint32_t currentVersion() const { return _dylibCurrentVersion; } @@ -167,15 +166,13 @@ namespace dylib { // attributes of how dylib will be used when linked void setWillBeLazyLoadedDylb() { _lazyLoadedDylib = true; } bool willBeLazyLoadedDylib() const { return _lazyLoadedDylib; } - void setWillBeWeakLinked() { _weakLinked = true; } - bool willBeWeakLinked() const { return _weakLinked || - (_hasWeakImportedSymbols && !_hasNonWeakImportedSymbols); } + void setForcedWeakLinked() { _forcedWeakLinked = true; } + bool forcedWeakLinked() const { return _forcedWeakLinked; } + void setWillBeReExported() { _reExported = true; } bool willBeReExported() const { return _reExported; } void setWillBeUpwardDylib() { _upward = true; } bool willBeUpwardDylib() const { return _upward; } - void setUsingNonWeakImportedSymbols(){ _hasNonWeakImportedSymbols = true; } - void setUsingWeakImportedSymbols() { _hasWeakImportedSymbols = true; } void setWillBeRemoved(bool value) { _dead = value; } bool willRemoved() const { return _dead; } @@ -187,6 +184,7 @@ namespace dylib { virtual bool deadStrippable() const = 0; virtual bool hasWeakDefinition(const char* name) const = 0; virtual bool hasPublicInstallName() const = 0; + virtual bool allSymbolsAreWeakImported() const = 0; protected: const char* _dylibInstallPath; uint32_t _dylibTimeStamp; @@ -195,16 +193,30 @@ namespace dylib { bool _explicitlyLinked; bool _implicitlyLinked; bool _lazyLoadedDylib; - bool _weakLinked; + bool _forcedWeakLinked; bool _reExported; bool _upward; - bool _hasNonWeakImportedSymbols; - bool _hasWeakImportedSymbols; bool _dead; }; } // namespace dylib +namespace archive { + // + // ld::archive::File + // + // Abstract base class for static libraries read by the linker processes. + // + class File : public ld::File + { + public: + File(const char* pth, time_t modTime, uint32_t ord) + : ld::File(pth, modTime, ord) { } + virtual ~File() {} + virtual bool justInTimeDataOnlyforEachAtom(const char* name, AtomHandler&) const = 0; + }; +} // namespace archive + // // ld::Section @@ -220,7 +232,7 @@ public: typeLazyDylibPointer, typeStubHelper, typeInitializerPointers, typeTerminatorPointers, typeStubClose, typeLazyPointerClose, typeAbsoluteSymbols, typeTLVDefs, typeTLVZeroFill, typeTLVInitialValues, typeTLVInitializerPointers, typeTLVPointers, - typeFirstSection, typeLastSection }; + typeFirstSection, typeLastSection, typeDebug }; Section(const char* sgName, const char* sctName, @@ -494,6 +506,8 @@ public: enum SymbolTableInclusion { symbolTableNotIn, symbolTableNotInFinalLinkedImages, symbolTableIn, symbolTableInAndNeverStrip, symbolTableInAsAbsolute, symbolTableInWithRandomAutoStripLabel }; + enum WeakImportState { weakImportUnset, weakImportTrue, weakImportFalse }; + struct Alignment { Alignment(int p2, int m=0) : powerOf2(p2), modulus(m) {} uint8_t trailingZeros() const { return (modulus==0) ? powerOf2 : __builtin_ctz(modulus); } @@ -522,7 +536,7 @@ public: _contentType(ct), _symbolTableInclusion(i), _scope(s), _mode(modeSectionOffset), _overridesADylibsWeakDef(false), _coalescedAway(false), - _weakImport(false), _live(false), _machoSection(0) + _live(false), _machoSection(0), _weakImportState(weakImportUnset) { #ifndef NDEBUG switch ( _combine ) { @@ -551,7 +565,8 @@ public: Alignment alignment() const { return Alignment(_alignmentPowerOf2, _alignmentModulus); } bool overridesDylibsWeakDef() const { return _overridesADylibsWeakDef; } bool coalescedAway() const { return _coalescedAway; } - bool weakImported() const { return _weakImport; } + bool weakImported() const { return _weakImportState == weakImportTrue; } + WeakImportState weakImportState() const { return _weakImportState; } bool autoHide() const { return _autoHide; } bool live() const { return _live; } uint8_t machoSection() const { assert(_machoSection != 0); return _machoSection; } @@ -562,7 +577,7 @@ public: void setCombine(Combine c) { _combine = c; } void setOverridesDylibsWeakDef() { _overridesADylibsWeakDef = true; } void setCoalescedAway() { _coalescedAway = true; } - void setWeakImported() { _weakImport = true; assert(_definition == definitionProxy); } + void setWeakImportState(bool w) { assert(_definition == definitionProxy); _weakImportState = ( w ? weakImportTrue : weakImportFalse); } void setAutoHide() { _autoHide = true; } void setLive() { _live = true; } void setLive(bool value) { _live = value; } @@ -607,7 +622,7 @@ protected: _mode = a._mode; _overridesADylibsWeakDef = a._overridesADylibsWeakDef; _coalescedAway = a._coalescedAway; - _weakImport = a._weakImport; + _weakImportState = a._weakImportState; } const Section * _section; @@ -626,9 +641,9 @@ protected: AddressMode _mode: 2; bool _overridesADylibsWeakDef : 1; bool _coalescedAway : 1; - bool _weakImport : 1; bool _live : 1; unsigned _machoSection : 8; + WeakImportState _weakImportState : 2; }; diff --git a/src/ld/parsers/archive_file.cpp b/src/ld/parsers/archive_file.cpp index 8c866cd..66f9432 100644 --- a/src/ld/parsers/archive_file.cpp +++ b/src/ld/parsers/archive_file.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2005-2009 Apple Inc. All rights reserved. + * Copyright (c) 2005-2011 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -31,6 +31,7 @@ #include #include +#include #include #include @@ -69,7 +70,7 @@ public: }; template -class File : public ld::File +class File : public ld::archive::File { public: static bool validFile(const uint8_t* fileContent, uint64_t fileLength, @@ -84,6 +85,9 @@ public: virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const; virtual uint32_t subFileCount() const { return _archiveFilelength/sizeof(ar_hdr); } + // overrides of ld::archive::File + virtual bool justInTimeDataOnlyforEachAtom(const char* name, ld::File::AtomHandler& handler) const; + private: static bool validMachOFile(const uint8_t* fileContent, uint64_t fileLength, const mach_o::relocatable::ParserOptions& opts); @@ -91,7 +95,6 @@ private: const mach_o::relocatable::ParserOptions& opts); static cpu_type_t architecture(); - class Entry : ar_hdr { public: @@ -116,8 +119,12 @@ 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; - ld::relocatable::File* makeObjectFileForMember(const Entry* member) const; + MemberState& makeObjectFileForMember(const Entry* member) const; bool memberHasObjCCategories(const Entry* member) const; void dumpTableOfContents(); void buildHashTable(); @@ -127,12 +134,12 @@ private: const struct ranlib* _tableOfContents; uint32_t _tableOfContentCount; const char* _tableOfContentStrings; - mutable std::vector _instantiatedFiles; - mutable std::set _instantiatedEntries; + mutable MemberToStateMap _instantiatedEntries; NameToEntryMap _hashTable; const bool _forceLoadAll; const bool _forceLoadObjC; const bool _forceLoadThis; + const bool _objc2ABI; const bool _verboseLoad; const bool _logAllFiles; const mach_o::relocatable::ParserOptions _objOpts; @@ -266,11 +273,11 @@ 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(strdup(pth), modTime, ord), + : ld::archive::File(strdup(pth), modTime, ord), _archiveFileContent(fileContent), _archiveFilelength(fileLength), _tableOfContents(NULL), _tableOfContentCount(0), _tableOfContentStrings(NULL), _forceLoadAll(opts.forceLoadAll), _forceLoadObjC(opts.forceLoadObjC), - _forceLoadThis(opts.forceLoadThisArchive), _verboseLoad(opts.verboseLoad), + _forceLoadThis(opts.forceLoadThisArchive), _objc2ABI(opts.objcABI2), _verboseLoad(opts.verboseLoad), _logAllFiles(opts.logAllFiles), _objOpts(opts.objOpts) { if ( strncmp((const char*)fileContent, "!\n", 8) != 0 ) @@ -297,8 +304,14 @@ File::File(const uint8_t fileContent[], uint64_t fileLength, const char* pth, template <> bool File::memberHasObjCCategories(const Entry* member) const { - // i386 uses ObjC1 ABI which has .objc_category* global symbols - return false; + if ( _objc2ABI ) { + // i386 for iOS simulator uses ObjC2 which has no global symbol for categories + return mach_o::relocatable::hasObjC2Categories(member->content()); + } + else { + // i386 uses ObjC1 ABI which has .objc_category* global symbols + return false; + } } template <> @@ -318,8 +331,13 @@ bool File::memberHasObjCCategories(const Entry* member) const template -ld::relocatable::File* File::makeObjectFileForMember(const Entry* member) const +typename File::MemberState& File::makeObjectFileForMember(const Entry* member) const { + // 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(); char memberPath[strlen(this->path()) + strlen(memberName)+4]; strcpy(memberPath, this->path()); @@ -340,14 +358,20 @@ ld::relocatable::File* File::makeObjectFileForMember(const Entry* member) con ld::relocatable::File* result = mach_o::relocatable::parse(member->content(), member->contentSize(), mPath, member->modificationTime(), this->ordinal() + memberIndex, _objOpts); - if ( result != NULL ) - return result; + if ( result != NULL ) { + MemberState state = {result, false, false}; + _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, _objOpts.architecture, _objOpts.subType, _logAllFiles); - if ( result != NULL ) - return result; + if ( result != NULL ) { + MemberState state = {result, false, false}; + _instantiatedEntries[member] = state; + return _instantiatedEntries[member]; + } throwf("archive member '%s' with length %d is not mach-o or llvm bitcode", memberName, member->contentSize()); } @@ -369,14 +393,16 @@ bool File::forEachAtom(ld::File::AtomHandler& handler) const const char* memberName = p->name(); 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; } - ld::relocatable::File* file = this->makeObjectFileForMember(p); - didSome |= file->forEachAtom(handler); + didSome |= state.file->forEachAtom(handler); + state.loaded = true; } } else if ( _forceLoadObjC ) { @@ -384,18 +410,18 @@ bool File::forEachAtom(ld::File::AtomHandler& handler) const for(typename NameToEntryMap::const_iterator it = _hashTable.begin(); it != _hashTable.end(); ++it) { 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)]; - if ( _instantiatedEntries.count(member) == 0 ) { - if ( _verboseLoad ) - printf("-ObjC forced load of %s(%s)\n", this->path(), member->name()); - // only return these atoms once - _instantiatedEntries.insert(member); - ld::relocatable::File* file = this->makeObjectFileForMember(member); - didSome |= file->forEachAtom(handler); - _instantiatedFiles.push_back(file); + 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; } } } - // ObjC2 has no symbols in .o files with categories, but not classes, look deeper for those + // 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()) { @@ -403,13 +429,15 @@ bool File::forEachAtom(ld::File::AtomHandler& handler) const if ( _instantiatedEntries.count(member) == 0 ) { //fprintf(stderr, "checking member %s\n", member->name()); if ( this->memberHasObjCCategories(member) ) { - if ( _verboseLoad ) + MemberState& state = this->makeObjectFileForMember(member); + if ( _verboseLoad && !state.logged ) { printf("-ObjC forced load of %s(%s)\n", this->path(), member->name()); - // only return these atoms once - _instantiatedEntries.insert(member); - ld::relocatable::File* file = this->makeObjectFileForMember(member); - didSome |= file->forEachAtom(handler); - _instantiatedFiles.push_back(file); + state.logged = true; + } + if ( ! state.loaded ) { + didSome |= state.file->forEachAtom(handler); + state.loaded = true; + } } } } @@ -428,14 +456,64 @@ bool File::justInTimeforEachAtom(const char* name, ld::File::AtomHandler& han const struct ranlib* result = ranlibHashSearch(name); 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 ( _instantiatedEntries.count(member) == 0 ) { - _instantiatedEntries.insert(member); - if ( _verboseLoad ) + if ( ! state.loaded && !state.logged ) { + if ( _verboseLoad ) { printf("%s forced load of %s(%s)\n", name, this->path(), member->name()); - ld::relocatable::File* file = this->makeObjectFileForMember(member); - _instantiatedFiles.push_back(file); - return file->forEachAtom(handler); + state.logged = true; + } + state.loaded = true; + return state.file->forEachAtom(handler); + } + } + //fprintf(stderr, "%s NOT found in archive %s\n", name, this->path()); + return false; +} + +class CheckIsDataSymbolHandler : public ld::File::AtomHandler +{ +public: + CheckIsDataSymbolHandler(const char* n) : _name(n), _isData(false) {} + virtual void doAtom(const class ld::Atom& atom) { + if ( strcmp(atom.name(), _name) == 0 ) { + if ( atom.section().type() != ld::Section::typeCode ) + _isData = true; + } + } + virtual void doFile(const class ld::File&) {} + bool symbolIsDataDefinition() { return _isData; } + +private: + const char* _name; + bool _isData; + +}; + +template +bool File::justInTimeDataOnlyforEachAtom(const char* name, ld::File::AtomHandler& handler) const +{ + // in force load case, all members already loaded + if ( _forceLoadAll || _forceLoadThis ) + return false; + + // do a hash search of table of contents looking for requested symbol + const struct ranlib* result = ranlibHashSearch(name); + 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 ) { + 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); + } } } //fprintf(stderr, "%s NOT found in archive %s\n", name, this->path()); @@ -487,7 +565,7 @@ void File::dumpTableOfContents() // // main function used by linker to instantiate archive files // -ld::File* parse(const uint8_t* fileContent, uint64_t fileLength, +ld::archive::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, uint32_t ordinal, const ParserOptions& opts) { switch ( opts.objOpts.architecture ) { diff --git a/src/ld/parsers/archive_file.h b/src/ld/parsers/archive_file.h index 4dcbc8b..31bab29 100644 --- a/src/ld/parsers/archive_file.h +++ b/src/ld/parsers/archive_file.h @@ -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@ * @@ -35,11 +35,12 @@ struct ParserOptions { bool forceLoadThisArchive; bool forceLoadAll; bool forceLoadObjC; + bool objcABI2; bool verboseLoad; bool logAllFiles; }; -extern ld::File* parse(const uint8_t* fileContent, uint64_t fileLength, +extern ld::archive::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, uint32_t ordinal, const ParserOptions& opts); } // namespace archive diff --git a/src/ld/parsers/libunwind/AddressSpace.hpp b/src/ld/parsers/libunwind/AddressSpace.hpp new file mode 100644 index 0000000..bebd3e6 --- /dev/null +++ b/src/ld/parsers/libunwind/AddressSpace.hpp @@ -0,0 +1,439 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +// +// C++ interface to lower levels of libuwind +// + +#ifndef __ADDRESSSPACE_HPP__ +#define __ADDRESSSPACE_HPP__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "FileAbstraction.hpp" +#include "libunwind.h" +#include "InternalMacros.h" +#include "dwarf2.h" + + +#if 0 +#if __i386__ || __x86_64__ +// In 10.6 and later i386 and x86_64 don't have a __dyld section +// We need one to access private _dyld_func_lookup function. + +struct __DATA__dyld { long lazy; int (*lookup)(const char*, void**); }; + +static volatile struct __DATA__dyld myDyldSection __attribute__ ((section ("__DATA,__dyld"))) = { 0, NULL }; + + +static int my_dyld_func_lookup(const char* dyld_func_name, void **address) +{ + return (*myDyldSection.lookup)(dyld_func_name, address); +} +#else + #define my_dyld_func_lookup _dyld_func_lookup +#endif + + +bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info) +{ + static void* (*p)(void*, dyld_unwind_sections*) = NULL; + + if(p == NULL) + my_dyld_func_lookup("__dyld_find_unwind_sections", (void**)&p); + return p(addr, info); +} +#endif // 0 + + + +namespace libunwind { + +/// +/// LocalAddressSpace is used as a template parameter to UnwindCursor when unwinding a thread +/// in the same process. It compiles away and making local unwinds very fast. +/// +class LocalAddressSpace +{ +public: + + #if __LP64__ + typedef uint64_t pint_t; + typedef int64_t sint_t; + #else + typedef uint32_t pint_t; + typedef int32_t sint_t; + #endif + uint8_t get8(pint_t addr) { return *((uint8_t*)addr); } + uint16_t get16(pint_t addr) { return *((uint16_t*)addr); } + uint32_t get32(pint_t addr) { return *((uint32_t*)addr); } + uint64_t get64(pint_t addr) { return *((uint64_t*)addr); } + double getDouble(pint_t addr) { return *((double*)addr); } + v128 getVector(pint_t addr) { return *((v128*)addr); } + uintptr_t getP(pint_t addr); + static uint64_t getULEB128(pint_t& addr, pint_t end); + static int64_t getSLEB128(pint_t& addr, pint_t end); + + pint_t getEncodedP(pint_t& addr, pint_t end, uint8_t encoding); + bool findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset); + bool findUnwindSections(pint_t addr, pint_t& mh, pint_t& dwarfStart, pint_t& dwarfLen, pint_t& compactStart); + +}; + +LocalAddressSpace sThisAddress; + +inline uintptr_t LocalAddressSpace::getP(pint_t addr) +{ +#if __LP64__ + return get64(addr); +#else + return get32(addr); +#endif +} + +/* Read a ULEB128 into a 64-bit word. */ +inline uint64_t +LocalAddressSpace::getULEB128(pint_t& addr, pint_t end) +{ + const uint8_t* p = (uint8_t*)addr; + const uint8_t* pend = (uint8_t*)end; + uint64_t result = 0; + int bit = 0; + do { + uint64_t b; + + if ( p == pend ) + ABORT("truncated uleb128 expression"); + + b = *p & 0x7f; + + if (bit >= 64 || b << bit >> bit != b) { + ABORT("malformed uleb128 expression"); + } + else { + result |= b << bit; + bit += 7; + } + } while ( *p++ >= 0x80 ); + addr = (pint_t)p; + return result; +} + +/* Read a SLEB128 into a 64-bit word. */ +inline int64_t +LocalAddressSpace::getSLEB128(pint_t& addr, pint_t end) +{ + const uint8_t* p = (uint8_t*)addr; + int64_t result = 0; + int bit = 0; + uint8_t byte; + do { + byte = *p++; + result |= ((byte & 0x7f) << bit); + bit += 7; + } while (byte & 0x80); + // sign extend negative numbers + if ( (byte & 0x40) != 0 ) + result |= (-1LL) << bit; + addr = (pint_t)p; + return result; +} + +LocalAddressSpace::pint_t +LocalAddressSpace::getEncodedP(pint_t& addr, pint_t end, uint8_t encoding) +{ + pint_t startAddr = addr; + const uint8_t* p = (uint8_t*)addr; + pint_t result; + + // first get value + switch (encoding & 0x0F) { + case DW_EH_PE_ptr: + result = getP(addr); + p += sizeof(pint_t); + addr = (pint_t)p; + break; + case DW_EH_PE_uleb128: + result = getULEB128(addr, end); + break; + case DW_EH_PE_udata2: + result = get16(addr); + p += 2; + addr = (pint_t)p; + break; + case DW_EH_PE_udata4: + result = get32(addr); + p += 4; + addr = (pint_t)p; + break; + case DW_EH_PE_udata8: + result = get64(addr); + p += 8; + addr = (pint_t)p; + break; + case DW_EH_PE_sleb128: + result = getSLEB128(addr, end); + break; + case DW_EH_PE_sdata2: + result = (int16_t)get16(addr); + p += 2; + addr = (pint_t)p; + break; + case DW_EH_PE_sdata4: + result = (int32_t)get32(addr); + p += 4; + addr = (pint_t)p; + break; + case DW_EH_PE_sdata8: + result = get64(addr); + p += 8; + addr = (pint_t)p; + break; + default: + ABORT("unknown pointer encoding"); + } + + // then add relative offset + switch ( encoding & 0x70 ) { + case DW_EH_PE_absptr: + // do nothing + break; + case DW_EH_PE_pcrel: + result += startAddr; + break; + case DW_EH_PE_textrel: + ABORT("DW_EH_PE_textrel pointer encoding not supported"); + break; + case DW_EH_PE_datarel: + ABORT("DW_EH_PE_datarel pointer encoding not supported"); + break; + case DW_EH_PE_funcrel: + ABORT("DW_EH_PE_funcrel pointer encoding not supported"); + break; + case DW_EH_PE_aligned: + ABORT("DW_EH_PE_aligned pointer encoding not supported"); + break; + default: + ABORT("unknown pointer encoding"); + break; + } + + if ( encoding & DW_EH_PE_indirect ) + result = getP(result); + + return result; +} + + +inline bool LocalAddressSpace::findUnwindSections(pint_t addr, pint_t& mh, pint_t& dwarfStart, pint_t& dwarfLen, pint_t& compactStart) +{ + dyld_unwind_sections info; + if ( _dyld_find_unwind_sections((void*)addr, &info) ) { + mh = (pint_t)info.mh; + dwarfStart = (pint_t)info.dwarf_section; + dwarfLen = (pint_t)info.dwarf_section_length; + compactStart = (pint_t)info.compact_unwind_section; + return true; + } + return false; +} + + +inline bool LocalAddressSpace::findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset) +{ + dl_info dyldInfo; + if ( dladdr((void*)addr, &dyldInfo) ) { + if ( dyldInfo.dli_sname != NULL ) { + strlcpy(buf, dyldInfo.dli_sname, bufLen); + *offset = (addr - (pint_t)dyldInfo.dli_saddr); + return true; + } + } + return false; +} + + + +#if UNW_REMOTE + +/// +/// OtherAddressSpace is used as a template parameter to UnwindCursor when unwinding a thread +/// in the another process. The other process can be a different endianness and a different +/// pointer size and is handled by the P template parameter. +/// +template +class OtherAddressSpace +{ +public: + OtherAddressSpace(task_t task) : fTask(task) {} + + typedef typename P::uint_t pint_t; + + uint8_t get8(pint_t addr); + uint16_t get16(pint_t addr); + uint32_t get32(pint_t addr); + uint64_t get64(pint_t addr); + pint_t getP(pint_t addr); + uint64_t getULEB128(pint_t& addr, pint_t end); + int64_t getSLEB128(pint_t& addr, pint_t end); + pint_t getEncodedP(pint_t& addr, pint_t end, uint8_t encoding); + bool findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset); + bool findUnwindSections(pint_t addr, unwind_sections& info); +private: + void* localCopy(pint_t addr); + + + task_t fTask; +}; + + +template +uint8_t OtherAddressSpace

::get8(pint_t addr) +{ + return *((uint8_t*)localCopy(addr)); +} + +template +uint16_t OtherAddressSpace

::get16(pint_t addr) +{ + return P::E::get16(*(uint16_t*)localCopy(addr)); +} + +template +uint32_t OtherAddressSpace

::get32(pint_t addr) +{ + return P::E::get32(*(uint32_t*)localCopy(addr)); +} + +template +uint64_t OtherAddressSpace

::get64(pint_t addr) +{ + return P::E::get64(*(uint64_t*)localCopy(addr)); +} + +template +typename P::uint_t OtherAddressSpace

::getP(pint_t addr) +{ + return P::getP(*(uint64_t*)localCopy(addr)); +} + +template +uint64_t OtherAddressSpace

::getULEB128(pint_t& addr, pint_t end) +{ + uintptr_t size = (end - addr); + LocalAddressSpace::pint_t laddr = (LocalAddressSpace::pint_t)localCopy(addr); + LocalAddressSpace::pint_t sladdr = laddr; + uint64_t result = LocalAddressSpace::getULEB128(laddr, laddr+size); + addr += (laddr-sladdr); + return result; +} + +template +int64_t OtherAddressSpace

::getSLEB128(pint_t& addr, pint_t end) +{ + uintptr_t size = (end - addr); + LocalAddressSpace::pint_t laddr = (LocalAddressSpace::pint_t)localCopy(addr); + LocalAddressSpace::pint_t sladdr = laddr; + uint64_t result = LocalAddressSpace::getSLEB128(laddr, laddr+size); + addr += (laddr-sladdr); + return result; +} + +template +void* OtherAddressSpace

::localCopy(pint_t addr) +{ + // FIX ME +} + +template +bool OtherAddressSpace

::findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset) +{ + // FIX ME +} + + + +/// +/// unw_addr_space is the base class that abstract unw_addr_space_t type in libunwind.h points to. +/// +struct unw_addr_space +{ + cpu_type_t cpuType; + task_t taskPort; +}; + + +/// +/// unw_addr_space_i386 is the concrete instance that a unw_addr_space_t points to when examining +/// a 32-bit intel process. +/// +struct unw_addr_space_i386 : public unw_addr_space +{ + unw_addr_space_i386(task_t task) : oas(task) {} + OtherAddressSpace > oas; +}; + + +/// +/// unw_addr_space_x86_64 is the concrete instance that a unw_addr_space_t points to when examining +/// a 64-bit intel process. +/// +struct unw_addr_space_x86_64 : public unw_addr_space +{ + unw_addr_space_x86_64(task_t task) : oas(task) {} + OtherAddressSpace > oas; +}; + + +/// +/// unw_addr_space_ppc is the concrete instance that a unw_addr_space_t points to when examining +/// a 32-bit PowerPC process. +/// +struct unw_addr_space_ppc : public unw_addr_space +{ + unw_addr_space_ppc(task_t task) : oas(task) {} + OtherAddressSpace > oas; +}; + + +#endif // UNW_REMOTE + + +} // namespace libunwind + + + +#endif // __ADDRESSSPACE_HPP__ + + + + diff --git a/src/ld/parsers/libunwind/DwarfInstructions.hpp b/src/ld/parsers/libunwind/DwarfInstructions.hpp new file mode 100644 index 0000000..b04582d --- /dev/null +++ b/src/ld/parsers/libunwind/DwarfInstructions.hpp @@ -0,0 +1,1726 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +// +// processor specific parsing of dwarf unwind instructions +// + +#ifndef __DWARF_INSTRUCTIONS_HPP__ +#define __DWARF_INSTRUCTIONS_HPP__ + +#include +#include +#include + +#include +#include + +#include +#include + +#include "dwarf2.h" +#include "AddressSpace.hpp" +#include "Registers.hpp" +#include "DwarfParser.hpp" +#include "InternalMacros.h" +//#include "CompactUnwinder.hpp" + +#define EXTRACT_BITS(value, mask) \ + ( (value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask)))-1) ) + +#define CFI_INVALID_ADDRESS ((pint_t)(-1)) + +namespace libunwind { + +/// +/// Used by linker when parsing __eh_frame section +/// +template +struct CFI_Reference { + typedef typename A::pint_t pint_t; + uint8_t encodingOfTargetAddress; + uint32_t offsetInCFI; + pint_t targetAddress; +}; +template +struct CFI_Atom_Info { + typedef typename A::pint_t pint_t; + pint_t address; + uint32_t size; + bool isCIE; + union { + struct { + CFI_Reference function; + CFI_Reference cie; + CFI_Reference lsda; + uint32_t compactUnwindInfo; + } fdeInfo; + struct { + CFI_Reference personality; + } cieInfo; + } u; +}; + +typedef void (*WarnFunc)(void* ref, uint64_t funcAddr, const char* msg); + +/// +/// DwarfInstructions maps abtract dwarf unwind instructions to a particular architecture +/// +template +class DwarfInstructions +{ +public: + typedef typename A::pint_t pint_t; + typedef typename A::sint_t sint_t; + + static const char* parseCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength, + CFI_Atom_Info* infos, uint32_t infosCount, void* ref, WarnFunc warn); + + + static compact_unwind_encoding_t createCompactEncodingFromFDE(A& addressSpace, pint_t fdeStart, + pint_t* lsda, pint_t* personality, + char warningBuffer[1024]); + + static int stepWithDwarf(A& addressSpace, pint_t pc, pint_t fdeStart, R& registers); + +private: + + enum { + DW_X86_64_RET_ADDR = 16 + }; + + enum { + DW_X86_RET_ADDR = 8 + }; + + static pint_t evaluateExpression(pint_t expression, A& addressSpace, const R& registers, pint_t initialStackValue); + static pint_t getSavedRegister(A& addressSpace, const R& registers, pint_t cfa, + const typename CFI_Parser::RegisterLocation& savedReg); + static double getSavedFloatRegister(A& addressSpace, const R& registers, pint_t cfa, + const typename CFI_Parser::RegisterLocation& savedReg); + static v128 getSavedVectorRegister(A& addressSpace, const R& registers, pint_t cfa, + const typename CFI_Parser::RegisterLocation& savedReg); + + // x86 specific variants + static int lastRestoreReg(const Registers_x86&); + static bool isReturnAddressRegister(int regNum, const Registers_x86&); + static pint_t getCFA(A& addressSpace, const typename CFI_Parser::PrologInfo& prolog, const Registers_x86&); + + static uint32_t getEBPEncodedRegister(uint32_t reg, int32_t regOffsetFromBaseOffset, bool& failure); + static compact_unwind_encoding_t encodeToUseDwarf(const Registers_x86&); + static compact_unwind_encoding_t createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_x86&, const typename CFI_Parser::PrologInfo& prolog, + char warningBuffer[1024]); + + // x86_64 specific variants + static int lastRestoreReg(const Registers_x86_64&); + static bool isReturnAddressRegister(int regNum, const Registers_x86_64&); + static pint_t getCFA(A& addressSpace, const typename CFI_Parser::PrologInfo& prolog, const Registers_x86_64&); + + static uint32_t getRBPEncodedRegister(uint32_t reg, int32_t regOffsetFromBaseOffset, bool& failure); + static compact_unwind_encoding_t encodeToUseDwarf(const Registers_x86_64&); + static compact_unwind_encoding_t createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_x86_64&, const typename CFI_Parser::PrologInfo& prolog, + char warningBuffer[1024]); + + // ppc specific variants + static int lastRestoreReg(const Registers_ppc&); + static bool isReturnAddressRegister(int regNum, const Registers_ppc&); + static pint_t getCFA(A& addressSpace, const typename CFI_Parser::PrologInfo& prolog, const Registers_ppc&); + static compact_unwind_encoding_t encodeToUseDwarf(const Registers_ppc&); + static compact_unwind_encoding_t createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_ppc&, const typename CFI_Parser::PrologInfo& prolog, + char warningBuffer[1024]); +}; + + + + +template +const char* DwarfInstructions::parseCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength, + CFI_Atom_Info* infos, uint32_t infosCount, void* ref, WarnFunc warn) +{ + typename CFI_Parser::CIE_Info cieInfo; + CFI_Atom_Info* entry = infos; + CFI_Atom_Info* end = &infos[infosCount]; + const pint_t ehSectionEnd = ehSectionStart + sectionLength; + for (pint_t p=ehSectionStart; p < ehSectionEnd; ) { + pint_t currentCFI = p; + uint64_t cfiLength = addressSpace.get32(p); + p += 4; + if ( cfiLength == 0xffffffff ) { + // 0xffffffff means length is really next 8 bytes + cfiLength = addressSpace.get64(p); + p += 8; + } + if ( cfiLength == 0 ) + return NULL; // end marker + if ( entry >= end ) + return "too little space allocated for parseCFIs"; + pint_t nextCFI = p + cfiLength; + uint32_t id = addressSpace.get32(p); + if ( id == 0 ) { + // is CIE + const char* err = CFI_Parser::parseCIE(addressSpace, currentCFI, &cieInfo); + if ( err != NULL ) + return err; + entry->address = currentCFI; + entry->size = nextCFI - currentCFI; + entry->isCIE = true; + entry->u.cieInfo.personality.targetAddress = cieInfo.personality; + entry->u.cieInfo.personality.offsetInCFI = cieInfo.personalityOffsetInCIE; + entry->u.cieInfo.personality.encodingOfTargetAddress = cieInfo.personalityEncoding; + ++entry; + } + else { + // is FDE + entry->address = currentCFI; + entry->size = nextCFI - currentCFI; + entry->isCIE = false; + entry->u.fdeInfo.function.targetAddress = CFI_INVALID_ADDRESS; + entry->u.fdeInfo.cie.targetAddress = CFI_INVALID_ADDRESS; + entry->u.fdeInfo.lsda.targetAddress = CFI_INVALID_ADDRESS; + uint32_t ciePointer = addressSpace.get32(p); + pint_t cieStart = p-ciePointer; + // validate pointer to CIE is within section + if ( (cieStart < ehSectionStart) || (cieStart > ehSectionEnd) ) + return "FDE points to CIE outside __eh_frame section"; + // optimize usual case where cie is same for all FDEs + if ( cieStart != cieInfo.cieStart ) { + const char* err = CFI_Parser::parseCIE(addressSpace, cieStart, &cieInfo); + if ( err != NULL ) + return err; + } + entry->u.fdeInfo.cie.targetAddress = cieStart; + entry->u.fdeInfo.cie.offsetInCFI = p-currentCFI; + entry->u.fdeInfo.cie.encodingOfTargetAddress = DW_EH_PE_sdata4 | DW_EH_PE_pcrel; + p += 4; + // parse pc begin and range + pint_t offsetOfFunctionAddress = p-currentCFI; + pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding); + pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding & 0x0F); + //fprintf(stderr, "FDE with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pcStart, (uint64_t)(pcStart+pcRange)); + // test if pc is within the function this FDE covers + entry->u.fdeInfo.function.targetAddress = pcStart; + entry->u.fdeInfo.function.offsetInCFI = offsetOfFunctionAddress; + entry->u.fdeInfo.function.encodingOfTargetAddress = cieInfo.pointerEncoding; + // check for augmentation length + if ( cieInfo.fdesHaveAugmentationData ) { + uintptr_t augLen = addressSpace.getULEB128(p, nextCFI); + pint_t endOfAug = p + augLen; + if ( cieInfo.lsdaEncoding != 0 ) { + // peek at value (without indirection). Zero means no lsda + pint_t lsdaStart = p; + if ( addressSpace.getEncodedP(p, nextCFI, cieInfo.lsdaEncoding & 0x0F) != 0 ) { + // reset pointer and re-parse lsda address + p = lsdaStart; + pint_t offsetOfLSDAAddress = p-currentCFI; + entry->u.fdeInfo.lsda.targetAddress = addressSpace.getEncodedP(p, nextCFI, cieInfo.lsdaEncoding); + entry->u.fdeInfo.lsda.offsetInCFI = offsetOfLSDAAddress; + entry->u.fdeInfo.lsda.encodingOfTargetAddress = cieInfo.lsdaEncoding; + } + } + p = endOfAug; + } + // compute compact unwind encoding + typename CFI_Parser::FDE_Info fdeInfo; + fdeInfo.fdeStart = currentCFI; + fdeInfo.fdeLength = nextCFI - currentCFI; + fdeInfo.fdeInstructions = p; + fdeInfo.pcStart = pcStart; + fdeInfo.pcEnd = pcStart + pcRange; + fdeInfo.lsda = entry->u.fdeInfo.lsda.targetAddress; + typename CFI_Parser::PrologInfo prolog; + R dummy; // for proper selection of architecture specific functions + if ( CFI_Parser::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, CFI_INVALID_ADDRESS, &prolog) ) { + char warningBuffer[1024]; + entry->u.fdeInfo.compactUnwindInfo = createCompactEncodingFromProlog(addressSpace, fdeInfo.pcStart, dummy, prolog, warningBuffer); + if ( fdeInfo.lsda != CFI_INVALID_ADDRESS ) + entry->u.fdeInfo.compactUnwindInfo |= UNWIND_HAS_LSDA; + if ( warningBuffer[0] != '\0' ) + warn(ref, fdeInfo.pcStart, warningBuffer); + } + else { + warn(ref, CFI_INVALID_ADDRESS, "dwarf unwind instructions could not be parsed"); + entry->u.fdeInfo.compactUnwindInfo = encodeToUseDwarf(dummy); + } + ++entry; + } + p = nextCFI; + } + if ( entry != end ) + return "wrong entry count for parseCFIs"; + return NULL; // success +} + + + + +template +compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromFDE(A& addressSpace, pint_t fdeStart, + pint_t* lsda, pint_t* personality, + char warningBuffer[1024]) +{ + typename CFI_Parser::FDE_Info fdeInfo; + typename CFI_Parser::CIE_Info cieInfo; + R dummy; // for proper selection of architecture specific functions + if ( CFI_Parser::decodeFDE(addressSpace, fdeStart, &fdeInfo, &cieInfo) == NULL ) { + typename CFI_Parser::PrologInfo prolog; + if ( CFI_Parser::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, CFI_INVALID_ADDRESS, &prolog) ) { + *lsda = fdeInfo.lsda; + *personality = cieInfo.personality; + compact_unwind_encoding_t encoding; + encoding = createCompactEncodingFromProlog(addressSpace, fdeInfo.pcStart, dummy, prolog, warningBuffer); + if ( fdeInfo.lsda != 0 ) + encoding |= UNWIND_HAS_LSDA; + return encoding; + } + else { + strcpy(warningBuffer, "dwarf unwind instructions could not be parsed"); + return encodeToUseDwarf(dummy); + } + } + else { + strcpy(warningBuffer, "dwarf FDE could not be parsed"); + return encodeToUseDwarf(dummy); + } +} + + +template +typename A::pint_t DwarfInstructions::getSavedRegister(A& addressSpace, const R& registers, pint_t cfa, + const typename CFI_Parser::RegisterLocation& savedReg) +{ + switch ( savedReg.location ) { + case CFI_Parser::kRegisterInCFA: + return addressSpace.getP(cfa + savedReg.value); + + case CFI_Parser::kRegisterAtExpression: + return addressSpace.getP(evaluateExpression(savedReg.value, addressSpace, registers, cfa)); + + case CFI_Parser::kRegisterIsExpression: + return evaluateExpression(savedReg.value, addressSpace, registers, cfa); + + case CFI_Parser::kRegisterInRegister: + return registers.getRegister(savedReg.value); + + case CFI_Parser::kRegisterUnused: + case CFI_Parser::kRegisterOffsetFromCFA: + // FIX ME + break; + } + ABORT("unsupported restore location for register"); +} + +template +double DwarfInstructions::getSavedFloatRegister(A& addressSpace, const R& registers, pint_t cfa, + const typename CFI_Parser::RegisterLocation& savedReg) +{ + switch ( savedReg.location ) { + case CFI_Parser::kRegisterInCFA: + return addressSpace.getDouble(cfa + savedReg.value); + + case CFI_Parser::kRegisterAtExpression: + return addressSpace.getDouble(evaluateExpression(savedReg.value, addressSpace, registers, cfa)); + + case CFI_Parser::kRegisterIsExpression: + case CFI_Parser::kRegisterUnused: + case CFI_Parser::kRegisterOffsetFromCFA: + case CFI_Parser::kRegisterInRegister: + // FIX ME + break; + } + ABORT("unsupported restore location for float register"); +} + +template +v128 DwarfInstructions::getSavedVectorRegister(A& addressSpace, const R& registers, pint_t cfa, + const typename CFI_Parser::RegisterLocation& savedReg) +{ + switch ( savedReg.location ) { + case CFI_Parser::kRegisterInCFA: + return addressSpace.getVector(cfa + savedReg.value); + + case CFI_Parser::kRegisterAtExpression: + return addressSpace.getVector(evaluateExpression(savedReg.value, addressSpace, registers, cfa)); + + case CFI_Parser::kRegisterIsExpression: + case CFI_Parser::kRegisterUnused: + case CFI_Parser::kRegisterOffsetFromCFA: + case CFI_Parser::kRegisterInRegister: + // FIX ME + break; + } + ABORT("unsupported restore location for vector register"); +} + + +template +int DwarfInstructions::stepWithDwarf(A& addressSpace, pint_t pc, pint_t fdeStart, R& registers) +{ + //fprintf(stderr, "stepWithDwarf(pc=0x%0llX, fdeStart=0x%0llX)\n", (uint64_t)pc, (uint64_t)fdeStart); + typename CFI_Parser::FDE_Info fdeInfo; + typename CFI_Parser::CIE_Info cieInfo; + if ( CFI_Parser::decodeFDE(addressSpace, fdeStart, &fdeInfo, &cieInfo) == NULL ) { + typename CFI_Parser::PrologInfo prolog; + if ( CFI_Parser::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, pc, &prolog) ) { + R newRegisters = registers; + + // get pointer to cfa (architecture specific) + pint_t cfa = getCFA(addressSpace, prolog, registers); + + // restore registers that dwarf says were saved + pint_t returnAddress = 0; + for (int i=0; i <= lastRestoreReg(newRegisters); ++i) { + if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterUnused ) { + if ( registers.validFloatRegister(i) ) + newRegisters.setFloatRegister(i, getSavedFloatRegister(addressSpace, registers, cfa, prolog.savedRegisters[i])); + else if ( registers.validVectorRegister(i) ) + newRegisters.setVectorRegister(i, getSavedVectorRegister(addressSpace, registers, cfa, prolog.savedRegisters[i])); + else if ( isReturnAddressRegister(i, registers) ) + returnAddress = getSavedRegister(addressSpace, registers, cfa, prolog.savedRegisters[i]); + else if ( registers.validRegister(i) ) + newRegisters.setRegister(i, getSavedRegister(addressSpace, registers, cfa, prolog.savedRegisters[i])); + else + return UNW_EBADREG; + } + } + + // by definition the CFA is the stack pointer at the call site, so restoring SP means setting it to CFA + newRegisters.setSP(cfa); + + // return address is address after call site instruction, so setting IP to that does a return + newRegisters.setIP(returnAddress); + + // do the actual step by replacing the register set with the new ones + registers = newRegisters; + + return UNW_STEP_SUCCESS; + } + } + return UNW_EBADFRAME; +} + + + +template +typename A::pint_t DwarfInstructions::evaluateExpression(pint_t expression, A& addressSpace, + const R& registers, pint_t initialStackValue) +{ + const bool log = false; + pint_t p = expression; + pint_t expressionEnd = expression+20; // just need something until length is read + uint64_t length = addressSpace.getULEB128(p, expressionEnd); + expressionEnd = p + length; + if (log) fprintf(stderr, "evaluateExpression(): length=%llu\n", length); + pint_t stack[100]; + pint_t* sp = stack; + *(++sp) = initialStackValue; + + while ( p < expressionEnd ) { + if (log) { + for(pint_t* t = sp; t > stack; --t) { + fprintf(stderr, "sp[] = 0x%llX\n", (uint64_t)(*t)); + } + } + uint8_t opcode = addressSpace.get8(p++); + sint_t svalue; + pint_t value; + uint32_t reg; + switch (opcode) { + case DW_OP_addr: + // push immediate address sized value + value = addressSpace.getP(p); + p += sizeof(pint_t); + *(++sp) = value; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_deref: + // pop stack, dereference, push result + value = *sp--; + *(++sp) = addressSpace.getP(value); + if (log) fprintf(stderr, "dereference 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_const1u: + // push immediate 1 byte value + value = addressSpace.get8(p); + p += 1; + *(++sp) = value; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_const1s: + // push immediate 1 byte signed value + svalue = (int8_t)addressSpace.get8(p); + p += 1; + *(++sp) = svalue; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)svalue); + break; + + case DW_OP_const2u: + // push immediate 2 byte value + value = addressSpace.get16(p); + p += 2; + *(++sp) = value; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_const2s: + // push immediate 2 byte signed value + svalue = (int16_t)addressSpace.get16(p); + p += 2; + *(++sp) = svalue; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)svalue); + break; + + case DW_OP_const4u: + // push immediate 4 byte value + value = addressSpace.get32(p); + p += 4; + *(++sp) = value; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_const4s: + // push immediate 4 byte signed value + svalue = (int32_t)addressSpace.get32(p); + p += 4; + *(++sp) = svalue; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)svalue); + break; + + case DW_OP_const8u: + // push immediate 8 byte value + value = addressSpace.get64(p); + p += 8; + *(++sp) = value; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_const8s: + // push immediate 8 byte signed value + value = (int32_t)addressSpace.get64(p); + p += 8; + *(++sp) = value; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_constu: + // push immediate ULEB128 value + value = addressSpace.getULEB128(p, expressionEnd); + *(++sp) = value; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_consts: + // push immediate SLEB128 value + svalue = addressSpace.getSLEB128(p, expressionEnd); + *(++sp) = svalue; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)svalue); + break; + + case DW_OP_dup: + // push top of stack + value = *sp; + *(++sp) = value; + if (log) fprintf(stderr, "duplicate top of stack\n"); + break; + + case DW_OP_drop: + // pop + --sp; + if (log) fprintf(stderr, "pop top of stack\n"); + break; + + case DW_OP_over: + // dup second + value = sp[-1]; + *(++sp) = value; + if (log) fprintf(stderr, "duplicate second in stack\n"); + break; + + case DW_OP_pick: + // pick from + reg = addressSpace.get8(p); + p += 1; + value = sp[-reg]; + *(++sp) = value; + if (log) fprintf(stderr, "duplicate %d in stack\n", reg); + break; + + case DW_OP_swap: + // swap top two + value = sp[0]; + sp[0] = sp[-1]; + sp[-1] = value; + if (log) fprintf(stderr, "swap top of stack\n"); + break; + + case DW_OP_rot: + // rotate top three + value = sp[0]; + sp[0] = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = value; + if (log) fprintf(stderr, "rotate top three of stack\n"); + break; + + case DW_OP_xderef: + // pop stack, dereference, push result + value = *sp--; + *sp = *((uint64_t*)value); + if (log) fprintf(stderr, "x-dereference 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_abs: + svalue = *sp; + if ( svalue < 0 ) + *sp = -svalue; + if (log) fprintf(stderr, "abs\n"); + break; + + case DW_OP_and: + value = *sp--; + *sp &= value; + if (log) fprintf(stderr, "and\n"); + break; + + case DW_OP_div: + svalue = *sp--; + *sp = *sp / svalue; + if (log) fprintf(stderr, "div\n"); + break; + + case DW_OP_minus: + svalue = *sp--; + *sp = *sp - svalue; + if (log) fprintf(stderr, "minus\n"); + break; + + case DW_OP_mod: + svalue = *sp--; + *sp = *sp % svalue; + if (log) fprintf(stderr, "module\n"); + break; + + case DW_OP_mul: + svalue = *sp--; + *sp = *sp * svalue; + if (log) fprintf(stderr, "mul\n"); + break; + + case DW_OP_neg: + *sp = 0 - *sp; + if (log) fprintf(stderr, "neg\n"); + break; + + case DW_OP_not: + svalue = *sp; + *sp = ~svalue; + if (log) fprintf(stderr, "not\n"); + break; + + case DW_OP_or: + value = *sp--; + *sp |= value; + if (log) fprintf(stderr, "or\n"); + break; + + case DW_OP_plus: + value = *sp--; + *sp += value; + if (log) fprintf(stderr, "plus\n"); + break; + + case DW_OP_plus_uconst: + // pop stack, add uelb128 constant, push result + *sp += addressSpace.getULEB128(p, expressionEnd); + if (log) fprintf(stderr, "add constant\n"); + break; + + case DW_OP_shl: + value = *sp--; + *sp = *sp << value; + if (log) fprintf(stderr, "shift left\n"); + break; + + case DW_OP_shr: + value = *sp--; + *sp = *sp >> value; + if (log) fprintf(stderr, "shift left\n"); + break; + + case DW_OP_shra: + value = *sp--; + svalue = *sp; + *sp = svalue >> value; + if (log) fprintf(stderr, "shift left arithmetric\n"); + break; + + case DW_OP_xor: + value = *sp--; + *sp ^= value; + if (log) fprintf(stderr, "xor\n"); + break; + + case DW_OP_skip: + svalue = (int16_t)addressSpace.get16(p); + p += 2; + p += svalue; + if (log) fprintf(stderr, "skip %lld\n", (uint64_t)svalue); + break; + + case DW_OP_bra: + svalue = (int16_t)addressSpace.get16(p); + p += 2; + if ( *sp-- ) + p += svalue; + if (log) fprintf(stderr, "bra %lld\n", (uint64_t)svalue); + break; + + case DW_OP_eq: + value = *sp--; + *sp = (*sp == value); + if (log) fprintf(stderr, "eq\n"); + break; + + case DW_OP_ge: + value = *sp--; + *sp = (*sp >= value); + if (log) fprintf(stderr, "ge\n"); + break; + + case DW_OP_gt: + value = *sp--; + *sp = (*sp > value); + if (log) fprintf(stderr, "gt\n"); + break; + + case DW_OP_le: + value = *sp--; + *sp = (*sp <= value); + if (log) fprintf(stderr, "le\n"); + break; + + case DW_OP_lt: + value = *sp--; + *sp = (*sp < value); + if (log) fprintf(stderr, "lt\n"); + break; + + case DW_OP_ne: + value = *sp--; + *sp = (*sp != value); + if (log) fprintf(stderr, "ne\n"); + break; + + case DW_OP_lit0: + case DW_OP_lit1: + case DW_OP_lit2: + case DW_OP_lit3: + case DW_OP_lit4: + case DW_OP_lit5: + case DW_OP_lit6: + case DW_OP_lit7: + case DW_OP_lit8: + case DW_OP_lit9: + case DW_OP_lit10: + case DW_OP_lit11: + case DW_OP_lit12: + case DW_OP_lit13: + case DW_OP_lit14: + case DW_OP_lit15: + case DW_OP_lit16: + case DW_OP_lit17: + case DW_OP_lit18: + case DW_OP_lit19: + case DW_OP_lit20: + case DW_OP_lit21: + case DW_OP_lit22: + case DW_OP_lit23: + case DW_OP_lit24: + case DW_OP_lit25: + case DW_OP_lit26: + case DW_OP_lit27: + case DW_OP_lit28: + case DW_OP_lit29: + case DW_OP_lit30: + case DW_OP_lit31: + value = opcode - DW_OP_lit0; + *(++sp) = value; + if (log) fprintf(stderr, "push literal 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_reg0: + case DW_OP_reg1: + case DW_OP_reg2: + case DW_OP_reg3: + case DW_OP_reg4: + case DW_OP_reg5: + case DW_OP_reg6: + case DW_OP_reg7: + case DW_OP_reg8: + case DW_OP_reg9: + case DW_OP_reg10: + case DW_OP_reg11: + case DW_OP_reg12: + case DW_OP_reg13: + case DW_OP_reg14: + case DW_OP_reg15: + case DW_OP_reg16: + case DW_OP_reg17: + case DW_OP_reg18: + case DW_OP_reg19: + case DW_OP_reg20: + case DW_OP_reg21: + case DW_OP_reg22: + case DW_OP_reg23: + case DW_OP_reg24: + case DW_OP_reg25: + case DW_OP_reg26: + case DW_OP_reg27: + case DW_OP_reg28: + case DW_OP_reg29: + case DW_OP_reg30: + case DW_OP_reg31: + reg = opcode - DW_OP_reg0; + *(++sp) = registers.getRegister(reg); + if (log) fprintf(stderr, "push reg %d\n", reg); + break; + + case DW_OP_regx: + reg = addressSpace.getULEB128(p, expressionEnd); + *(++sp) = registers.getRegister(reg); + if (log) fprintf(stderr, "push reg %d + 0x%llX\n", reg, (uint64_t)svalue); + break; + + case DW_OP_breg0: + case DW_OP_breg1: + case DW_OP_breg2: + case DW_OP_breg3: + case DW_OP_breg4: + case DW_OP_breg5: + case DW_OP_breg6: + case DW_OP_breg7: + case DW_OP_breg8: + case DW_OP_breg9: + case DW_OP_breg10: + case DW_OP_breg11: + case DW_OP_breg12: + case DW_OP_breg13: + case DW_OP_breg14: + case DW_OP_breg15: + case DW_OP_breg16: + case DW_OP_breg17: + case DW_OP_breg18: + case DW_OP_breg19: + case DW_OP_breg20: + case DW_OP_breg21: + case DW_OP_breg22: + case DW_OP_breg23: + case DW_OP_breg24: + case DW_OP_breg25: + case DW_OP_breg26: + case DW_OP_breg27: + case DW_OP_breg28: + case DW_OP_breg29: + case DW_OP_breg30: + case DW_OP_breg31: + reg = opcode - DW_OP_breg0; + svalue = addressSpace.getSLEB128(p, expressionEnd); + *(++sp) = registers.getRegister(reg) + svalue; + if (log) fprintf(stderr, "push reg %d + 0x%llX\n", reg, (uint64_t)svalue); + break; + + case DW_OP_bregx: + reg = addressSpace.getULEB128(p, expressionEnd); + svalue = addressSpace.getSLEB128(p, expressionEnd); + *(++sp) = registers.getRegister(reg) + svalue; + if (log) fprintf(stderr, "push reg %d + 0x%llX\n", reg, (uint64_t)svalue); + break; + + case DW_OP_fbreg: + ABORT("DW_OP_fbreg not implemented"); + break; + + case DW_OP_piece: + ABORT("DW_OP_piece not implemented"); + break; + + case DW_OP_deref_size: + // pop stack, dereference, push result + value = *sp--; + switch ( addressSpace.get8(p++) ) { + case 1: + value = addressSpace.get8(value); + break; + case 2: + value = addressSpace.get16(value); + break; + case 4: + value = addressSpace.get32(value); + break; + case 8: + value = addressSpace.get64(value); + break; + default: + ABORT("DW_OP_deref_size with bad size"); + } + *(++sp) = value; + if (log) fprintf(stderr, "sized dereference 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_xderef_size: + case DW_OP_nop: + case DW_OP_push_object_addres: + case DW_OP_call2: + case DW_OP_call4: + case DW_OP_call_ref: + default: + ABORT("dwarf opcode not implemented"); + } + + } + if (log) fprintf(stderr, "expression evaluates to 0x%llX\n", (uint64_t)*sp); + return *sp; +} + + + +// +// x86_64 specific functions +// + +template +int DwarfInstructions::lastRestoreReg(const Registers_x86_64&) +{ + COMPILE_TIME_ASSERT( (int)CFI_Parser::kMaxRegisterNumber > (int)DW_X86_64_RET_ADDR ); + return DW_X86_64_RET_ADDR; +} + +template +bool DwarfInstructions::isReturnAddressRegister(int regNum, const Registers_x86_64&) +{ + return (regNum == DW_X86_64_RET_ADDR); +} + +template +typename A::pint_t DwarfInstructions::getCFA(A& addressSpace, const typename CFI_Parser::PrologInfo& prolog, + const Registers_x86_64& registers) +{ + if ( prolog.cfaRegister != 0 ) + return registers.getRegister(prolog.cfaRegister) + prolog.cfaRegisterOffset; + else if ( prolog.cfaExpression != 0 ) + return evaluateExpression(prolog.cfaExpression, addressSpace, registers, 0); + else + ABORT("getCFA(): unknown location for x86_64 cfa"); +} + + + +template +compact_unwind_encoding_t DwarfInstructions::encodeToUseDwarf(const Registers_x86_64&) +{ + return UNWIND_X86_64_MODE_DWARF; +} + +template +compact_unwind_encoding_t DwarfInstructions::encodeToUseDwarf(const Registers_x86&) +{ + return UNWIND_X86_MODE_DWARF; +} + + + +template +uint32_t DwarfInstructions::getRBPEncodedRegister(uint32_t reg, int32_t regOffsetFromBaseOffset, bool& failure) +{ + if ( (regOffsetFromBaseOffset < 0) || (regOffsetFromBaseOffset > 32) ) { + failure = true; + return 0; + } + unsigned int slotIndex = regOffsetFromBaseOffset/8; + + switch ( reg ) { + case UNW_X86_64_RBX: + return UNWIND_X86_64_REG_RBX << (slotIndex*3); + case UNW_X86_64_R12: + return UNWIND_X86_64_REG_R12 << (slotIndex*3); + case UNW_X86_64_R13: + return UNWIND_X86_64_REG_R13 << (slotIndex*3); + case UNW_X86_64_R14: + return UNWIND_X86_64_REG_R14 << (slotIndex*3); + case UNW_X86_64_R15: + return UNWIND_X86_64_REG_R15 << (slotIndex*3); + } + + // invalid register + failure = true; + return 0; +} + + + +template +compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_x86_64& r, const typename CFI_Parser::PrologInfo& prolog, + char warningBuffer[1024]) +{ + warningBuffer[0] = '\0'; + + if ( prolog.registerSavedTwiceInCIE == DW_X86_64_RET_ADDR ) { + warningBuffer[0] = '\0'; // silently disable conversion to compact unwind by linker + return UNWIND_X86_64_MODE_DWARF; + } + // don't create compact unwind info for unsupported dwarf kinds + if ( prolog.registerSavedMoreThanOnce ) { + strcpy(warningBuffer, "register saved more than once (might be shrink wrap)"); + return UNWIND_X86_64_MODE_DWARF; + } + if ( prolog.cfaOffsetWasNegative ) { + strcpy(warningBuffer, "cfa had negative offset (dwarf might contain epilog)"); + return UNWIND_X86_64_MODE_DWARF; + } + if ( prolog.spExtraArgSize != 0 ) { + strcpy(warningBuffer, "dwarf uses DW_CFA_GNU_args_size"); + return UNWIND_X86_64_MODE_DWARF; + } + if ( prolog.sameValueUsed ) { + strcpy(warningBuffer, "dwarf uses DW_CFA_same_value"); + return UNWIND_X86_64_MODE_DWARF; + } + + // figure out which kind of frame this function uses + bool standardRBPframe = ( + (prolog.cfaRegister == UNW_X86_64_RBP) + && (prolog.cfaRegisterOffset == 16) + && (prolog.savedRegisters[UNW_X86_64_RBP].location == CFI_Parser::kRegisterInCFA) + && (prolog.savedRegisters[UNW_X86_64_RBP].value == -16) ); + bool standardRSPframe = (prolog.cfaRegister == UNW_X86_64_RSP); + if ( !standardRBPframe && !standardRSPframe ) { + // no compact encoding for this + strcpy(warningBuffer, "does not use RBP or RSP based frame"); + return UNWIND_X86_64_MODE_DWARF; + } + + // scan which registers are saved + int saveRegisterCount = 0; + bool rbxSaved = false; + bool r12Saved = false; + bool r13Saved = false; + bool r14Saved = false; + bool r15Saved = false; + bool rbpSaved = false; + for (int i=0; i < 64; ++i) { + if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterUnused ) { + if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterInCFA ) { + sprintf(warningBuffer, "register %d saved somewhere other that in frame", i); + return UNWIND_X86_64_MODE_DWARF; + } + switch (i) { + case UNW_X86_64_RBX: + rbxSaved = true; + ++saveRegisterCount; + break; + case UNW_X86_64_R12: + r12Saved = true; + ++saveRegisterCount; + break; + case UNW_X86_64_R13: + r13Saved = true; + ++saveRegisterCount; + break; + case UNW_X86_64_R14: + r14Saved = true; + ++saveRegisterCount; + break; + case UNW_X86_64_R15: + r15Saved = true; + ++saveRegisterCount; + break; + case UNW_X86_64_RBP: + rbpSaved = true; + ++saveRegisterCount; + break; + case DW_X86_64_RET_ADDR: + break; + default: + sprintf(warningBuffer, "non-standard register %d being saved in prolog", i); + return UNWIND_X86_64_MODE_DWARF; + } + } + } + const int64_t cfaOffsetRBX = prolog.savedRegisters[UNW_X86_64_RBX].value; + const int64_t cfaOffsetR12 = prolog.savedRegisters[UNW_X86_64_R12].value; + const int64_t cfaOffsetR13 = prolog.savedRegisters[UNW_X86_64_R13].value; + const int64_t cfaOffsetR14 = prolog.savedRegisters[UNW_X86_64_R14].value; + const int64_t cfaOffsetR15 = prolog.savedRegisters[UNW_X86_64_R15].value; + const int64_t cfaOffsetRBP = prolog.savedRegisters[UNW_X86_64_RBP].value; + + // encode standard RBP frames + compact_unwind_encoding_t encoding = 0; + if ( standardRBPframe ) { + // | | + // +--------------+ <- CFA + // | ret addr | + // +--------------+ + // | rbp | + // +--------------+ <- rbp + // ~ ~ + // +--------------+ + // | saved reg3 | + // +--------------+ <- CFA - offset+16 + // | saved reg2 | + // +--------------+ <- CFA - offset+8 + // | saved reg1 | + // +--------------+ <- CFA - offset + // | | + // +--------------+ + // | | + // <- rsp + // + encoding = UNWIND_X86_64_MODE_RBP_FRAME; + + // find save location of farthest register from rbp + int furthestCfaOffset = 0; + if ( rbxSaved & (cfaOffsetRBX < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetRBX; + if ( r12Saved & (cfaOffsetR12 < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetR12; + if ( r13Saved & (cfaOffsetR13 < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetR13; + if ( r14Saved & (cfaOffsetR14 < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetR14; + if ( r15Saved & (cfaOffsetR15 < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetR15; + + if ( furthestCfaOffset == 0 ) { + // no registers saved, nothing more to encode + return encoding; + } + + // add stack offset to encoding + int rbpOffset = furthestCfaOffset + 16; + int encodedOffset = rbpOffset/(-8); + if ( encodedOffset > 255 ) { + strcpy(warningBuffer, "offset of saved registers too far to encode"); + return UNWIND_X86_64_MODE_DWARF; + } + encoding |= (encodedOffset << __builtin_ctz(UNWIND_X86_64_RBP_FRAME_OFFSET)); + + // add register saved from each stack location + bool encodingFailure = false; + if ( rbxSaved ) + encoding |= getRBPEncodedRegister(UNW_X86_64_RBX, cfaOffsetRBX - furthestCfaOffset, encodingFailure); + if ( r12Saved ) + encoding |= getRBPEncodedRegister(UNW_X86_64_R12, cfaOffsetR12 - furthestCfaOffset, encodingFailure); + if ( r13Saved ) + encoding |= getRBPEncodedRegister(UNW_X86_64_R13, cfaOffsetR13 - furthestCfaOffset, encodingFailure); + if ( r14Saved ) + encoding |= getRBPEncodedRegister(UNW_X86_64_R14, cfaOffsetR14 - furthestCfaOffset, encodingFailure); + if ( r15Saved ) + encoding |= getRBPEncodedRegister(UNW_X86_64_R15, cfaOffsetR15 - furthestCfaOffset, encodingFailure); + + if ( encodingFailure ){ + strcpy(warningBuffer, "saved registers not contiguous"); + return UNWIND_X86_64_MODE_DWARF; + } + + return encoding; + } + else { + // | | + // +--------------+ <- CFA + // | ret addr | + // +--------------+ + // | saved reg1 | + // +--------------+ <- CFA - 16 + // | saved reg2 | + // +--------------+ <- CFA - 24 + // | saved reg3 | + // +--------------+ <- CFA - 32 + // | saved reg4 | + // +--------------+ <- CFA - 40 + // | saved reg5 | + // +--------------+ <- CFA - 48 + // | saved reg6 | + // +--------------+ <- CFA - 56 + // | | + // <- esp + // + + // for RSP based frames we need to encode stack size in unwind info + encoding = UNWIND_X86_64_MODE_STACK_IMMD; + uint64_t stackValue = prolog.cfaRegisterOffset / 8; + uint32_t stackAdjust = 0; + bool immedStackSize = true; + const uint32_t stackMaxImmedValue = EXTRACT_BITS(0xFFFFFFFF,UNWIND_X86_64_FRAMELESS_STACK_SIZE); + if ( stackValue > stackMaxImmedValue ) { + // stack size is too big to fit as an immediate value, so encode offset of subq instruction in function + if ( prolog.codeOffsetAtStackDecrement == 0 ) { + strcpy(warningBuffer, "stack size is large but stack subq instruction not found"); + return UNWIND_X86_64_MODE_DWARF; + } + pint_t functionContentAdjustStackIns = funcAddr + prolog.codeOffsetAtStackDecrement - 4; + try { + uint32_t stackDecrementInCode = addressSpace.get32(functionContentAdjustStackIns); + stackAdjust = (prolog.cfaRegisterOffset - stackDecrementInCode)/8; + } + catch (...) { + strcpy(warningBuffer, "stack size is large but stack subq instruction not found"); + return UNWIND_X86_64_MODE_DWARF; + } + stackValue = functionContentAdjustStackIns - funcAddr; + immedStackSize = false; + if ( stackAdjust > 7 ) { + strcpy(warningBuffer, "stack subq instruction is too different from dwarf stack size"); + return UNWIND_X86_64_MODE_DWARF; + } + encoding = UNWIND_X86_64_MODE_STACK_IND; + } + + + // validate that saved registers are all within 6 slots abutting return address + int registers[6]; + for (int i=0; i < 6;++i) + registers[i] = 0; + if ( r15Saved ) { + if ( cfaOffsetR15 < -56 ) { + strcpy(warningBuffer, "r15 is saved too far from return address"); + return UNWIND_X86_64_MODE_DWARF; + } + registers[(cfaOffsetR15+56)/8] = UNWIND_X86_64_REG_R15; + } + if ( r14Saved ) { + if ( cfaOffsetR14 < -56 ) { + strcpy(warningBuffer, "r14 is saved too far from return address"); + return UNWIND_X86_64_MODE_DWARF; + } + registers[(cfaOffsetR14+56)/8] = UNWIND_X86_64_REG_R14; + } + if ( r13Saved ) { + if ( cfaOffsetR13 < -56 ) { + strcpy(warningBuffer, "r13 is saved too far from return address"); + return UNWIND_X86_64_MODE_DWARF; + } + registers[(cfaOffsetR13+56)/8] = UNWIND_X86_64_REG_R13; + } + if ( r12Saved ) { + if ( cfaOffsetR12 < -56 ) { + strcpy(warningBuffer, "r12 is saved too far from return address"); + return UNWIND_X86_64_MODE_DWARF; + } + registers[(cfaOffsetR12+56)/8] = UNWIND_X86_64_REG_R12; + } + if ( rbxSaved ) { + if ( cfaOffsetRBX < -56 ) { + strcpy(warningBuffer, "rbx is saved too far from return address"); + return UNWIND_X86_64_MODE_DWARF; + } + registers[(cfaOffsetRBX+56)/8] = UNWIND_X86_64_REG_RBX; + } + if ( rbpSaved ) { + if ( cfaOffsetRBP < -56 ) { + strcpy(warningBuffer, "rbp is saved too far from return address"); + return UNWIND_X86_64_MODE_DWARF; + } + registers[(cfaOffsetRBP+56)/8] = UNWIND_X86_64_REG_RBP; + } + + // validate that saved registers are contiguous and abut return address on stack + for (int i=0; i < saveRegisterCount; ++i) { + if ( registers[5-i] == 0 ) { + strcpy(warningBuffer, "registers not save contiguously in stack"); + return UNWIND_X86_64_MODE_DWARF; + } + } + + // encode register permutation + // the 10-bits are encoded differently depending on the number of registers saved + int renumregs[6]; + for (int i=6-saveRegisterCount; i < 6; ++i) { + int countless = 0; + for (int j=6-saveRegisterCount; j < i; ++j) { + if ( registers[j] < registers[i] ) + ++countless; + } + renumregs[i] = registers[i] - countless -1; + } + uint32_t permutationEncoding = 0; + switch ( saveRegisterCount ) { + case 6: + permutationEncoding |= (120*renumregs[0] + 24*renumregs[1] + 6*renumregs[2] + 2*renumregs[3] + renumregs[4]); + break; + case 5: + permutationEncoding |= (120*renumregs[1] + 24*renumregs[2] + 6*renumregs[3] + 2*renumregs[4] + renumregs[5]); + break; + case 4: + permutationEncoding |= (60*renumregs[2] + 12*renumregs[3] + 3*renumregs[4] + renumregs[5]); + break; + case 3: + permutationEncoding |= (20*renumregs[3] + 4*renumregs[4] + renumregs[5]); + break; + case 2: + permutationEncoding |= (5*renumregs[4] + renumregs[5]); + break; + case 1: + permutationEncoding |= (renumregs[5]); + break; + } + + encoding |= (stackValue << __builtin_ctz(UNWIND_X86_64_FRAMELESS_STACK_SIZE)); + encoding |= (stackAdjust << __builtin_ctz(UNWIND_X86_64_FRAMELESS_STACK_ADJUST)); + encoding |= (saveRegisterCount << __builtin_ctz(UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT)); + encoding |= (permutationEncoding << __builtin_ctz(UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION)); + return encoding; + } +} + + + + +// +// x86 specific functions +// +template +int DwarfInstructions::lastRestoreReg(const Registers_x86&) +{ + COMPILE_TIME_ASSERT( (int)CFI_Parser::kMaxRegisterNumber > (int)DW_X86_RET_ADDR ); + return DW_X86_RET_ADDR; +} + +template +bool DwarfInstructions::isReturnAddressRegister(int regNum, const Registers_x86&) +{ + return (regNum == DW_X86_RET_ADDR); +} + +template +typename A::pint_t DwarfInstructions::getCFA(A& addressSpace, const typename CFI_Parser::PrologInfo& prolog, + const Registers_x86& registers) +{ + if ( prolog.cfaRegister != 0 ) + return registers.getRegister(prolog.cfaRegister) + prolog.cfaRegisterOffset; + else if ( prolog.cfaExpression != 0 ) + return evaluateExpression(prolog.cfaExpression, addressSpace, registers, 0); + else + ABORT("getCFA(): unknown location for x86 cfa"); +} + + + + + +template +uint32_t DwarfInstructions::getEBPEncodedRegister(uint32_t reg, int32_t regOffsetFromBaseOffset, bool& failure) +{ + if ( (regOffsetFromBaseOffset < 0) || (regOffsetFromBaseOffset > 16) ) { + failure = true; + return 0; + } + unsigned int slotIndex = regOffsetFromBaseOffset/4; + + switch ( reg ) { + case UNW_X86_EBX: + return UNWIND_X86_REG_EBX << (slotIndex*3); + case UNW_X86_ECX: + return UNWIND_X86_REG_ECX << (slotIndex*3); + case UNW_X86_EDX: + return UNWIND_X86_REG_EDX << (slotIndex*3); + case UNW_X86_EDI: + return UNWIND_X86_REG_EDI << (slotIndex*3); + case UNW_X86_ESI: + return UNWIND_X86_REG_ESI << (slotIndex*3); + } + + // invalid register + failure = true; + return 0; +} + +template +compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_x86& r, const typename CFI_Parser::PrologInfo& prolog, + char warningBuffer[1024]) +{ + warningBuffer[0] = '\0'; + + if ( prolog.registerSavedTwiceInCIE == DW_X86_RET_ADDR ) { + warningBuffer[0] = '\0'; // silently disable conversion to compact unwind by linker + return UNWIND_X86_64_MODE_DWARF; + } + // don't create compact unwind info for unsupported dwarf kinds + if ( prolog.registerSavedMoreThanOnce ) { + strcpy(warningBuffer, "register saved more than once (might be shrink wrap)"); + return UNWIND_X86_MODE_DWARF; + } + if ( prolog.spExtraArgSize != 0 ) { + strcpy(warningBuffer, "dwarf uses DW_CFA_GNU_args_size"); + return UNWIND_X86_MODE_DWARF; + } + if ( prolog.sameValueUsed ) { + strcpy(warningBuffer, "dwarf uses DW_CFA_same_value"); + return UNWIND_X86_MODE_DWARF; + } + + // figure out which kind of frame this function uses + bool standardEBPframe = ( + (prolog.cfaRegister == UNW_X86_EBP) + && (prolog.cfaRegisterOffset == 8) + && (prolog.savedRegisters[UNW_X86_EBP].location == CFI_Parser::kRegisterInCFA) + && (prolog.savedRegisters[UNW_X86_EBP].value == -8) ); + bool standardESPframe = (prolog.cfaRegister == UNW_X86_ESP); + if ( !standardEBPframe && !standardESPframe ) { + // no compact encoding for this + strcpy(warningBuffer, "does not use EBP or ESP based frame"); + return UNWIND_X86_MODE_DWARF; + } + + // scan which registers are saved + int saveRegisterCount = 0; + bool ebxSaved = false; + bool ecxSaved = false; + bool edxSaved = false; + bool esiSaved = false; + bool ediSaved = false; + bool ebpSaved = false; + for (int i=0; i < 64; ++i) { + if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterUnused ) { + if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterInCFA ) { + sprintf(warningBuffer, "register %d saved somewhere other that in frame", i); + return UNWIND_X86_MODE_DWARF; + } + switch (i) { + case UNW_X86_EBX: + ebxSaved = true; + ++saveRegisterCount; + break; + case UNW_X86_ECX: + ecxSaved = true; + ++saveRegisterCount; + break; + case UNW_X86_EDX: + edxSaved = true; + ++saveRegisterCount; + break; + case UNW_X86_ESI: + esiSaved = true; + ++saveRegisterCount; + break; + case UNW_X86_EDI: + ediSaved = true; + ++saveRegisterCount; + break; + case UNW_X86_EBP: + ebpSaved = true; + ++saveRegisterCount; + break; + case DW_X86_RET_ADDR: + break; + default: + sprintf(warningBuffer, "non-standard register %d being saved in prolog", i); + return UNWIND_X86_MODE_DWARF; + } + } + } + const int32_t cfaOffsetEBX = prolog.savedRegisters[UNW_X86_EBX].value; + const int32_t cfaOffsetECX = prolog.savedRegisters[UNW_X86_ECX].value; + const int32_t cfaOffsetEDX = prolog.savedRegisters[UNW_X86_EDX].value; + const int32_t cfaOffsetEDI = prolog.savedRegisters[UNW_X86_EDI].value; + const int32_t cfaOffsetESI = prolog.savedRegisters[UNW_X86_ESI].value; + const int32_t cfaOffsetEBP = prolog.savedRegisters[UNW_X86_EBP].value; + + // encode standard RBP frames + compact_unwind_encoding_t encoding = 0; + if ( standardEBPframe ) { + // | | + // +--------------+ <- CFA + // | ret addr | + // +--------------+ + // | ebp | + // +--------------+ <- ebp + // ~ ~ + // +--------------+ + // | saved reg3 | + // +--------------+ <- CFA - offset+8 + // | saved reg2 | + // +--------------+ <- CFA - offset+e + // | saved reg1 | + // +--------------+ <- CFA - offset + // | | + // +--------------+ + // | | + // <- esp + // + encoding = UNWIND_X86_MODE_EBP_FRAME; + + // find save location of farthest register from ebp + int furthestCfaOffset = 0; + if ( ebxSaved & (cfaOffsetEBX < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetEBX; + if ( ecxSaved & (cfaOffsetECX < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetECX; + if ( edxSaved & (cfaOffsetEDX < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetEDX; + if ( ediSaved & (cfaOffsetEDI < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetEDI; + if ( esiSaved & (cfaOffsetESI < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetESI; + + if ( furthestCfaOffset == 0 ) { + // no registers saved, nothing more to encode + return encoding; + } + + // add stack offset to encoding + int ebpOffset = furthestCfaOffset + 8; + int encodedOffset = ebpOffset/(-4); + if ( encodedOffset > 255 ) { + strcpy(warningBuffer, "offset of saved registers too far to encode"); + return UNWIND_X86_MODE_DWARF; + } + encoding |= (encodedOffset << __builtin_ctz(UNWIND_X86_EBP_FRAME_OFFSET)); + + // add register saved from each stack location + bool encodingFailure = false; + if ( ebxSaved ) + encoding |= getEBPEncodedRegister(UNW_X86_EBX, cfaOffsetEBX - furthestCfaOffset, encodingFailure); + if ( ecxSaved ) + encoding |= getEBPEncodedRegister(UNW_X86_ECX, cfaOffsetECX - furthestCfaOffset, encodingFailure); + if ( edxSaved ) + encoding |= getEBPEncodedRegister(UNW_X86_EDX, cfaOffsetEDX - furthestCfaOffset, encodingFailure); + if ( ediSaved ) + encoding |= getEBPEncodedRegister(UNW_X86_EDI, cfaOffsetEDI - furthestCfaOffset, encodingFailure); + if ( esiSaved ) + encoding |= getEBPEncodedRegister(UNW_X86_ESI, cfaOffsetESI - furthestCfaOffset, encodingFailure); + + if ( encodingFailure ){ + strcpy(warningBuffer, "saved registers not contiguous"); + return UNWIND_X86_MODE_DWARF; + } + + return encoding; + } + else { + // | | + // +--------------+ <- CFA + // | ret addr | + // +--------------+ + // | saved reg1 | + // +--------------+ <- CFA - 8 + // | saved reg2 | + // +--------------+ <- CFA - 12 + // | saved reg3 | + // +--------------+ <- CFA - 16 + // | saved reg4 | + // +--------------+ <- CFA - 20 + // | saved reg5 | + // +--------------+ <- CFA - 24 + // | saved reg6 | + // +--------------+ <- CFA - 28 + // | | + // <- esp + // + + // for ESP based frames we need to encode stack size in unwind info + encoding = UNWIND_X86_MODE_STACK_IMMD; + uint64_t stackValue = prolog.cfaRegisterOffset / 4; + uint32_t stackAdjust = 0; + bool immedStackSize = true; + const uint32_t stackMaxImmedValue = EXTRACT_BITS(0xFFFFFFFF,UNWIND_X86_FRAMELESS_STACK_SIZE); + if ( stackValue > stackMaxImmedValue ) { + // stack size is too big to fit as an immediate value, so encode offset of subq instruction in function + pint_t functionContentAdjustStackIns = funcAddr + prolog.codeOffsetAtStackDecrement - 4; + uint32_t stackDecrementInCode = addressSpace.get32(functionContentAdjustStackIns); + stackAdjust = (prolog.cfaRegisterOffset - stackDecrementInCode)/4; + stackValue = functionContentAdjustStackIns - funcAddr; + immedStackSize = false; + if ( stackAdjust > 7 ) { + strcpy(warningBuffer, "stack subq instruction is too different from dwarf stack size"); + return UNWIND_X86_MODE_DWARF; + } + encoding = UNWIND_X86_MODE_STACK_IND; + } + + + // validate that saved registers are all within 6 slots abutting return address + int registers[6]; + for (int i=0; i < 6;++i) + registers[i] = 0; + if ( ebxSaved ) { + if ( cfaOffsetEBX < -28 ) { + strcpy(warningBuffer, "ebx is saved too far from return address"); + return UNWIND_X86_MODE_DWARF; + } + registers[(cfaOffsetEBX+28)/4] = UNWIND_X86_REG_EBX; + } + if ( ecxSaved ) { + if ( cfaOffsetECX < -28 ) { + strcpy(warningBuffer, "ecx is saved too far from return address"); + return UNWIND_X86_MODE_DWARF; + } + registers[(cfaOffsetECX+28)/4] = UNWIND_X86_REG_ECX; + } + if ( edxSaved ) { + if ( cfaOffsetEDX < -28 ) { + strcpy(warningBuffer, "edx is saved too far from return address"); + return UNWIND_X86_MODE_DWARF; + } + registers[(cfaOffsetEDX+28)/4] = UNWIND_X86_REG_EDX; + } + if ( ediSaved ) { + if ( cfaOffsetEDI < -28 ) { + strcpy(warningBuffer, "edi is saved too far from return address"); + return UNWIND_X86_MODE_DWARF; + } + registers[(cfaOffsetEDI+28)/4] = UNWIND_X86_REG_EDI; + } + if ( esiSaved ) { + if ( cfaOffsetESI < -28 ) { + strcpy(warningBuffer, "esi is saved too far from return address"); + return UNWIND_X86_MODE_DWARF; + } + registers[(cfaOffsetESI+28)/4] = UNWIND_X86_REG_ESI; + } + if ( ebpSaved ) { + if ( cfaOffsetEBP < -28 ) { + strcpy(warningBuffer, "ebp is saved too far from return address"); + return UNWIND_X86_MODE_DWARF; + } + registers[(cfaOffsetEBP+28)/4] = UNWIND_X86_REG_EBP; + } + + // validate that saved registers are contiguous and abut return address on stack + for (int i=0; i < saveRegisterCount; ++i) { + if ( registers[5-i] == 0 ) { + strcpy(warningBuffer, "registers not save contiguously in stack"); + return UNWIND_X86_MODE_DWARF; + } + } + + // encode register permutation + // the 10-bits are encoded differently depending on the number of registers saved + int renumregs[6]; + for (int i=6-saveRegisterCount; i < 6; ++i) { + int countless = 0; + for (int j=6-saveRegisterCount; j < i; ++j) { + if ( registers[j] < registers[i] ) + ++countless; + } + renumregs[i] = registers[i] - countless -1; + } + uint32_t permutationEncoding = 0; + switch ( saveRegisterCount ) { + case 6: + permutationEncoding |= (120*renumregs[0] + 24*renumregs[1] + 6*renumregs[2] + 2*renumregs[3] + renumregs[4]); + break; + case 5: + permutationEncoding |= (120*renumregs[1] + 24*renumregs[2] + 6*renumregs[3] + 2*renumregs[4] + renumregs[5]); + break; + case 4: + permutationEncoding |= (60*renumregs[2] + 12*renumregs[3] + 3*renumregs[4] + renumregs[5]); + break; + case 3: + permutationEncoding |= (20*renumregs[3] + 4*renumregs[4] + renumregs[5]); + break; + case 2: + permutationEncoding |= (5*renumregs[4] + renumregs[5]); + break; + case 1: + permutationEncoding |= (renumregs[5]); + break; + } + + encoding |= (stackValue << __builtin_ctz(UNWIND_X86_FRAMELESS_STACK_SIZE)); + encoding |= (stackAdjust << __builtin_ctz(UNWIND_X86_FRAMELESS_STACK_ADJUST)); + encoding |= (saveRegisterCount << __builtin_ctz(UNWIND_X86_FRAMELESS_STACK_REG_COUNT)); + encoding |= (permutationEncoding << __builtin_ctz(UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION)); + return encoding; + } +} + + + + + + + +// +// ppc specific functions +// +template +int DwarfInstructions::lastRestoreReg(const Registers_ppc&) +{ + COMPILE_TIME_ASSERT( (int)CFI_Parser::kMaxRegisterNumber > (int)UNW_PPC_SPEFSCR ); + return UNW_PPC_SPEFSCR; +} + +template +bool DwarfInstructions::isReturnAddressRegister(int regNum, const Registers_ppc&) +{ + return (regNum == UNW_PPC_LR); +} + +template +typename A::pint_t DwarfInstructions::getCFA(A& addressSpace, const typename CFI_Parser::PrologInfo& prolog, + const Registers_ppc& registers) +{ + if ( prolog.cfaRegister != 0 ) + return registers.getRegister(prolog.cfaRegister) + prolog.cfaRegisterOffset; + else if ( prolog.cfaExpression != 0 ) + return evaluateExpression(prolog.cfaExpression, addressSpace, registers, 0); + else + ABORT("getCFA(): unknown location for ppc cfa"); +} + + +template +compact_unwind_encoding_t DwarfInstructions::encodeToUseDwarf(const Registers_ppc&) +{ + return UNWIND_X86_MODE_DWARF; +} + + +template +compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_ppc& r, const typename CFI_Parser::PrologInfo& prolog, + char warningBuffer[1024]) +{ + warningBuffer[0] = '\0'; + return UNWIND_X86_MODE_DWARF; +} + + + + +} // namespace libunwind + + +#endif // __DWARF_INSTRUCTIONS_HPP__ + + + + diff --git a/src/ld/parsers/libunwind/DwarfParser.hpp b/src/ld/parsers/libunwind/DwarfParser.hpp new file mode 100644 index 0000000..3824d2e --- /dev/null +++ b/src/ld/parsers/libunwind/DwarfParser.hpp @@ -0,0 +1,819 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +// +// processor specific parsing of dwarf unwind instructions +// + +#ifndef __DWARF_PARSER_HPP__ +#define __DWARF_PARSER_HPP__ + +#include +#include +#include + +#include + +#include "libunwind.h" +#include "dwarf2.h" + +#include "AddressSpace.hpp" + + +namespace libunwind { + + +/// +/// CFI_Parser does basic parsing of a CFI (Call Frame Information) records. +/// See Dwarf Spec for details: +/// http://www.linux-foundation.org/spec/booksets/LSB-Core-generic/LSB-Core-generic/ehframechpt.html +/// +template +class CFI_Parser +{ +public: + typedef typename A::pint_t pint_t; + + /// + /// Information encoded in a CIE (Common Information Entry) + /// + struct CIE_Info { + pint_t cieStart; + pint_t cieLength; + pint_t cieInstructions; + uint8_t pointerEncoding; + uint8_t lsdaEncoding; + uint8_t personalityEncoding; + uint8_t personalityOffsetInCIE; + pint_t personality; + int codeAlignFactor; + int dataAlignFactor; + bool isSignalFrame; + bool fdesHaveAugmentationData; + }; + + /// + /// Information about an FDE (Frame Description Entry) + /// + struct FDE_Info { + pint_t fdeStart; + pint_t fdeLength; + pint_t fdeInstructions; + pint_t pcStart; + pint_t pcEnd; + pint_t lsda; + }; + + /// + /// Used by linker when parsing __eh_frame section + /// + struct FDE_Reference { + pint_t address; + uint32_t offsetInFDE; + uint8_t encodingOfAddress; + }; + struct FDE_Atom_Info { + pint_t fdeAddress; + FDE_Reference function; + FDE_Reference cie; + FDE_Reference lsda; + }; + struct CIE_Atom_Info { + pint_t cieAddress; + FDE_Reference personality; + }; + + + /// + /// Information about a frame layout and registers saved determined + /// by "running" the dwarf FDE "instructions" + /// + enum { kMaxRegisterNumber = 120 }; + enum RegisterSavedWhere { kRegisterUnused, kRegisterInCFA, kRegisterOffsetFromCFA, + kRegisterInRegister, kRegisterAtExpression, kRegisterIsExpression } ; + struct RegisterLocation { + RegisterSavedWhere location; + int64_t value; + }; + struct PrologInfo { + uint32_t cfaRegister; + int32_t cfaRegisterOffset; // CFA = (cfaRegister)+cfaRegisterOffset + int64_t cfaExpression; // CFA = expression + uint32_t spExtraArgSize; + uint32_t codeOffsetAtStackDecrement; + uint8_t registerSavedTwiceInCIE; + bool registersInOtherRegisters; + bool registerSavedMoreThanOnce; + bool cfaOffsetWasNegative; + bool sameValueUsed; + RegisterLocation savedRegisters[kMaxRegisterNumber]; // from where to restore registers + }; + + struct PrologInfoStackEntry { + PrologInfoStackEntry(PrologInfoStackEntry* n, const PrologInfo& i) + : next(n), info(i) {} + PrologInfoStackEntry* next; + PrologInfo info; + }; + + static bool findFDE(A& addressSpace, pint_t pc, pint_t ehSectionStart, uint32_t sectionLength, pint_t fdeHint, FDE_Info* fdeInfo, CIE_Info* cieInfo); + static const char* decodeFDE(A& addressSpace, pint_t fdeStart, FDE_Info* fdeInfo, CIE_Info* cieInfo); + static bool parseFDEInstructions(A& addressSpace, const FDE_Info& fdeInfo, const CIE_Info& cieInfo, pint_t upToPC, PrologInfo* results); + static const char* getCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength, + std::vector& fdes, std::vector& cies); + static uint32_t getCFICount(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength); + + static const char* parseCIE(A& addressSpace, pint_t cie, CIE_Info* cieInfo); + +private: + static bool parseInstructions(A& addressSpace, pint_t instructions, pint_t instructionsEnd, const CIE_Info& cieInfo, + pint_t pcoffset, PrologInfoStackEntry*& rememberStack, PrologInfo* results); +}; + + +/// +/// Parse a FDE into a CIE_Info and an FDE_Info +/// +template +const char* CFI_Parser::decodeFDE(A& addressSpace, pint_t fdeStart, FDE_Info* fdeInfo, CIE_Info* cieInfo) +{ + pint_t p = fdeStart; + uint64_t cfiLength = addressSpace.get32(p); + p += 4; + if ( cfiLength == 0xffffffff ) { + // 0xffffffff means length is really next 8 bytes + cfiLength = addressSpace.get64(p); + p += 8; + } + if ( cfiLength == 0 ) + return "FDE has zero length"; // end marker + uint32_t ciePointer = addressSpace.get32(p); + if ( ciePointer == 0 ) + return "FDE is really a CIE"; // this is a CIE not an FDE + pint_t nextCFI = p + cfiLength; + pint_t cieStart = p-ciePointer; + const char* err = parseCIE(addressSpace, cieStart, cieInfo); + if (err != NULL) + return err; + p += 4; + // parse pc begin and range + pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding); + pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding & 0x0F); + // parse rest of info + fdeInfo->lsda = 0; + // check for augmentation length + if ( cieInfo->fdesHaveAugmentationData ) { + uintptr_t augLen = addressSpace.getULEB128(p, nextCFI); + pint_t endOfAug = p + augLen; + if ( cieInfo->lsdaEncoding != 0 ) { + // peek at value (without indirection). Zero means no lsda + pint_t lsdaStart = p; + if ( addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding & 0x0F) != 0 ) { + // reset pointer and re-parse lsda address + p = lsdaStart; + fdeInfo->lsda = addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding); + } + } + p = endOfAug; + } + fdeInfo->fdeStart = fdeStart; + fdeInfo->fdeLength = nextCFI - fdeStart; + fdeInfo->fdeInstructions = p; + fdeInfo->pcStart = pcStart; + fdeInfo->pcEnd = pcStart+pcRange; + return NULL; // success +} + + +/// +/// Scan an eh_frame section to find an FDE for a pc +/// +template +bool CFI_Parser::findFDE(A& addressSpace, pint_t pc, pint_t ehSectionStart, uint32_t sectionLength, pint_t fdeHint, FDE_Info* fdeInfo, CIE_Info* cieInfo) +{ + //fprintf(stderr, "findFDE(0x%llX)\n", (long long)pc); + pint_t p = (fdeHint != 0) ? fdeHint : ehSectionStart; + const pint_t ehSectionEnd = p + sectionLength; + while ( p < ehSectionEnd ) { + pint_t currentCFI = p; + //fprintf(stderr, "findFDE() CFI at 0x%llX\n", (long long)p); + uint64_t cfiLength = addressSpace.get32(p); + p += 4; + if ( cfiLength == 0xffffffff ) { + // 0xffffffff means length is really next 8 bytes + cfiLength = addressSpace.get64(p); + p += 8; + } + if ( cfiLength == 0 ) + return false; // end marker + uint32_t id = addressSpace.get32(p); + if ( id == 0 ) { + // skip over CIEs + p += cfiLength; + } + else { + // process FDE to see if it covers pc + pint_t nextCFI = p + cfiLength; + uint32_t ciePointer = addressSpace.get32(p); + pint_t cieStart = p-ciePointer; + // validate pointer to CIE is within section + if ( (ehSectionStart <= cieStart) && (cieStart < ehSectionEnd) ) { + if ( parseCIE(addressSpace, cieStart, cieInfo) == NULL ) { + p += 4; + // parse pc begin and range + pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding); + pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding & 0x0F); + //fprintf(stderr, "FDE with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pcStart, (uint64_t)(pcStart+pcRange)); + // test if pc is within the function this FDE covers + if ( (pcStart < pc) && (pc <= pcStart+pcRange) ) { + // parse rest of info + fdeInfo->lsda = 0; + // check for augmentation length + if ( cieInfo->fdesHaveAugmentationData ) { + uintptr_t augLen = addressSpace.getULEB128(p, nextCFI); + pint_t endOfAug = p + augLen; + if ( cieInfo->lsdaEncoding != 0 ) { + // peek at value (without indirection). Zero means no lsda + pint_t lsdaStart = p; + if ( addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding & 0x0F) != 0 ) { + // reset pointer and re-parse lsda address + p = lsdaStart; + fdeInfo->lsda = addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding); + } + } + p = endOfAug; + } + fdeInfo->fdeStart = currentCFI; + fdeInfo->fdeLength = nextCFI - currentCFI; + fdeInfo->fdeInstructions = p; + fdeInfo->pcStart = pcStart; + fdeInfo->pcEnd = pcStart+pcRange; + //fprintf(stderr, "findFDE(pc=0x%llX) found with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pc, (uint64_t)pcStart, (uint64_t)(pcStart+pcRange)); + return true; + } + else { + //fprintf(stderr, "findFDE(pc=0x%llX) not found with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pc, (uint64_t)pcStart, (uint64_t)(pcStart+pcRange)); + // pc is not in begin/range, skip this FDE + } + } + else { + // malformed CIE, now augmentation describing pc range encoding + //fprintf(stderr, "malformed CIE\n"); + } + } + else { + // malformed FDE. CIE is bad + //fprintf(stderr, "malformed FDE, cieStart=0x%llX, ehSectionStart=0x%llX, ehSectionEnd=0x%llX\n", + // (uint64_t)cieStart, (uint64_t)ehSectionStart, (uint64_t)ehSectionEnd); + } + p = nextCFI; + } + } + //fprintf(stderr, "findFDE(pc=0x%llX) not found\n",(uint64_t)pc); + return false; +} + + + +/// +/// Extract info from a CIE +/// +template +const char* CFI_Parser::parseCIE(A& addressSpace, pint_t cie, CIE_Info* cieInfo) +{ + //fprintf(stderr, "parseCIE(0x%llX)\n", (long long)cie); + cieInfo->pointerEncoding = 0; + cieInfo->lsdaEncoding = 0; + cieInfo->personalityEncoding = 0; + cieInfo->personalityOffsetInCIE = 0; + cieInfo->personality = 0; + cieInfo->codeAlignFactor = 0; + cieInfo->dataAlignFactor = 0; + cieInfo->isSignalFrame = false; + cieInfo->fdesHaveAugmentationData = false; + cieInfo->cieStart = cie; + pint_t p = cie; + uint64_t cieLength = addressSpace.get32(p); + p += 4; + pint_t cieContentEnd = p + cieLength; + if ( cieLength == 0xffffffff ) { + // 0xffffffff means length is really next 8 bytes + cieLength = addressSpace.get64(p); + p += 8; + cieContentEnd = p + cieLength; + } + if ( cieLength == 0 ) + return NULL; + // CIE ID is always 0 + if ( addressSpace.get32(p) != 0 ) + return "CIE ID is not zero"; + p += 4; + // Version is always 1 or 3 + uint8_t version = addressSpace.get8(p); + if ( (version != 1) && (version != 3) ) + return "CIE version is not 1 or 3"; + ++p; + // save start of augmentation string and find end + pint_t strStart = p; + while ( addressSpace.get8(p) != 0 ) + ++p; + ++p; + // parse code aligment factor + cieInfo->codeAlignFactor = addressSpace.getULEB128(p, cieContentEnd); + // parse data alignment factor + cieInfo->dataAlignFactor = addressSpace.getSLEB128(p, cieContentEnd); + // parse return address register + addressSpace.getULEB128(p, cieContentEnd); + // parse augmentation data based on augmentation string + const char* result = NULL; + if ( addressSpace.get8(strStart) == 'z' ) { + // parse augmentation data length + addressSpace.getULEB128(p, cieContentEnd); + for (pint_t s=strStart; addressSpace.get8(s) != '\0'; ++s) { + switch ( addressSpace.get8(s) ) { + case 'z': + cieInfo->fdesHaveAugmentationData = true; + break; + case 'P': + cieInfo->personalityEncoding = addressSpace.get8(p); + ++p; + cieInfo->personalityOffsetInCIE = p-cie; + cieInfo->personality = addressSpace.getEncodedP(p, cieContentEnd, cieInfo->personalityEncoding); + break; + case 'L': + cieInfo->lsdaEncoding = addressSpace.get8(p); + ++p; + break; + case 'R': + cieInfo->pointerEncoding = addressSpace.get8(p); + ++p; + break; + case 'S': + cieInfo->isSignalFrame = true; + break; + default: + // ignore unknown letters + break; + } + } + } + cieInfo->cieLength = cieContentEnd - cieInfo->cieStart; + cieInfo->cieInstructions = p; + return result; +} + + +template +uint32_t CFI_Parser::getCFICount(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength) +{ + uint32_t count = 0; + const pint_t ehSectionEnd = ehSectionStart + sectionLength; + for (pint_t p=ehSectionStart; p < ehSectionEnd; ) { + uint64_t cfiLength = addressSpace.get32(p); + p += 4; + if ( cfiLength == 0xffffffff ) { + // 0xffffffff means length is really next 8 bytes + cfiLength = addressSpace.get64(p); + p += 8; + } + if ( cfiLength == 0 ) + return count; // end marker + ++count; + p += cfiLength; + } + return count; +} + + + +template +const char* CFI_Parser::getCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength, + std::vector& fdes, std::vector& cies) +{ + const pint_t ehSectionEnd = ehSectionStart + sectionLength; + for (pint_t p=ehSectionStart; p < ehSectionEnd; ) { + pint_t currentCFI = p; + uint64_t cfiLength = addressSpace.get32(p); + p += 4; + if ( cfiLength == 0xffffffff ) { + // 0xffffffff means length is really next 8 bytes + cfiLength = addressSpace.get64(p); + p += 8; + } + if ( cfiLength == 0 ) + return NULL; // end marker + uint32_t id = addressSpace.get32(p); + if ( id == 0 ) { + // is CIE + CIE_Info cieInfo; + const char* err = parseCIE(addressSpace, currentCFI, &cieInfo); + if ( err != NULL ) + return err; + CIE_Atom_Info entry; + entry.cieAddress = currentCFI; + entry.personality.address = cieInfo.personality; + entry.personality.offsetInFDE = cieInfo.personalityOffsetInCIE; + entry.personality.encodingOfAddress = cieInfo.personalityEncoding; + cies.push_back(entry); + p += cfiLength; + } + else { + // is FDE + FDE_Atom_Info entry; + entry.fdeAddress = currentCFI; + entry.function.address = 0; + entry.cie.address = 0; + entry.lsda.address = 0; + pint_t nextCFI = p + cfiLength; + uint32_t ciePointer = addressSpace.get32(p); + pint_t cieStart = p-ciePointer; + // validate pointer to CIE is within section + if ( (cieStart < ehSectionStart) || (cieStart > ehSectionEnd) ) + return "FDE points to CIE outside __eh_frame section"; + CIE_Info cieInfo; + const char* err = parseCIE(addressSpace, cieStart, &cieInfo); + if ( err != NULL ) + return err; + entry.cie.address = cieStart; + entry.cie.offsetInFDE = p-currentCFI; + entry.cie.encodingOfAddress = DW_EH_PE_sdata4 | DW_EH_PE_pcrel; + p += 4; + // parse pc begin and range + pint_t offsetOfFunctionAddress = p-currentCFI; + pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding); + pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding & 0x0F); + //fprintf(stderr, "FDE with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pcStart, (uint64_t)(pcStart+pcRange)); + // test if pc is within the function this FDE covers + entry.function.address = pcStart; + entry.function.offsetInFDE = offsetOfFunctionAddress; + entry.function.encodingOfAddress = cieInfo.pointerEncoding; + // skip over augmentation length + if ( cieInfo.fdesHaveAugmentationData ) { + uintptr_t augLen = addressSpace.getULEB128(p, nextCFI); + pint_t endOfAug = p + augLen; + if ( (cieInfo.lsdaEncoding != 0) && (addressSpace.getP(p) != 0) ) { + pint_t offsetOfLSDAAddress = p-currentCFI; + entry.lsda.address = addressSpace.getEncodedP(p, nextCFI, cieInfo.lsdaEncoding); + entry.lsda.offsetInFDE = offsetOfLSDAAddress; + entry.lsda.encodingOfAddress = cieInfo.lsdaEncoding; + } + p = endOfAug; + } + fdes.push_back(entry); + p = nextCFI; + } + } + return NULL; // success +} + + + +/// +/// "run" the dwarf instructions and create the abstact PrologInfo for an FDE +/// +template +bool CFI_Parser::parseFDEInstructions(A& addressSpace, const FDE_Info& fdeInfo, const CIE_Info& cieInfo, pint_t upToPC, PrologInfo* results) +{ + // clear results + bzero(results, sizeof(PrologInfo)); + PrologInfoStackEntry* rememberStack = NULL; + + // parse CIE then FDE instructions + return parseInstructions(addressSpace, cieInfo.cieInstructions, cieInfo.cieStart+cieInfo.cieLength, + cieInfo, (pint_t)(-1), rememberStack, results) + && parseInstructions(addressSpace, fdeInfo.fdeInstructions, fdeInfo.fdeStart+fdeInfo.fdeLength, + cieInfo, upToPC-fdeInfo.pcStart, rememberStack, results); +} + + +/// +/// "run" the dwarf instructions +/// +template +bool CFI_Parser::parseInstructions(A& addressSpace, pint_t instructions, pint_t instructionsEnd, const CIE_Info& cieInfo, + pint_t pcoffset, PrologInfoStackEntry*& rememberStack, PrologInfo* results) +{ + const bool logDwarf = false; + pint_t p = instructions; + uint32_t codeOffset = 0; + PrologInfo initialState = *results; + if ( logDwarf ) fprintf(stderr, "parseInstructions(instructions=0x%0llX)\n", (uint64_t)instructionsEnd); + + // see Dwarf Spec, section 6.4.2 for details on unwind opcodes + while ( (p < instructionsEnd) && (codeOffset < pcoffset) ) { + uint64_t reg; + uint64_t reg2; + int64_t offset; + uint64_t length; + uint8_t opcode = addressSpace.get8(p); + uint8_t operand; + PrologInfoStackEntry* entry; + ++p; + switch (opcode) { + case DW_CFA_nop: + if ( logDwarf ) fprintf(stderr, "DW_CFA_nop\n"); + break; + case DW_CFA_set_loc: + codeOffset = addressSpace.getEncodedP(p, instructionsEnd, cieInfo.pointerEncoding); + if ( logDwarf ) fprintf(stderr, "DW_CFA_set_loc\n"); + break; + case DW_CFA_advance_loc1: + codeOffset += (addressSpace.get8(p) * cieInfo.codeAlignFactor); + p += 1; + if ( logDwarf ) fprintf(stderr, "DW_CFA_advance_loc1: new offset=%u\n", codeOffset); + break; + case DW_CFA_advance_loc2: + codeOffset += (addressSpace.get16(p) * cieInfo.codeAlignFactor); + p += 2; + if ( logDwarf ) fprintf(stderr, "DW_CFA_advance_loc2: new offset=%u\n", codeOffset); + break; + case DW_CFA_advance_loc4: + codeOffset += (addressSpace.get32(p) * cieInfo.codeAlignFactor); + p += 4; + if ( logDwarf ) fprintf(stderr, "DW_CFA_advance_loc4: new offset=%u\n", codeOffset); + break; + case DW_CFA_offset_extended: + reg = addressSpace.getULEB128(p, instructionsEnd); + offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_offset_extended dwarf unwind, reg too big\n"); + return false; + } + if ( results->savedRegisters[reg].location != kRegisterUnused ) + results->registerSavedMoreThanOnce = true; + results->savedRegisters[reg].location = kRegisterInCFA; + results->savedRegisters[reg].value = offset; + if ( logDwarf ) fprintf(stderr, "DW_CFA_offset_extended(reg=%lld, offset=%lld)\n", reg, offset); + break; + case DW_CFA_restore_extended: + reg = addressSpace.getULEB128(p, instructionsEnd);; + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_restore_extended dwarf unwind, reg too big\n"); + return false; + } + results->savedRegisters[reg] = initialState.savedRegisters[reg]; + if ( logDwarf ) fprintf(stderr, "DW_CFA_restore_extended(reg=%lld)\n", reg); + break; + case DW_CFA_undefined: + reg = addressSpace.getULEB128(p, instructionsEnd); + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_undefined dwarf unwind, reg too big\n"); + return false; + } + results->savedRegisters[reg].location = kRegisterUnused; + if ( logDwarf ) fprintf(stderr, "DW_CFA_undefined(reg=%lld)\n", reg); + break; + case DW_CFA_same_value: + reg = addressSpace.getULEB128(p, instructionsEnd); + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_same_value dwarf unwind, reg too big\n"); + return false; + } + // DW_CFA_same_value unsupported + // "same value" means register was stored in frame, but its current + // value has not changed, so no need to restore from frame. + // We model this as if the register was never saved. + results->savedRegisters[reg].location = kRegisterUnused; + // set flag to disable conversion to compact unwind + results->sameValueUsed = true; + if ( logDwarf ) fprintf(stderr, "DW_CFA_same_value(reg=%lld)\n", reg); + break; + case DW_CFA_register: + reg = addressSpace.getULEB128(p, instructionsEnd); + reg2 = addressSpace.getULEB128(p, instructionsEnd); + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_register dwarf unwind, reg too big\n"); + return false; + } + if ( reg2 > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_register dwarf unwind, reg2 too big\n"); + return false; + } + results->savedRegisters[reg].location = kRegisterInRegister; + results->savedRegisters[reg].value = reg2; + // set flag to disable conversion to compact unwind + results->registersInOtherRegisters = true; + if ( logDwarf ) fprintf(stderr, "DW_CFA_register(reg=%lld, reg2=%lld)\n", reg, reg2); + break; + case DW_CFA_remember_state: + // avoid operator new, because that would be an upward dependency + entry = (PrologInfoStackEntry*)malloc(sizeof(PrologInfoStackEntry)); + if ( entry != NULL ) { + entry->next = rememberStack; + entry->info = *results; + rememberStack = entry; + } + else { + return false; + } + if ( logDwarf ) fprintf(stderr, "DW_CFA_remember_state\n"); + break; + case DW_CFA_restore_state: + if ( rememberStack != NULL ) { + PrologInfoStackEntry* top = rememberStack; + *results = top->info; + rememberStack = top->next; + free((char*)top); + } + else { + return false; + } + if ( logDwarf ) fprintf(stderr, "DW_CFA_restore_state\n"); + break; + case DW_CFA_def_cfa: + reg = addressSpace.getULEB128(p, instructionsEnd); + offset = addressSpace.getULEB128(p, instructionsEnd); + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_def_cfa dwarf unwind, reg too big\n"); + return false; + } + results->cfaRegister = reg; + results->cfaRegisterOffset = offset; + if ( offset > 0x80000000 ) + results->cfaOffsetWasNegative = true; + if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa(reg=%lld, offset=%lld)\n", reg, offset); + break; + case DW_CFA_def_cfa_register: + reg = addressSpace.getULEB128(p, instructionsEnd); + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_def_cfa_register dwarf unwind, reg too big\n"); + return false; + } + results->cfaRegister = reg; + if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_register(%lld)\n", reg); + break; + case DW_CFA_def_cfa_offset: + results->cfaRegisterOffset = addressSpace.getULEB128(p, instructionsEnd); + results->codeOffsetAtStackDecrement = codeOffset; + if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_offset(%d)\n", results->cfaRegisterOffset); + break; + case DW_CFA_def_cfa_expression: + results->cfaRegister = 0; + results->cfaExpression = p; + length = addressSpace.getULEB128(p, instructionsEnd); + p += length; + if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_expression(expression=0x%llX, length=%llu)\n", + results->cfaExpression, length); + break; + case DW_CFA_expression: + reg = addressSpace.getULEB128(p, instructionsEnd); + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_expression dwarf unwind, reg too big\n"); + return false; + } + results->savedRegisters[reg].location = kRegisterAtExpression; + results->savedRegisters[reg].value = p; + length = addressSpace.getULEB128(p, instructionsEnd); + p += length; + if ( logDwarf ) fprintf(stderr, "DW_CFA_expression(reg=%lld, expression=0x%llX, length=%llu)\n", + reg, results->savedRegisters[reg].value, length); + break; + case DW_CFA_offset_extended_sf: + reg = addressSpace.getULEB128(p, instructionsEnd); + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_offset_extended_sf dwarf unwind, reg too big\n"); + return false; + } + offset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + if ( results->savedRegisters[reg].location != kRegisterUnused ) + results->registerSavedMoreThanOnce = true; + results->savedRegisters[reg].location = kRegisterInCFA; + results->savedRegisters[reg].value = offset; + if ( logDwarf ) fprintf(stderr, "DW_CFA_offset_extended_sf(reg=%lld, offset=%lld)\n", reg, offset); + break; + case DW_CFA_def_cfa_sf: + reg = addressSpace.getULEB128(p, instructionsEnd); + offset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_def_cfa_sf dwarf unwind, reg too big\n"); + return false; + } + results->cfaRegister = reg; + results->cfaRegisterOffset = offset; + if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_sf(reg=%lld, offset=%lld)\n", reg, offset); + break; + case DW_CFA_def_cfa_offset_sf: + results->cfaRegisterOffset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + results->codeOffsetAtStackDecrement = codeOffset; + if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_offset_sf(%d)\n", results->cfaRegisterOffset); + break; + case DW_CFA_val_offset: + reg = addressSpace.getULEB128(p, instructionsEnd); + offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + results->savedRegisters[reg].location = kRegisterOffsetFromCFA; + results->savedRegisters[reg].value = offset; + if ( logDwarf ) fprintf(stderr, "DW_CFA_val_offset(reg=%lld, offset=%lld\n", reg, offset); + break; + case DW_CFA_val_offset_sf: + reg = addressSpace.getULEB128(p, instructionsEnd); + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_val_offset_sf dwarf unwind, reg too big\n"); + return false; + } + offset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + results->savedRegisters[reg].location = kRegisterOffsetFromCFA; + results->savedRegisters[reg].value = offset; + if ( logDwarf ) fprintf(stderr, "DW_CFA_val_offset_sf(reg=%lld, offset=%lld\n", reg, offset); + break; + case DW_CFA_val_expression: + reg = addressSpace.getULEB128(p, instructionsEnd); + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_val_expression dwarf unwind, reg too big\n"); + return false; + } + results->savedRegisters[reg].location = kRegisterIsExpression; + results->savedRegisters[reg].value = p; + length = addressSpace.getULEB128(p, instructionsEnd); + p += length; + if ( logDwarf ) fprintf(stderr, "DW_CFA_val_expression(reg=%lld, expression=0x%llX, length=%lld)\n", + reg, results->savedRegisters[reg].value, length); + break; + case DW_CFA_GNU_args_size: + offset = addressSpace.getULEB128(p, instructionsEnd); + results->spExtraArgSize = offset; + if ( logDwarf ) fprintf(stderr, "DW_CFA_GNU_args_size(%lld)\n", offset); + break; + case DW_CFA_GNU_negative_offset_extended: + reg = addressSpace.getULEB128(p, instructionsEnd); + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_GNU_negative_offset_extended dwarf unwind, reg too big\n"); + return false; + } + offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + if ( results->savedRegisters[reg].location != kRegisterUnused ) + results->registerSavedMoreThanOnce = true; + results->savedRegisters[reg].location = kRegisterInCFA; + results->savedRegisters[reg].value = -offset; + if ( logDwarf ) fprintf(stderr, "DW_CFA_GNU_negative_offset_extended(%lld)\n", offset); + break; + default: + operand = opcode & 0x3F; + switch ( opcode & 0xC0 ) { + case DW_CFA_offset: + reg = operand; + offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + if ( results->savedRegisters[reg].location != kRegisterUnused ) { + // look for idiom of PC saved twice in CIE to mean disable compact unwind encoding + if ( (pcoffset == (pint_t)(-1)) + && (results->savedRegisters[reg].location == kRegisterInCFA) + && (results->savedRegisters[reg].value == offset) ) + results->registerSavedTwiceInCIE = reg; + else + results->registerSavedMoreThanOnce = true; + } + results->savedRegisters[reg].location = kRegisterInCFA; + results->savedRegisters[reg].value = offset; + if ( logDwarf ) fprintf(stderr, "DW_CFA_offset(reg=%d, offset=%lld)\n", operand, offset); + break; + case DW_CFA_advance_loc: + codeOffset += operand * cieInfo.codeAlignFactor; + if ( logDwarf ) fprintf(stderr, "DW_CFA_advance_loc: new offset=%u\n", codeOffset); + break; + case DW_CFA_restore: + // Python crashes when handling an exception thrown by an obj-c object + // libffi uses DW_CFA_restore in the middle of some custom dwarf, so it is not a good epilog flag + //return true; // gcc-4.5 starts the epilog with this + reg = operand; + results->savedRegisters[reg] = initialState.savedRegisters[reg]; + if ( logDwarf ) fprintf(stderr, "DW_CFA_restore(reg=%lld)\n", reg); + break; + default: + if ( logDwarf ) fprintf(stderr, "unknown CFA opcode 0x%02X\n", opcode); + return false; + } + } + } + + return true; +} + + +} // namespace libunwind + + +#endif // __DWARF_PARSER_HPP__ + + + + diff --git a/src/ld/parsers/libunwind/InternalMacros.h b/src/ld/parsers/libunwind/InternalMacros.h new file mode 100644 index 0000000..25c1631 --- /dev/null +++ b/src/ld/parsers/libunwind/InternalMacros.h @@ -0,0 +1,105 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + + +#ifndef INTERNAL_MACROS_H +#define INTERNAL_MACROS_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + extern void __assert_rtn(const char *, const char *, int, const char *) __attribute__((noreturn)); +#ifdef __cplusplus +} +#endif + +#define UNW_STEP_SUCCESS 1 +#define UNW_STEP_END 0 + + +struct v128 { unsigned int vec[4]; }; + + +#define EXPORT __attribute__((visibility("default"))) + +#define COMPILE_TIME_ASSERT( expr ) \ + extern int compile_time_assert_failed[ ( expr ) ? 1 : -1 ] __attribute__( ( unused ) ); + +#define ABORT(msg) __assert_rtn(__func__, __FILE__, __LINE__, msg) + +#if NDEBUG + #define DEBUG_MESSAGE(msg, ...) + #define DEBUG_PRINT_API(msg, ...) + #define DEBUG_PRINT_UNWINDING_TEST 0 + #define DEBUG_PRINT_UNWINDING(msg, ...) + #define DEBUG_LOG_NON_ZERO(x) x; + #define INITIALIZE_DEBUG_PRINT_API + #define INITIALIZE_DEBUG_PRINT_UNWINDING +#else + #define DEBUG_MESSAGE(msg, ...) fprintf(stderr, "libuwind: " msg, __VA_ARGS__) + #ifdef __cplusplus + extern "C" { + #endif + extern bool logAPIs(); + extern bool logUnwinding(); + #ifdef __cplusplus + } + #endif + #define DEBUG_LOG_NON_ZERO(x) { int _err = x; if ( _err != 0 ) fprintf(stderr, "libuwind: " #x "=%d in %s", _err, __FUNCTION__); } + #define DEBUG_PRINT_API(msg, ...) do { if ( logAPIs() ) fprintf(stderr, msg, __VA_ARGS__); } while(0) + #define DEBUG_PRINT_UNWINDING(msg, ...) do { if ( logUnwinding() ) fprintf(stderr, msg, __VA_ARGS__); } while(0) + #define DEBUG_PRINT_UNWINDING_TEST logUnwinding() + #define INITIALIZE_DEBUG_PRINT_API bool logAPIs() { static bool log = (getenv("LIBUNWIND_PRINT_APIS") != NULL); return log; } + #define INITIALIZE_DEBUG_PRINT_UNWINDING bool logUnwinding() { static bool log = (getenv("LIBUNWIND_PRINT_UNWINDING") != NULL); return log; } +#endif + + +// note hack for +// Once libgcc_s.dylib vectors to libSystem, then we can remove the $ld$hide$os10.6$ lines +#if __ppc__ + #define NOT_HERE_BEFORE_10_6(sym) \ + extern const char sym##_tmp3 __asm("$ld$hide$os10.3$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp3 = 0; \ + extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \ + extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp5 = 0; + #define NEVER_HERE(sym) \ + extern const char sym##_tmp3 __asm("$ld$hide$os10.3$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp3 = 0; \ + extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \ + extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp5 = 0; \ + extern const char sym##_tmp6 __asm("$ld$hide$os10.6$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp6 = 0; +#else + #define NOT_HERE_BEFORE_10_6(sym) \ + extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \ + extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp5 = 0; + #define NEVER_HERE(sym) \ + extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \ + extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp5 = 0; \ + extern const char sym##_tmp6 __asm("$ld$hide$os10.6$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp6 = 0; +#endif + + + +#endif // INTERNAL_MACROS_H diff --git a/src/ld/parsers/libunwind/Registers.hpp b/src/ld/parsers/libunwind/Registers.hpp new file mode 100644 index 0000000..7d39fd7 --- /dev/null +++ b/src/ld/parsers/libunwind/Registers.hpp @@ -0,0 +1,1050 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +// +// C++ interface to lower levels of libuwind +// + +#ifndef __REGISTERS_HPP__ +#define __REGISTERS_HPP__ + +#include +#include +#include +#include +#include +#include +#include + +#include "libunwind.h" +#include "InternalMacros.h" + +namespace libunwind { + + +/// +/// Registers_x86 holds the register state of a thread in a 32-bit intel process. +/// +class Registers_x86 +{ +public: + Registers_x86(); + Registers_x86(const void* registers); + + bool validRegister(int num) const; + uint32_t getRegister(int num) const; + void setRegister(int num, uint32_t value); + bool validFloatRegister(int num) const { return false; } + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const { return false; } + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + const char* getRegisterName(int num); + void jumpto(); + + uint32_t getSP() const { return fRegisters.__esp; } + void setSP(uint32_t value) { fRegisters.__esp = value; } + uint32_t getIP() const { return fRegisters.__eip; } + void setIP(uint32_t value) { fRegisters.__eip = value; } + uint32_t getEBP() const { return fRegisters.__ebp; } + void setEBP(uint32_t value) { fRegisters.__ebp = value; } + uint32_t getEBX() const { return fRegisters.__ebx; } + void setEBX(uint32_t value) { fRegisters.__ebx = value; } + uint32_t getECX() const { return fRegisters.__ecx; } + void setECX(uint32_t value) { fRegisters.__ecx = value; } + uint32_t getEDX() const { return fRegisters.__edx; } + void setEDX(uint32_t value) { fRegisters.__edx = value; } + uint32_t getESI() const { return fRegisters.__esi; } + void setESI(uint32_t value) { fRegisters.__esi = value; } + uint32_t getEDI() const { return fRegisters.__edi; } + void setEDI(uint32_t value) { fRegisters.__edi = value; } + +private: + i386_thread_state_t fRegisters; +}; + +inline Registers_x86::Registers_x86(const void* registers) +{ + COMPILE_TIME_ASSERT( sizeof(Registers_x86) < sizeof(unw_context_t) ); + fRegisters = *((i386_thread_state_t*)registers); +} + +inline Registers_x86::Registers_x86() +{ + bzero(&fRegisters, sizeof(fRegisters)); +} + + +inline bool Registers_x86::validRegister(int regNum) const +{ + if ( regNum == UNW_REG_IP ) + return true; + if ( regNum == UNW_REG_SP ) + return true; + if ( regNum < 0 ) + return false; + if ( regNum > 7 ) + return false; + return true; +} + +inline uint32_t Registers_x86::getRegister(int regNum) const +{ + switch ( regNum ) { + case UNW_REG_IP: + return fRegisters.__eip; + case UNW_REG_SP: + return fRegisters.__esp; + case UNW_X86_EAX: + return fRegisters.__eax; + case UNW_X86_ECX: + return fRegisters.__ecx; + case UNW_X86_EDX: + return fRegisters.__edx; + case UNW_X86_EBX: + return fRegisters.__ebx; + case UNW_X86_EBP: + return fRegisters.__ebp; + case UNW_X86_ESP: + return fRegisters.__esp; + case UNW_X86_ESI: + return fRegisters.__esi; + case UNW_X86_EDI: + return fRegisters.__edi; + } + ABORT("unsupported x86 register"); +} + +inline void Registers_x86::setRegister(int regNum, uint32_t value) +{ + switch ( regNum ) { + case UNW_REG_IP: + fRegisters.__eip = value; + return; + case UNW_REG_SP: + fRegisters.__esp = value; + return; + case UNW_X86_EAX: + fRegisters.__eax = value; + return; + case UNW_X86_ECX: + fRegisters.__ecx = value; + return; + case UNW_X86_EDX: + fRegisters.__edx = value; + return; + case UNW_X86_EBX: + fRegisters.__ebx = value; + return; + case UNW_X86_EBP: + fRegisters.__ebp = value; + return; + case UNW_X86_ESP: + fRegisters.__esp = value; + return; + case UNW_X86_ESI: + fRegisters.__esi = value; + return; + case UNW_X86_EDI: + fRegisters.__edi = value; + return; + } + ABORT("unsupported x86 register"); +} + +inline const char* Registers_x86::getRegisterName(int regNum) +{ + switch ( regNum ) { + case UNW_REG_IP: + return "ip"; + case UNW_REG_SP: + return "esp"; + case UNW_X86_EAX: + return "eax"; + case UNW_X86_ECX: + return "ecx"; + case UNW_X86_EDX: + return "edx"; + case UNW_X86_EBX: + return "ebx"; + case UNW_X86_EBP: + return "ebp"; + case UNW_X86_ESP: + return "esp"; + case UNW_X86_ESI: + return "esi"; + case UNW_X86_EDI: + return "edi"; + default: + return "unknown register"; + } +} + +inline double Registers_x86::getFloatRegister(int num) const +{ + ABORT("no x86 float registers"); +} + +inline void Registers_x86::setFloatRegister(int num, double value) +{ + ABORT("no x86 float registers"); +} + +inline v128 Registers_x86::getVectorRegister(int num) const +{ + ABORT("no x86 vector registers"); +} + +inline void Registers_x86::setVectorRegister(int num, v128 value) +{ + ABORT("no x86 vector registers"); +} + + + + +/// +/// Registers_x86_64 holds the register state of a thread in a 64-bit intel process. +/// +class Registers_x86_64 +{ +public: + Registers_x86_64(); + Registers_x86_64(const void* registers); + + bool validRegister(int num) const; + uint64_t getRegister(int num) const; + void setRegister(int num, uint64_t value); + bool validFloatRegister(int num) const{ return false; } + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const { return false; } + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + const char* getRegisterName(int num); + void jumpto(); + uint64_t getSP() const { return fRegisters.__rsp; } + void setSP(uint64_t value) { fRegisters.__rsp = value; } + uint64_t getIP() const { return fRegisters.__rip; } + void setIP(uint64_t value) { fRegisters.__rip = value; } + uint64_t getRBP() const { return fRegisters.__rbp; } + void setRBP(uint64_t value) { fRegisters.__rbp = value; } + uint64_t getRBX() const { return fRegisters.__rbx; } + void setRBX(uint64_t value) { fRegisters.__rbx = value; } + uint64_t getR12() const { return fRegisters.__r12; } + void setR12(uint64_t value) { fRegisters.__r12 = value; } + uint64_t getR13() const { return fRegisters.__r13; } + void setR13(uint64_t value) { fRegisters.__r13 = value; } + uint64_t getR14() const { return fRegisters.__r14; } + void setR14(uint64_t value) { fRegisters.__r14 = value; } + uint64_t getR15() const { return fRegisters.__r15; } + void setR15(uint64_t value) { fRegisters.__r15 = value; } +private: + x86_thread_state64_t fRegisters; +}; + +inline Registers_x86_64::Registers_x86_64(const void* registers) +{ + COMPILE_TIME_ASSERT( sizeof(Registers_x86_64) < sizeof(unw_context_t) ); + fRegisters = *((x86_thread_state64_t*)registers); +} + +inline Registers_x86_64::Registers_x86_64() +{ + bzero(&fRegisters, sizeof(fRegisters)); +} + + +inline bool Registers_x86_64::validRegister(int regNum) const +{ + if ( regNum == UNW_REG_IP ) + return true; + if ( regNum == UNW_REG_SP ) + return true; + if ( regNum < 0 ) + return false; + if ( regNum > 15 ) + return false; + return true; +} + +inline uint64_t Registers_x86_64::getRegister(int regNum) const +{ + switch ( regNum ) { + case UNW_REG_IP: + return fRegisters.__rip; + case UNW_REG_SP: + return fRegisters.__rsp; + case UNW_X86_64_RAX: + return fRegisters.__rax; + case UNW_X86_64_RDX: + return fRegisters.__rdx; + case UNW_X86_64_RCX: + return fRegisters.__rcx; + case UNW_X86_64_RBX: + return fRegisters.__rbx; + case UNW_X86_64_RSI: + return fRegisters.__rsi; + case UNW_X86_64_RDI: + return fRegisters.__rdi; + case UNW_X86_64_RBP: + return fRegisters.__rbp; + case UNW_X86_64_RSP: + return fRegisters.__rsp; + case UNW_X86_64_R8: + return fRegisters.__r8; + case UNW_X86_64_R9: + return fRegisters.__r9; + case UNW_X86_64_R10: + return fRegisters.__r10; + case UNW_X86_64_R11: + return fRegisters.__r11; + case UNW_X86_64_R12: + return fRegisters.__r12; + case UNW_X86_64_R13: + return fRegisters.__r13; + case UNW_X86_64_R14: + return fRegisters.__r14; + case UNW_X86_64_R15: + return fRegisters.__r15; + } + ABORT("unsupported x86_64 register"); +} + +inline void Registers_x86_64::setRegister(int regNum, uint64_t value) +{ + switch ( regNum ) { + case UNW_REG_IP: + fRegisters.__rip = value; + return; + case UNW_REG_SP: + fRegisters.__rsp = value; + return; + case UNW_X86_64_RAX: + fRegisters.__rax = value; + return; + case UNW_X86_64_RDX: + fRegisters.__rdx = value; + return; + case UNW_X86_64_RCX: + fRegisters.__rcx = value; + return; + case UNW_X86_64_RBX: + fRegisters.__rbx = value; + return; + case UNW_X86_64_RSI: + fRegisters.__rsi = value; + return; + case UNW_X86_64_RDI: + fRegisters.__rdi = value; + return; + case UNW_X86_64_RBP: + fRegisters.__rbp = value; + return; + case UNW_X86_64_RSP: + fRegisters.__rsp = value; + return; + case UNW_X86_64_R8: + fRegisters.__r8 = value; + return; + case UNW_X86_64_R9: + fRegisters.__r9 = value; + return; + case UNW_X86_64_R10: + fRegisters.__r10 = value; + return; + case UNW_X86_64_R11: + fRegisters.__r11 = value; + return; + case UNW_X86_64_R12: + fRegisters.__r12 = value; + return; + case UNW_X86_64_R13: + fRegisters.__r13 = value; + return; + case UNW_X86_64_R14: + fRegisters.__r14 = value; + return; + case UNW_X86_64_R15: + fRegisters.__r15 = value; + return; + } + ABORT("unsupported x86_64 register"); +} + +inline const char* Registers_x86_64::getRegisterName(int regNum) +{ + switch ( regNum ) { + case UNW_REG_IP: + return "rip"; + case UNW_REG_SP: + return "rsp"; + case UNW_X86_64_RAX: + return "rax"; + case UNW_X86_64_RDX: + return "rdx"; + case UNW_X86_64_RCX: + return "rcx"; + case UNW_X86_64_RBX: + return "rbx"; + case UNW_X86_64_RSI: + return "rsi"; + case UNW_X86_64_RDI: + return "rdi"; + case UNW_X86_64_RBP: + return "rbp"; + case UNW_X86_64_RSP: + return "rsp"; + case UNW_X86_64_R8: + return "r8"; + case UNW_X86_64_R9: + return "r9"; + case UNW_X86_64_R10: + return "r10"; + case UNW_X86_64_R11: + return "r11"; + case UNW_X86_64_R12: + return "r12"; + case UNW_X86_64_R13: + return "r13"; + case UNW_X86_64_R14: + return "r14"; + case UNW_X86_64_R15: + return "r15"; + default: + return "unknown register"; + } +} + +double Registers_x86_64::getFloatRegister(int num) const +{ + ABORT("no x86_64 float registers"); +} + +void Registers_x86_64::setFloatRegister(int num, double value) +{ + ABORT("no x86_64 float registers"); +} + +inline v128 Registers_x86_64::getVectorRegister(int num) const +{ + ABORT("no x86_64 vector registers"); +} + +inline void Registers_x86_64::setVectorRegister(int num, v128 value) +{ + ABORT("no x86_64 vector registers"); +} + + +/// +/// Registers_ppc holds the register state of a thread in a 32-bit PowerPC process. +/// +class Registers_ppc +{ +public: + Registers_ppc(); + Registers_ppc(const void* registers); + + bool validRegister(int num) const; + uint32_t getRegister(int num) const; + void setRegister(int num, uint32_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + void jumpto(); + const char* getRegisterName(int num); + uint64_t getSP() const { return fRegisters.__r1; } + void setSP(uint64_t value) { fRegisters.__r1 = value; } + uint64_t getIP() const { return fRegisters.__srr0; } + void setIP(uint64_t value) { fRegisters.__srr0 = value; } +private: + struct ppc_thread_state_t + { + unsigned int __srr0; /* Instruction address register (PC) */ + unsigned int __srr1; /* Machine state register (supervisor) */ + unsigned int __r0; + unsigned int __r1; + unsigned int __r2; + unsigned int __r3; + unsigned int __r4; + unsigned int __r5; + unsigned int __r6; + unsigned int __r7; + unsigned int __r8; + unsigned int __r9; + unsigned int __r10; + unsigned int __r11; + unsigned int __r12; + unsigned int __r13; + unsigned int __r14; + unsigned int __r15; + unsigned int __r16; + unsigned int __r17; + unsigned int __r18; + unsigned int __r19; + unsigned int __r20; + unsigned int __r21; + unsigned int __r22; + unsigned int __r23; + unsigned int __r24; + unsigned int __r25; + unsigned int __r26; + unsigned int __r27; + unsigned int __r28; + unsigned int __r29; + unsigned int __r30; + unsigned int __r31; + unsigned int __cr; /* Condition register */ + unsigned int __xer; /* User's integer exception register */ + unsigned int __lr; /* Link register */ + unsigned int __ctr; /* Count register */ + unsigned int __mq; /* MQ register (601 only) */ + unsigned int __vrsave; /* Vector Save Register */ + }; + + struct ppc_float_state_t + { + double __fpregs[32]; + + unsigned int __fpscr_pad; /* fpscr is 64 bits, 32 bits of rubbish */ + unsigned int __fpscr; /* floating point status register */ + }; + + ppc_thread_state_t fRegisters; + ppc_float_state_t fFloatRegisters; + v128 fVectorRegisters[32]; // offset 424 +}; + + + +inline Registers_ppc::Registers_ppc(const void* registers) +{ + COMPILE_TIME_ASSERT( sizeof(Registers_ppc) < sizeof(unw_context_t) ); + fRegisters = *((ppc_thread_state_t*)registers); + fFloatRegisters = *((ppc_float_state_t*)((char*)registers+160)); + memcpy(fVectorRegisters, ((char*)registers+424), sizeof(fVectorRegisters)); +} + +inline Registers_ppc::Registers_ppc() +{ + bzero(&fRegisters, sizeof(fRegisters)); + bzero(&fFloatRegisters, sizeof(fFloatRegisters)); + bzero(&fVectorRegisters, sizeof(fVectorRegisters)); +} + + +inline bool Registers_ppc::validRegister(int regNum) const +{ + if ( regNum == UNW_REG_IP ) + return true; + if ( regNum == UNW_REG_SP ) + return true; + if ( regNum == UNW_PPC_VRSAVE ) + return true; + if ( regNum < 0 ) + return false; + if ( regNum <= UNW_PPC_R31 ) + return true; + if ( regNum == UNW_PPC_MQ ) + return true; + if ( regNum == UNW_PPC_LR ) + return true; + if ( regNum == UNW_PPC_CTR ) + return true; + if ( (UNW_PPC_CR0 <= regNum) && (regNum <= UNW_PPC_CR7) ) + return true; + return false; +} + + +inline uint32_t Registers_ppc::getRegister(int regNum) const +{ + switch ( regNum ) { + case UNW_REG_IP: + return fRegisters.__srr0; + case UNW_REG_SP: + return fRegisters.__r1; + case UNW_PPC_R0: + return fRegisters.__r0; + case UNW_PPC_R1: + return fRegisters.__r1; + case UNW_PPC_R2: + return fRegisters.__r2; + case UNW_PPC_R3: + return fRegisters.__r3; + case UNW_PPC_R4: + return fRegisters.__r4; + case UNW_PPC_R5: + return fRegisters.__r5; + case UNW_PPC_R6: + return fRegisters.__r6; + case UNW_PPC_R7: + return fRegisters.__r7; + case UNW_PPC_R8: + return fRegisters.__r8; + case UNW_PPC_R9: + return fRegisters.__r9; + case UNW_PPC_R10: + return fRegisters.__r10; + case UNW_PPC_R11: + return fRegisters.__r11; + case UNW_PPC_R12: + return fRegisters.__r12; + case UNW_PPC_R13: + return fRegisters.__r13; + case UNW_PPC_R14: + return fRegisters.__r14; + case UNW_PPC_R15: + return fRegisters.__r15; + case UNW_PPC_R16: + return fRegisters.__r16; + case UNW_PPC_R17: + return fRegisters.__r17; + case UNW_PPC_R18: + return fRegisters.__r18; + case UNW_PPC_R19: + return fRegisters.__r19; + case UNW_PPC_R20: + return fRegisters.__r20; + case UNW_PPC_R21: + return fRegisters.__r21; + case UNW_PPC_R22: + return fRegisters.__r22; + case UNW_PPC_R23: + return fRegisters.__r23; + case UNW_PPC_R24: + return fRegisters.__r24; + case UNW_PPC_R25: + return fRegisters.__r25; + case UNW_PPC_R26: + return fRegisters.__r26; + case UNW_PPC_R27: + return fRegisters.__r27; + case UNW_PPC_R28: + return fRegisters.__r28; + case UNW_PPC_R29: + return fRegisters.__r29; + case UNW_PPC_R30: + return fRegisters.__r30; + case UNW_PPC_R31: + return fRegisters.__r31; + case UNW_PPC_LR: + return fRegisters.__lr; + case UNW_PPC_CR0: + return (fRegisters.__cr & 0xF0000000); + case UNW_PPC_CR1: + return (fRegisters.__cr & 0x0F000000); + case UNW_PPC_CR2: + return (fRegisters.__cr & 0x00F00000); + case UNW_PPC_CR3: + return (fRegisters.__cr & 0x000F0000); + case UNW_PPC_CR4: + return (fRegisters.__cr & 0x0000F000); + case UNW_PPC_CR5: + return (fRegisters.__cr & 0x00000F00); + case UNW_PPC_CR6: + return (fRegisters.__cr & 0x000000F0); + case UNW_PPC_CR7: + return (fRegisters.__cr & 0x0000000F); + case UNW_PPC_VRSAVE: + return fRegisters.__vrsave; + } + ABORT("unsupported ppc register"); +} + + +inline void Registers_ppc::setRegister(int regNum, uint32_t value) +{ + //fprintf(stderr, "Registers_ppc::setRegister(%d, 0x%08X)\n", regNum, value); + switch ( regNum ) { + case UNW_REG_IP: + fRegisters.__srr0 = value; + return; + case UNW_REG_SP: + fRegisters.__r1 = value; + return; + case UNW_PPC_R0: + fRegisters.__r0 = value; + return; + case UNW_PPC_R1: + fRegisters.__r1 = value; + return; + case UNW_PPC_R2: + fRegisters.__r2 = value; + return; + case UNW_PPC_R3: + fRegisters.__r3 = value; + return; + case UNW_PPC_R4: + fRegisters.__r4 = value; + return; + case UNW_PPC_R5: + fRegisters.__r5 = value; + return; + case UNW_PPC_R6: + fRegisters.__r6 = value; + return; + case UNW_PPC_R7: + fRegisters.__r7 = value; + return; + case UNW_PPC_R8: + fRegisters.__r8 = value; + return; + case UNW_PPC_R9: + fRegisters.__r9 = value; + return; + case UNW_PPC_R10: + fRegisters.__r10 = value; + return; + case UNW_PPC_R11: + fRegisters.__r11 = value; + return; + case UNW_PPC_R12: + fRegisters.__r12 = value; + return; + case UNW_PPC_R13: + fRegisters.__r13 = value; + return; + case UNW_PPC_R14: + fRegisters.__r14 = value; + return; + case UNW_PPC_R15: + fRegisters.__r15 = value; + return; + case UNW_PPC_R16: + fRegisters.__r16 = value; + return; + case UNW_PPC_R17: + fRegisters.__r17 = value; + return; + case UNW_PPC_R18: + fRegisters.__r18 = value; + return; + case UNW_PPC_R19: + fRegisters.__r19 = value; + return; + case UNW_PPC_R20: + fRegisters.__r20 = value; + return; + case UNW_PPC_R21: + fRegisters.__r21 = value; + return; + case UNW_PPC_R22: + fRegisters.__r22 = value; + return; + case UNW_PPC_R23: + fRegisters.__r23 = value; + return; + case UNW_PPC_R24: + fRegisters.__r24 = value; + return; + case UNW_PPC_R25: + fRegisters.__r25 = value; + return; + case UNW_PPC_R26: + fRegisters.__r26 = value; + return; + case UNW_PPC_R27: + fRegisters.__r27 = value; + return; + case UNW_PPC_R28: + fRegisters.__r28 = value; + return; + case UNW_PPC_R29: + fRegisters.__r29 = value; + return; + case UNW_PPC_R30: + fRegisters.__r30 = value; + return; + case UNW_PPC_R31: + fRegisters.__r31 = value; + return; + case UNW_PPC_MQ: + fRegisters.__mq = value; + return; + case UNW_PPC_LR: + fRegisters.__lr = value; + return; + case UNW_PPC_CTR: + fRegisters.__ctr = value; + return; + case UNW_PPC_CR0: + fRegisters.__cr &= 0x0FFFFFFF; + fRegisters.__cr |= (value & 0xF0000000); + return; + case UNW_PPC_CR1: + fRegisters.__cr &= 0xF0FFFFFF; + fRegisters.__cr |= (value & 0x0F000000); + return; + case UNW_PPC_CR2: + fRegisters.__cr &= 0xFF0FFFFF; + fRegisters.__cr |= (value & 0x00F00000); + return; + case UNW_PPC_CR3: + fRegisters.__cr &= 0xFFF0FFFF; + fRegisters.__cr |= (value & 0x000F0000); + return; + case UNW_PPC_CR4: + fRegisters.__cr &= 0xFFFF0FFF; + fRegisters.__cr |= (value & 0x0000F000); + return; + case UNW_PPC_CR5: + fRegisters.__cr &= 0xFFFFF0FF; + fRegisters.__cr |= (value & 0x00000F00); + return; + case UNW_PPC_CR6: + fRegisters.__cr &= 0xFFFFFF0F; + fRegisters.__cr |= (value & 0x000000F0); + return; + case UNW_PPC_CR7: + fRegisters.__cr &= 0xFFFFFFF0; + fRegisters.__cr |= (value & 0x0000000F); + return; + case UNW_PPC_VRSAVE: + fRegisters.__vrsave = value; + return; + // not saved + return; + case UNW_PPC_XER: + fRegisters.__xer = value; + return; + case UNW_PPC_AP: + case UNW_PPC_VSCR: + case UNW_PPC_SPEFSCR: + // not saved + return; + } + ABORT("unsupported ppc register"); +} + +inline bool Registers_ppc::validFloatRegister(int regNum) const +{ + if ( regNum < UNW_PPC_F0 ) + return false; + if ( regNum > UNW_PPC_F31 ) + return false; + return true; +} + +inline double Registers_ppc::getFloatRegister(int regNum) const +{ + assert(validFloatRegister(regNum)); + return fFloatRegisters.__fpregs[regNum-UNW_PPC_F0]; +} + +inline void Registers_ppc::setFloatRegister(int regNum, double value) +{ + //fprintf(stderr, "Registers_ppc::setFloatRegister(%d, %g))\n", regNum, value); + assert(validFloatRegister(regNum)); + fFloatRegisters.__fpregs[regNum-UNW_PPC_F0] = value; +} + + +inline bool Registers_ppc::validVectorRegister(int regNum) const +{ + if ( regNum < UNW_PPC_V0 ) + return false; + if ( regNum > UNW_PPC_V31 ) + return false; + return true; +} + +v128 Registers_ppc::getVectorRegister(int regNum) const +{ + assert(validVectorRegister(regNum)); + v128 result = fVectorRegisters[regNum-UNW_PPC_V0]; + //fprintf(stderr, "Registers_ppc::getVectorRegister(this=%p, %d) => <0x%08X, 0x%08X, 0x%08X, 0x%08X> \n", + // this, regNum, result.vec[0], result.vec[1], result.vec[2], result.vec[3]); + return result; +} + +void Registers_ppc::setVectorRegister(int regNum, v128 value) +{ + assert(validVectorRegister(regNum)); + //fprintf(stderr, "Registers_ppc::setVectorRegister(this=%p, %d) <0x%08X, 0x%08X, 0x%08X, 0x%08X> => <0x%08X, 0x%08X, 0x%08X, 0x%08X> \n", + // this, regNum, fVectorRegisters[regNum-UNW_PPC_V0].vec[0], fVectorRegisters[regNum-UNW_PPC_V0].vec[1], fVectorRegisters[regNum-UNW_PPC_V0].vec[2], + // fVectorRegisters[regNum-UNW_PPC_V0].vec[3], value.vec[0], value.vec[1], value.vec[2], value.vec[3]); + fVectorRegisters[regNum-UNW_PPC_V0] = value; +} + + +inline const char* Registers_ppc::getRegisterName(int regNum) +{ + switch ( regNum ) { + case UNW_REG_IP: + return "ip"; + case UNW_REG_SP: + return "sp"; + case UNW_PPC_R0: + return "r0"; + case UNW_PPC_R1: + return "r1"; + case UNW_PPC_R2: + return "r2"; + case UNW_PPC_R3: + return "r3"; + case UNW_PPC_R4: + return "r4"; + case UNW_PPC_R5: + return "r5"; + case UNW_PPC_R6: + return "r6"; + case UNW_PPC_R7: + return "r7"; + case UNW_PPC_R8: + return "r8"; + case UNW_PPC_R9: + return "r9"; + case UNW_PPC_R10: + return "r10"; + case UNW_PPC_R11: + return "r11"; + case UNW_PPC_R12: + return "r12"; + case UNW_PPC_R13: + return "r13"; + case UNW_PPC_R14: + return "r14"; + case UNW_PPC_R15: + return "r15"; + case UNW_PPC_R16: + return "r16"; + case UNW_PPC_R17: + return "r17"; + case UNW_PPC_R18: + return "r18"; + case UNW_PPC_R19: + return "r19"; + case UNW_PPC_R20: + return "r20"; + case UNW_PPC_R21: + return "r21"; + case UNW_PPC_R22: + return "r22"; + case UNW_PPC_R23: + return "r23"; + case UNW_PPC_R24: + return "r24"; + case UNW_PPC_R25: + return "r25"; + case UNW_PPC_R26: + return "r26"; + case UNW_PPC_R27: + return "r27"; + case UNW_PPC_R28: + return "r28"; + case UNW_PPC_R29: + return "r29"; + case UNW_PPC_R30: + return "r30"; + case UNW_PPC_R31: + return "r31"; + case UNW_PPC_F0: + return "fp0"; + case UNW_PPC_F1: + return "fp1"; + case UNW_PPC_F2: + return "fp2"; + case UNW_PPC_F3: + return "fp3"; + case UNW_PPC_F4: + return "fp4"; + case UNW_PPC_F5: + return "fp5"; + case UNW_PPC_F6: + return "fp6"; + case UNW_PPC_F7: + return "fp7"; + case UNW_PPC_F8: + return "fp8"; + case UNW_PPC_F9: + return "fp9"; + case UNW_PPC_F10: + return "fp10"; + case UNW_PPC_F11: + return "fp11"; + case UNW_PPC_F12: + return "fp12"; + case UNW_PPC_F13: + return "fp13"; + case UNW_PPC_F14: + return "fp14"; + case UNW_PPC_F15: + return "fp15"; + case UNW_PPC_F16: + return "fp16"; + case UNW_PPC_F17: + return "fp17"; + case UNW_PPC_F18: + return "fp18"; + case UNW_PPC_F19: + return "fp19"; + case UNW_PPC_F20: + return "fp20"; + case UNW_PPC_F21: + return "fp21"; + case UNW_PPC_F22: + return "fp22"; + case UNW_PPC_F23: + return "fp23"; + case UNW_PPC_F24: + return "fp24"; + case UNW_PPC_F25: + return "fp25"; + case UNW_PPC_F26: + return "fp26"; + case UNW_PPC_F27: + return "fp27"; + case UNW_PPC_F28: + return "fp28"; + case UNW_PPC_F29: + return "fp29"; + case UNW_PPC_F30: + return "fp30"; + case UNW_PPC_F31: + return "fp31"; + case UNW_PPC_LR: + return "lr"; + default: + return "unknown register"; + } + + +} + + +} // namespace libunwind + + + +#endif // __REGISTERS_HPP__ + + + + diff --git a/src/ld/parsers/lto_file.cpp b/src/ld/parsers/lto_file.cpp index 6221c90..46d350d 100644 --- a/src/ld/parsers/lto_file.cpp +++ b/src/ld/parsers/lto_file.cpp @@ -41,6 +41,9 @@ #include "macho_relocatable_file.h" #include "lto_file.h" +// #defines are a work around for +#define __STDC_LIMIT_MACROS 1 +#define __STDC_CONSTANT_MACROS 1 #include "llvm-c/lto.h" @@ -249,11 +252,9 @@ bool Parser::validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type case CPU_TYPE_X86_64: return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, "x86_64-"); case CPU_TYPE_ARM: - switch ( subarch ) { - case CPU_SUBTYPE_ARM_V6: - return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, "armv6-"); - case CPU_SUBTYPE_ARM_V7: - return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, "thumbv7-"); + 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); } break; case CPU_TYPE_POWERPC: @@ -274,10 +275,10 @@ const char* Parser::fileKind(const uint8_t* p, uint64_t fileLength) case CPU_TYPE_X86_64: return "x86_64"; case CPU_TYPE_ARM: - if ( ::lto_module_is_object_file_in_memory_for_target(p, fileLength, "armv6-") ) - return "armv6"; - if ( ::lto_module_is_object_file_in_memory_for_target(p, fileLength, "thumbv7-") ) - return "armv7"; + for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) { + if ( ::lto_module_is_object_file_in_memory_for_target(p, fileLength, t->llvmTriplePrefix) ) + return t->subTypeName; + } return "arm"; } return "unknown bitcode architecture"; diff --git a/src/ld/parsers/macho_dylib_file.cpp b/src/ld/parsers/macho_dylib_file.cpp index 40dad13..436f3e6 100644 --- a/src/ld/parsers/macho_dylib_file.cpp +++ b/src/ld/parsers/macho_dylib_file.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2005-2009 Apple Inc. All rights reserved. + * Copyright (c) 2005-2011 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -146,8 +146,8 @@ public: File(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t mTime, uint32_t ordinal, bool linkingFlatNamespace, bool linkingMainExecutable, bool hoistImplicitPublicDylibs, - ld::MacVersionMin macMin, ld::IPhoneVersionMin iPhoneMin, - bool logAllFiles); + ld::MacVersionMin macMin, ld::IOSVersionMin iPhoneMin, bool addVers, + bool logAllFiles, const char* installPath, bool indirectDylib); virtual ~File() {} // overrides of ld::File @@ -164,6 +164,7 @@ public: virtual bool deadStrippable() const { return _deadStrippable; } virtual bool hasPublicInstallName() const{ return _hasPublicInstallName; } virtual bool hasWeakDefinition(const char* name) const; + virtual bool allSymbolsAreWeakImported() const; protected: @@ -185,7 +186,7 @@ private: public: bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } }; - struct AtomAndWeak { ld::Atom* atom; bool weak; bool tlv; pint_t address; }; + struct AtomAndWeak { ld::Atom* atom; bool weakDef; bool tlv; pint_t address; }; typedef __gnu_cxx::hash_map, CStringEquals> NameToAtomMap; typedef __gnu_cxx::hash_set, CStringEquals> NameSet; @@ -204,7 +205,8 @@ private: static const char* objCInfoSectionName(); const ld::MacVersionMin _macVersionMin; - const ld::IPhoneVersionMin _iPhoneVersionMin; + const ld::IOSVersionMin _iOSVersionMin; + const bool _addVersionLoadCommand; bool _linkingFlat; bool _implicitlyLinkPublicDylibs; ld::File::ObjcConstraint _objcContraint; @@ -240,9 +242,10 @@ template const char* File::objCInfoSectionName() { return "__ima template File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, time_t mTime, uint32_t ord, bool linkingFlatNamespace, bool linkingMainExecutable, bool hoistImplicitPublicDylibs, - ld::MacVersionMin macMin, ld::IPhoneVersionMin iPhoneMin, bool logAllFiles) + ld::MacVersionMin macMin, ld::IOSVersionMin iOSMin, bool addVers, + bool logAllFiles, const char* targetInstallPath, bool indirectDylib) : ld::dylib::File(strdup(pth), mTime, ord), - _macVersionMin(macMin), _iPhoneVersionMin(iPhoneMin), + _macVersionMin(macMin), _iOSVersionMin(iOSMin), _addVersionLoadCommand(addVers), _linkingFlat(linkingFlatNamespace), _implicitlyLinkPublicDylibs(hoistImplicitPublicDylibs), _objcContraint(ld::File::objcConstraintNone), _importProxySection("__TEXT", "__import", ld::Section::typeImportProxies, true), @@ -322,6 +325,14 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, case LC_SUB_CLIENT: _allowableClients.push_back(strdup(((macho_sub_client_command

*)cmd)->client())); break; + case LC_VERSION_MIN_MACOSX: + if ( _addVersionLoadCommand && !indirectDylib && (_iOSVersionMin != ld::iOSVersionUnset) ) + warning("building for iOS, but linking against dylib built for MacOSX: %s", pth); + break; + case LC_VERSION_MIN_IPHONEOS: + if ( _addVersionLoadCommand && !indirectDylib && (_macVersionMin != ld::macVersionUnset) ) + warning("building for MacOSX, but linking against dylib built for iOS: %s", pth); + break; case macho_segment_command

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

*)cmd)->segname(), objCInfoSegmentName()) == 0 ) { @@ -381,7 +392,8 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, entry.path = strdup(((macho_dylib_command

*)cmd)->name()); entry.dylib = NULL; entry.reExport = (cmd->cmd() == LC_REEXPORT_DYLIB); - _dependentDylibs.push_back(entry); + if ( (targetInstallPath == NULL) || (strcmp(targetInstallPath, entry.path) != 0) ) + _dependentDylibs.push_back(entry); break; } cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); @@ -546,8 +558,8 @@ void File::addSymbol(const char* name, bool weakDef, bool tlv, pint_t address if ( _macVersionMin != ld::macVersionUnset ) { sprintf(curOSVers, "$os%d.%d$", (_macVersionMin >> 16), ((_macVersionMin >> 8) & 0xFF)); } - else if ( _iPhoneVersionMin != ld::iPhoneVersionUnset ) { - sprintf(curOSVers, "$os%d.%d$", (_iPhoneVersionMin >> 16), ((_iPhoneVersionMin >> 8) & 0xFF)); + else if ( _iOSVersionMin != ld::iOSVersionUnset ) { + sprintf(curOSVers, "$os%d.%d$", (_iOSVersionMin >> 16), ((_iOSVersionMin >> 8) & 0xFF)); } else { assert(0 && "targeting neither macosx nor iphoneos"); @@ -580,7 +592,7 @@ void File::addSymbol(const char* name, bool weakDef, bool tlv, pint_t address if ( _ignoreExports.count(name) == 0 ) { AtomAndWeak bucket; bucket.atom = NULL; - bucket.weak = weakDef; + bucket.weakDef = weakDef; bucket.tlv = tlv; bucket.address = address; if ( _s_logHashtable ) fprintf(stderr, " adding %s to hash table for %s\n", name, this->path()); @@ -611,7 +623,7 @@ bool File::hasWeakDefinition(const char* name) const typename NameToAtomMap::const_iterator pos = _atoms.find(name); if ( pos != _atoms.end() ) { - return pos->second.weak; + return pos->second.weakDef; } else { // look in children that I re-export @@ -620,20 +632,48 @@ bool File::hasWeakDefinition(const char* name) const //fprintf(stderr, "getJustInTimeAtomsFor: %s NOT found in %s, looking in child %s\n", name, this->path(), (*it)->getInstallPath()); typename NameToAtomMap::iterator cpos = it->dylib->_atoms.find(name); if ( cpos != it->dylib->_atoms.end() ) - return cpos->second.weak; + return cpos->second.weakDef; } } } return false; } + +// If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB +template +bool File::allSymbolsAreWeakImported() const +{ + bool foundNonWeakImport = false; + bool foundWeakImport = false; + //fprintf(stderr, "%s:\n", this->path()); + for (typename NameToAtomMap::const_iterator it = _atoms.begin(); it != _atoms.end(); ++it) { + const ld::Atom* atom = it->second.atom; + if ( atom != NULL ) { + if ( atom->weakImported() ) + foundWeakImport = true; + else + foundNonWeakImport = true; + //fprintf(stderr, " weak_import=%d, name=%s\n", atom->weakImported(), it->first); + } + } + + // don't automatically weak link dylib with no imports + // so at least one weak import symbol and no non-weak-imported symbols must be found + return foundWeakImport && !foundNonWeakImport; +} + + template bool File::containsOrReExports(const char* name, bool* weakDef, bool* tlv, pint_t* defAddress) const { - // check myself + if ( _ignoreExports.count(name) != 0 ) + return false; + +// check myself typename NameToAtomMap::iterator pos = _atoms.find(name); if ( pos != _atoms.end() ) { - *weakDef = pos->second.weak; + *weakDef = pos->second.weakDef; *tlv = pos->second.tlv; *defAddress = pos->second.address; return true; @@ -660,8 +700,8 @@ bool File::justInTimeforEachAtom(const char* name, ld::File::AtomHandler& han AtomAndWeak bucket; - if ( this->containsOrReExports(name, &bucket.weak, &bucket.tlv, &bucket.address) ) { - bucket.atom = new ExportAtom(*this, name, bucket.weak, bucket.tlv, bucket.address); + if ( this->containsOrReExports(name, &bucket.weakDef, &bucket.tlv, &bucket.address) ) { + bucket.atom = new ExportAtom(*this, name, bucket.weakDef, bucket.tlv, bucket.address); _atoms[name] = bucket; _providedAtom = true; if ( _s_logHashtable ) fprintf(stderr, "getJustInTimeAtomsFor: %s found in %s\n", name, this->path()); @@ -789,15 +829,6 @@ void File::assertNoReExportCycles(ReExportChain* prev) } -struct ParserOptions { - bool linkingFlat; - bool linkingMain; - bool addImplictDylibs; - ld::MacVersionMin macOSMin; - ld::IPhoneVersionMin iphoneOSMin; -}; - - template class Parser { @@ -807,14 +838,17 @@ 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) { + uint32_t ordinal, const Options& opts, bool indirectDylib) { return new File(fileContent, fileLength, path, mTime, ordinal, opts.flatNamespace(), opts.linkingMainExecutable(), opts.implicitlyLinkIndirectPublicDylibs(), opts.macosxVersionMin(), - opts.iphoneOSVersionMin(), - opts.logAllFiles()); + opts.iOSVersionMin(), + opts.addVersionLoadCommand(), + opts.logAllFiles(), + opts.installPath(), + indirectDylib); } }; @@ -962,28 +996,29 @@ 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, bool bundleLoader) + const char* path, time_t modTime, const Options& opts, uint32_t ordinal, + bool bundleLoader, bool indirectDylib) { switch ( opts.architecture() ) { case CPU_TYPE_X86_64: if ( Parser::validFile(fileContent, bundleLoader) ) - return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); break; case CPU_TYPE_I386: if ( Parser::validFile(fileContent, bundleLoader) ) - return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); break; case CPU_TYPE_ARM: if ( Parser::validFile(fileContent, bundleLoader) ) - return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); break; case CPU_TYPE_POWERPC: if ( Parser::validFile(fileContent, bundleLoader) ) - return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); break; case CPU_TYPE_POWERPC64: if ( Parser::validFile(fileContent, bundleLoader) ) - return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); break; } return NULL; diff --git a/src/ld/parsers/macho_dylib_file.h b/src/ld/parsers/macho_dylib_file.h index 5c858e2..d26f50e 100644 --- a/src/ld/parsers/macho_dylib_file.h +++ b/src/ld/parsers/macho_dylib_file.h @@ -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@ * @@ -32,7 +32,8 @@ 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, bool bundleLoader); + time_t modTime, const Options& opts, uint32_t ordinal, + bool bundleLoader, bool indirectDylib); } // namespace dylib } // namespace mach_o diff --git a/src/ld/parsers/macho_relocatable_file.cpp b/src/ld/parsers/macho_relocatable_file.cpp index 25c1965..abd44fa 100644 --- a/src/ld/parsers/macho_relocatable_file.cpp +++ b/src/ld/parsers/macho_relocatable_file.cpp @@ -21,7 +21,7 @@ * * @APPLE_LICENSE_HEADER_END@ */ - + #include #include @@ -34,9 +34,9 @@ #include "MachOFileAbstraction.hpp" -#include -#include -#include +#include "libunwind/DwarfInstructions.hpp" +#include "libunwind/AddressSpace.hpp" +#include "libunwind/Registers.hpp" #include #include @@ -64,6 +64,7 @@ template class Parser; template class Atom; template class Section; template class CFISection; +template class CUSection; template class File : public ld::relocatable::File @@ -147,15 +148,16 @@ public: virtual bool addFollowOnFixups() const { return ! _file.canScatterAtoms(); } virtual uint32_t appendAtoms(class Parser& parser, uint8_t* buffer, struct Parser::LabelAndCFIBreakIterator& it, - const struct Parser::CFIInfoArray&) = 0; + const struct Parser::CFI_CU_InfoArrays&) = 0; virtual uint32_t computeAtomCount(class Parser& parser, struct Parser::LabelAndCFIBreakIterator& it, - const struct Parser::CFIInfoArray&) = 0; - virtual void makeFixups(class Parser& parser, const struct Parser::CFIInfoArray&); + const struct Parser::CFI_CU_InfoArrays&) = 0; + virtual void makeFixups(class Parser& parser, const struct Parser::CFI_CU_InfoArrays&); virtual bool addRelocFixup(class Parser& parser, const macho_relocation_info

*); virtual unsigned long contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const { return 0; } virtual bool canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, const ld::IndirectBindingTable& ind) const { return false; } + static const char* makeSectionName(const macho_section* s); protected: Section(File& f, const macho_section* s) @@ -170,7 +172,6 @@ protected: Atom* findContentAtomByAddress(pint_t addr, class Atom* start, class Atom* end); uint32_t x86_64PcRelOffset(uint8_t r_type); static const char* makeSegmentName(const macho_section* s); - static const char* makeSectionName(const macho_section* s); static bool readable(const macho_section* s); static bool writable(const macho_section* s); static bool exectuable(const macho_section* s); @@ -193,9 +194,9 @@ public: uint32_t cfiCount(); virtual ld::Atom::ContentType contentType() { return ld::Atom::typeCFI; } - virtual uint32_t computeAtomCount(class Parser& parser, struct Parser::LabelAndCFIBreakIterator& it, const struct Parser::CFIInfoArray&); - virtual uint32_t appendAtoms(class Parser& parser, uint8_t* buffer, struct Parser::LabelAndCFIBreakIterator& it, const struct Parser::CFIInfoArray&); - virtual void makeFixups(class Parser& parser, const struct Parser::CFIInfoArray&); + virtual uint32_t computeAtomCount(class Parser& parser, struct Parser::LabelAndCFIBreakIterator& it, const struct Parser::CFI_CU_InfoArrays&); + virtual uint32_t appendAtoms(class Parser& parser, uint8_t* buffer, struct Parser::LabelAndCFIBreakIterator& it, const struct Parser::CFI_CU_InfoArrays&); + virtual void makeFixups(class Parser& parser, const struct Parser::CFI_CU_InfoArrays&); virtual bool addFollowOnFixups() const { return false; } @@ -248,6 +249,46 @@ private: }; +template +class CUSection : public Section +{ +public: + CUSection(Parser& parser, File& f, const macho_section* s) + : Section(f, s) { } + + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; + typedef typename A::P::E E; + + virtual uint32_t computeAtomCount(class Parser& parser, struct Parser::LabelAndCFIBreakIterator& it, const struct Parser::CFI_CU_InfoArrays&) { return 0; } + virtual uint32_t appendAtoms(class Parser& parser, uint8_t* buffer, struct Parser::LabelAndCFIBreakIterator& it, const struct Parser::CFI_CU_InfoArrays&) { return 0; } + virtual void makeFixups(class Parser& parser, const struct Parser::CFI_CU_InfoArrays&); + virtual bool addFollowOnFixups() const { return false; } + + struct Info { + pint_t functionStartAddress; + uint32_t functionSymbolIndex; + uint32_t rangeLength; + uint32_t compactUnwindInfo; + const char* personality; + pint_t lsdaAddress; + Atom* function; + Atom* lsda; + }; + + uint32_t count(); + void parse(class Parser& parser, uint32_t cnt, Info array[]); + + +private: + + const char* personalityName(class Parser& parser, const macho_relocation_info

* reloc); + + static int infoSorter(const void* l, const void* r); + +}; + + template class TentativeDefinitionSection : public Section { @@ -259,11 +300,11 @@ public: virtual bool addFollowOnFixups() const { return false; } virtual Atom* findAtomByAddress(typename A::P::uint_t addr) { throw "TentativeDefinitionSection::findAtomByAddress() should never be called"; } virtual uint32_t computeAtomCount(class Parser& parser, struct Parser::LabelAndCFIBreakIterator& it, - const struct Parser::CFIInfoArray&); + const struct Parser::CFI_CU_InfoArrays&); virtual uint32_t appendAtoms(class Parser& parser, uint8_t* buffer, struct Parser::LabelAndCFIBreakIterator& it, - const struct Parser::CFIInfoArray&); - virtual void makeFixups(class Parser& parser, const struct Parser::CFIInfoArray&) {} + const struct Parser::CFI_CU_InfoArrays&); + virtual void makeFixups(class Parser& parser, const struct Parser::CFI_CU_InfoArrays&) {} private: typedef typename A::P::uint_t pint_t; typedef typename A::P P; @@ -283,11 +324,11 @@ public: virtual bool addFollowOnFixups() const { return false; } virtual Atom* findAtomByAddress(typename A::P::uint_t addr) { throw "AbsoluteSymbolSection::findAtomByAddress() should never be called"; } virtual uint32_t computeAtomCount(class Parser& parser, struct Parser::LabelAndCFIBreakIterator& it, - const struct Parser::CFIInfoArray&); + const struct Parser::CFI_CU_InfoArrays&); virtual uint32_t appendAtoms(class Parser& parser, uint8_t* buffer, struct Parser::LabelAndCFIBreakIterator& it, - const struct Parser::CFIInfoArray&); - virtual void makeFixups(class Parser& parser, const struct Parser::CFIInfoArray&) {} + const struct Parser::CFI_CU_InfoArrays&); + virtual void makeFixups(class Parser& parser, const struct Parser::CFI_CU_InfoArrays&) {} virtual Atom* findAbsAtomForValue(typename A::P::uint_t); private: @@ -304,10 +345,10 @@ public: virtual ld::Atom::ContentType contentType() { return _type; } virtual bool dontDeadStrip(); virtual uint32_t computeAtomCount(class Parser& parser, struct Parser::LabelAndCFIBreakIterator& it, - const struct Parser::CFIInfoArray&); + const struct Parser::CFI_CU_InfoArrays&); virtual uint32_t appendAtoms(class Parser& parser, uint8_t* buffer, struct Parser::LabelAndCFIBreakIterator& it, - const struct Parser::CFIInfoArray&); + const struct Parser::CFI_CU_InfoArrays&); protected: typedef typename A::P::uint_t pint_t; typedef typename A::P P; @@ -334,8 +375,8 @@ class ImplicitSizeSection : public Section public: ImplicitSizeSection(Parser& parser, File& f, const macho_section* s) : Section(f, s) { } - virtual uint32_t computeAtomCount(class Parser& parser, struct Parser::LabelAndCFIBreakIterator& it, const struct Parser::CFIInfoArray&); - virtual uint32_t appendAtoms(class Parser& parser, uint8_t* buffer, struct Parser::LabelAndCFIBreakIterator& it, const struct Parser::CFIInfoArray&); + virtual uint32_t computeAtomCount(class Parser& parser, struct Parser::LabelAndCFIBreakIterator& it, const struct Parser::CFI_CU_InfoArrays&); + virtual uint32_t appendAtoms(class Parser& parser, uint8_t* buffer, struct Parser::LabelAndCFIBreakIterator& it, const struct Parser::CFI_CU_InfoArrays&); protected: typedef typename A::P::uint_t pint_t; typedef typename A::P P; @@ -437,7 +478,7 @@ protected: typedef typename A::P::uint_t pint_t; typedef typename A::P P; - virtual void makeFixups(class Parser& parser, const struct Parser::CFIInfoArray&); + virtual void makeFixups(class Parser& parser, const struct Parser::CFI_CU_InfoArrays&); virtual ld::Atom::ContentType contentType() { return ld::Atom::typeNonLazyPointer; } virtual ld::Atom::Alignment alignmentForAddress(pint_t addr) { return ld::Atom::Alignment(log2(sizeof(pint_t))); } virtual const char* unlabeledAtomName(Parser&, pint_t) { return "non_lazy_ptr"; } @@ -598,6 +639,7 @@ protected: virtual Atom* findAtomByAddress(pint_t addr); virtual const char* unlabeledAtomName(Parser&, pint_t) { return "cstring"; } virtual pint_t elementSizeAtAddress(pint_t addr); + virtual bool ignoreLabel(const char* label); virtual bool useElementAt(Parser& parser, struct Parser::LabelAndCFIBreakIterator& it, pint_t addr); virtual ld::Atom::Combine combine(Parser&, pint_t) { return ld::Atom::combineByNameAndContent; } @@ -668,6 +710,7 @@ public: File& machofile() const { return ((Section*)(this->_section))->file(); } void setFixupsRange(uint32_t s, uint32_t c); void setUnwindInfoRange(uint32_t s, uint32_t c); + void extendUnwindInfoRange(); void setLineInfoRange(uint32_t s, uint32_t c); bool roomForMoreLineInfoCount() { return (_lineInfoCount < ((1<::setUnwindInfoRange(uint32_t startIndex, uint32_t count) _unwindInfoCount = count; } +template +void Atom::extendUnwindInfoRange() +{ + if ( _unwindInfoCount+1 >= (1 << kUnwindInfoCountBits) ) + throwf("too many compact unwind infos in function %s", this->name()); + _unwindInfoCount += 1; +} + template void Atom::setLineInfoRange(uint32_t startIndex, uint32_t count) { @@ -792,8 +843,8 @@ template <> void Atom::verifyAlignment() const { if ( (this->section().type() == ld::Section::typeCode) && ! isThumb() ) { - if ( (_objAddress % 4) != 0 ) - warning("ARM function %s not 4-byte aligned", this->name()); + if ( ((_objAddress % 4) != 0) || (this->alignment().powerOf2 < 2) ) + warning("ARM function not 4-byte aligned: %s from %s", this->name(), this->file()->path()); } } @@ -951,14 +1002,19 @@ public: uint32_t symIndex; }; - struct CFIInfoArray { + struct CFI_CU_InfoArrays { typedef typename CFISection::CFI_Atom_Info CFI_Atom_Info; - CFIInfoArray(const CFI_Atom_Info* cfia, uint32_t cfiac) : array(cfia), count(cfiac) {} - const CFI_Atom_Info* const array; - const uint32_t count; + typedef typename CUSection::Info CU_Info; + CFI_CU_InfoArrays(const CFI_Atom_Info* cfiAr, uint32_t cfiC, CU_Info* cuAr, uint32_t cuC) + : cfiArray(cfiAr), cuArray(cuAr), cfiCount(cfiC), cuCount(cuC) {} + const CFI_Atom_Info* const cfiArray; + CU_Info* const cuArray; + const uint32_t cfiCount; + const uint32_t cuCount; }; + private: friend class Section; @@ -966,7 +1022,8 @@ private: sectionTypeNonLazy, sectionTypeCFI, sectionTypeCString, sectionTypeCStringPointer, sectionTypeUTF16Strings, sectionTypeCFString, sectionTypeObjC2ClassRefs, typeObjC2CategoryList, sectionTypeObjC1Classes, sectionTypeSymboled, sectionTypeObjC1ClassRefs, - sectionTypeTentativeDefinitions, sectionTypeAbsoluteSymbols, sectionTypeTLVDefs }; + sectionTypeTentativeDefinitions, sectionTypeAbsoluteSymbols, sectionTypeTLVDefs, + sectionTypeCompactUnwind }; template struct MachOSectionAndSectionClass @@ -986,6 +1043,9 @@ private: return 1; } }; + + struct ParserAndSectionsArray { Parser* parser; const uint32_t* sortedSectionsArray; }; + Parser(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, @@ -994,11 +1054,13 @@ private: uint8_t loadCommandSizeMask(); bool parseLoadCommands(); void makeSections(); - void checkForLSDA(); void prescanSymbolTable(); - void makeSortedSymbolsArray(uint32_t array[]); + void makeSortedSymbolsArray(uint32_t symArray[], const uint32_t sectionArray[]); + void makeSortedSectionsArray(uint32_t array[]); static int pointerSorter(const void* l, const void* r); static int symbolIndexSorter(void* extra, const void* l, const void* r); + static int sectionIndexSorter(void* extra, const void* l, const void* r); + void parseDebugInfo(); void parseStabs(); static bool isConstFunStabs(const char *stabStr); @@ -1032,9 +1094,8 @@ private: // filled in by parse() CFISection* _EHFrameSection; + CUSection* _compactUnwindSection; AbsoluteSymbolSection* _absoluteSection; - uint32_t _lsdaTextSectionNum; - uint32_t _lsdaDataSectionNum; uint32_t _tentativeDefinitionCount; uint32_t _absoluteSymbolCount; uint32_t _symbolsInSections; @@ -1059,8 +1120,7 @@ Parser::Parser(const uint8_t* fileContent, uint64_t fileLength, const char* p _indirectTable(NULL), _indirectTableCount(0), _undefinedStartIndex(0), _undefinedEndIndex(0), _sectionsStart(NULL), _machOSectionsCount(0), _hasUUID(false), - _EHFrameSection(NULL), _absoluteSection(NULL), - _lsdaTextSectionNum(0), _lsdaDataSectionNum(0), + _EHFrameSection(NULL), _compactUnwindSection(NULL), _absoluteSection(NULL), _tentativeDefinitionCount(0), _absoluteSymbolCount(0), _symbolsInSections(0), _hasLongBranchStubs(false), _AppleObjc(false), _overlappingSymbols(false), _convertUnwindInfo(convertDUI), @@ -1206,17 +1266,10 @@ const char* Parser::fileKind(const uint8_t* fileContent) return NULL; if ( header->cputype() != CPU_TYPE_ARM ) return NULL; - switch ( header->cpusubtype() ) { - case CPU_SUBTYPE_ARM_V4T: - return "armv4t"; - case CPU_SUBTYPE_ARM_V5TEJ: - return "armv5"; - case CPU_SUBTYPE_ARM_V6: - return "armv6"; - case CPU_SUBTYPE_ARM_V7: - return "armv7"; - case CPU_SUBTYPE_ARM_ALL: - return "arm-ALL"; + for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) { + if ( t->subType == (cpu_subtype_t)header->cpusubtype() ) { + return t->subTypeName; + } } return "arm???"; } @@ -1302,7 +1355,8 @@ bool Parser::LabelAndCFIBreakIterator::next(Parser& parser, uint32_t sectN while ( symIndex < sortedSymbolCount ) { const macho_nlist

& sym = parser.symbolFromIndex(sortedSymbolIndexes[symIndex]); pint_t nextSymbolAddr = sym.n_value(); - if ( (nextSymbolAddr >= startAddr) && (sym.n_sect() >= sectNum) ) + //fprintf(stderr, "sectNum=%d, nextSymbolAddr=0x%08llX, name=%s\n", sectNum, (uint64_t)nextSymbolAddr, parser.nameFromSymbol(sym)); + if ( (nextSymbolAddr > startAddr) || ((nextSymbolAddr == startAddr) && (sym.n_sect() == sectNum)) ) break; ++symIndex; } @@ -1444,16 +1498,28 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) if ( ! parseLoadCommands() ) return _file; + // make array of + uint32_t sortedSectionIndexes[_machOSectionsCount]; + this->makeSortedSectionsArray(sortedSectionIndexes); + // make symbol table sorted by address - this->checkForLSDA(); this->prescanSymbolTable(); uint32_t sortedSymbolIndexes[_symbolsInSections]; - this->makeSortedSymbolsArray(sortedSymbolIndexes); + this->makeSortedSymbolsArray(sortedSymbolIndexes, sortedSectionIndexes); // allocate Section object for each mach-o section makeSections(); - // if it exists, do special parsing of __eh_frame section + // if it exists, do special early parsing of __compact_unwind section + uint32_t countOfCUs = 0; + if ( _compactUnwindSection != NULL ) + countOfCUs = _compactUnwindSection->count(); + uint8_t cuInfoBuffer[sizeof(typename CUSection::Info) * countOfCUs]; + typename CUSection::Info* cuInfoArray = (typename CUSection::Info*)cuInfoBuffer; + if ( countOfCUs != 0 ) + _compactUnwindSection->parse(*this, countOfCUs, cuInfoArray); + + // if it exists, do special early parsing of __eh_frame section // stack allocate array of CFI_Atom_Info uint32_t countOfCFIs = 0; if ( _EHFrameSection != NULL ) @@ -1489,7 +1555,7 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) ++cfiStartsCount; } } - CFIInfoArray cfis(cfiArray, countOfCFIs); + CFI_CU_InfoArrays cfis(cfiArray, countOfCFIs, cuInfoArray, countOfCUs); // create sorted array of function starts and lsda starts pint_t cfiStartsArray[cfiStartsCount]; @@ -1539,7 +1605,7 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) uint8_t* atoms = _file->_atomsArray + _file->_atomsArrayCount*sizeof(Atom); breakIterator2.beginSection(); uint32_t count = sections[i]->appendAtoms(*this, atoms, breakIterator2, cfis); - //fprintf(stderr, "append count=%u for section %s\n", count, sections[i]->machoSection()->sectname()); + //fprintf(stderr, "append count=%u for section %s/%s\n", count, sections[i]->machoSection()->segname(), sections[i]->machoSection()->sectname()); _file->_atomsArrayCount += count; } assert( _file->_atomsArrayCount == computedAtomCount && "more atoms allocated than expected"); @@ -1574,7 +1640,7 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) _allFixups.clear(); // add unwind info - _file->_unwindInfos.reserve(countOfFDEs); + _file->_unwindInfos.reserve(countOfFDEs+countOfCUs); for(uint32_t i=0; i < countOfCFIs; ++i) { if ( cfiArray[i].isCIE ) continue; @@ -1587,7 +1653,33 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) func->setUnwindInfoRange(_file->_unwindInfos.size()-1, 1); } } - + // apply compact infos in __LD,__compact_unwind section to each function + // if function also has dwarf unwind, CU will override it + Atom* lastFunc = NULL; + uint32_t lastEnd = 0; + for(uint32_t i=0; i < countOfCUs; ++i) { + typename CUSection::Info* info = &cuInfoArray[i]; + assert(info->function != NULL); + ld::Atom::UnwindInfo ui; + ui.startOffset = info->functionStartAddress - info->function->objectAddress(); + ui.unwindInfo = info->compactUnwindInfo; + _file->_unwindInfos.push_back(ui); + // if previous is for same function, extend range + if ( info->function == lastFunc ) { + if ( lastEnd != ui.startOffset ) { + if ( lastEnd < ui.startOffset ) + warning("__LD,__compact_unwind entries for %s have a gap at offset 0x%0X", info->function->name(), lastEnd); + else + warning("__LD,__compact_unwind entries for %s overlap at offset 0x%0X", info->function->name(), lastEnd); + } + lastFunc->extendUnwindInfoRange(); + } + else + info->function->setUnwindInfoRange(_file->_unwindInfos.size()-1, 1); + lastFunc = info->function; + lastEnd = ui.startOffset + info->rangeLength; + } + // parse dwarf debug info to get line info this->parseDebugInfo(); @@ -1686,31 +1778,6 @@ bool Parser::parseLoadCommands() return true; } -template <> -void Parser::checkForLSDA() -{ - // ARM has no FDEs, so need labels to break up section into atoms -} - -template -void Parser::checkForLSDA() -{ - // ignore labels on __gcc_except_tab section, we'll break it into atoms based on FDE info - for (uint32_t i=0; i < _machOSectionsCount; ++i) { - const macho_section

* sect = &_sectionsStart[i]; - if ( strncmp(sect->sectname(), "__gcc_except_tab", 16) == 0 ) { - if ( strcmp(sect->segname(), "__TEXT") == 0 ) { - assert(_lsdaTextSectionNum == 0); - _lsdaTextSectionNum = i+1; - } - else if ( strcmp(sect->segname(), "__DATA") == 0 ) { - assert(_lsdaDataSectionNum == 0); - _lsdaDataSectionNum = i+1; - } - } - } -} - template void Parser::prescanSymbolTable() @@ -1766,12 +1833,6 @@ void Parser::prescanSymbolTable() if ( symbolName[0] == 'L' ) continue; - // ignore labels in __gcc_except_tab section - if ( (_lsdaTextSectionNum != 0) && (sym.n_sect() == _lsdaTextSectionNum) ) - continue; - if ( (_lsdaDataSectionNum != 0) && (sym.n_sect() == _lsdaDataSectionNum) ) - continue; - // how many def syms in each section if ( sym.n_sect() > _machOSectionsCount ) throw "bad n_sect in symbol table"; @@ -1781,11 +1842,68 @@ void Parser::prescanSymbolTable() } template -int Parser::symbolIndexSorter(void* extra, const void* l, const void* r) +int Parser::sectionIndexSorter(void* extra, const void* l, const void* r) { Parser* parser = (Parser*)extra; const uint32_t* left = (uint32_t*)l; const uint32_t* right = (uint32_t*)r; + const macho_section

* leftSect = parser->machOSectionFromSectionIndex(*left); + const macho_section

* rightSect = parser->machOSectionFromSectionIndex(*right); + + // can't just return difference because 64-bit diff does not fit in 32-bit return type + int64_t result = leftSect->addr() - rightSect->addr(); + if ( result == 0 ) { + // two sections with same start address + // one with zero size goes first + bool leftEmpty = ( leftSect->size() == 0 ); + bool rightEmpty = ( rightSect->size() == 0 ); + if ( leftEmpty != rightEmpty ) { + return ( rightEmpty ? 1 : -1 ); + } + if ( !leftEmpty && !rightEmpty ) + throwf("overlapping sections"); + // both empty, so chose file order + return ( rightSect - leftSect ); + } + else if ( result < 0 ) + return -1; + else + return 1; +} + +template +void Parser::makeSortedSectionsArray(uint32_t array[]) +{ + const bool log = false; + + if ( log ) { + fprintf(stderr, "unsorted sections:\n"); + for(unsigned int i=0; i < _machOSectionsCount; ++i ) + fprintf(stderr, "0x%08llX %s %s\n", _sectionsStart[i].addr(), _sectionsStart[i].segname(), _sectionsStart[i].sectname()); + } + + // sort by symbol table address + for (uint32_t i=0; i < _machOSectionsCount; ++i) + array[i] = i; + ::qsort_r(array, _machOSectionsCount, sizeof(uint32_t), this, §ionIndexSorter); + + if ( log ) { + fprintf(stderr, "sorted sections:\n"); + for(unsigned int i=0; i < _machOSectionsCount; ++i ) + fprintf(stderr, "0x%08llX %s %s\n", _sectionsStart[array[i]].addr(), _sectionsStart[array[i]].segname(), _sectionsStart[array[i]].sectname()); + } +} + + + +template +int Parser::symbolIndexSorter(void* extra, const void* l, const void* r) +{ + ParserAndSectionsArray* extraInfo = (ParserAndSectionsArray*)extra; + Parser* parser = extraInfo->parser; + const uint32_t* sortedSectionsArray = extraInfo->sortedSectionsArray; + const uint32_t* left = (uint32_t*)l; + const uint32_t* right = (uint32_t*)r; const macho_nlist

& leftSym = parser->symbolFromIndex(*left); const macho_nlist

& rightSym = parser->symbolFromIndex(*right); // can't just return difference because 64-bit diff does not fit in 32-bit return type @@ -1793,9 +1911,15 @@ int Parser::symbolIndexSorter(void* extra, const void* l, const void* r) if ( result == 0 ) { // two symbols with same address // if in different sections, sort earlier section first - if ( leftSym.n_sect() != rightSym.n_sect() ) - return (leftSym.n_sect() - rightSym.n_sect()); - //, means one is an alias + if ( leftSym.n_sect() != rightSym.n_sect() ) { + for (uint32_t i=0; i < parser->machOSectionCount(); ++i) { + if ( sortedSectionsArray[i]+1 == leftSym.n_sect() ) + return -1; + if ( sortedSectionsArray[i]+1 == rightSym.n_sect() ) + return 1; + } + } + // two symbols in same section, means one is an alias // if only one is global, make the other an alias (sort first) if ( (leftSym.n_type() & N_EXT) != (rightSym.n_type() & N_EXT) ) { if ( (rightSym.n_type() & N_EXT) != 0 ) @@ -1812,9 +1936,12 @@ int Parser::symbolIndexSorter(void* extra, const void* l, const void* r) return 1; } + template -void Parser::makeSortedSymbolsArray(uint32_t array[]) +void Parser::makeSortedSymbolsArray(uint32_t array[], const uint32_t sectionArray[]) { + const bool log = false; + uint32_t* p = array; for (uint32_t i=0; i < this->_symbolCount; ++i) { const macho_nlist

& sym = symbolFromIndex(i); @@ -1831,12 +1958,6 @@ void Parser::makeSortedSymbolsArray(uint32_t array[]) if ( symbolName[0] == 'L' ) continue; - // ignore labels in __gcc_except_tab section - if ( (_lsdaTextSectionNum != 0) && (sym.n_sect() == _lsdaTextSectionNum) ) - continue; - if ( (_lsdaDataSectionNum != 0) && (sym.n_sect() == _lsdaDataSectionNum) ) - continue; - // how many def syms in each section if ( sym.n_sect() > _machOSectionsCount ) throw "bad n_sect in symbol table"; @@ -1847,7 +1968,8 @@ void Parser::makeSortedSymbolsArray(uint32_t array[]) assert(p == &array[_symbolsInSections] && "second pass over symbol table yield a different number of symbols"); // sort by symbol table address - ::qsort_r(array, _symbolsInSections, sizeof(uint32_t), this, &symbolIndexSorter); + ParserAndSectionsArray extra = { this, sectionArray }; + ::qsort_r(array, _symbolsInSections, sizeof(uint32_t), &extra, &symbolIndexSorter); // look for two symbols at same address _overlappingSymbols = false; @@ -1858,9 +1980,11 @@ void Parser::makeSortedSymbolsArray(uint32_t array[]) } } - //fprintf(stderr, "sorted symbols:\n"); - //for(unsigned int i=0; i < _symbolsInSections; ++i ) - // fprintf(stderr, "0x%09llX symIndex=%3d sectNum=%2d, %s\n", symbolFromIndex(array[i]).n_value(), array[i], symbolFromIndex(array[i]).n_sect(), nameFromSymbol(symbolFromIndex(array[i])) ); + if ( log ) { + fprintf(stderr, "sorted symbols:\n"); + for(unsigned int i=0; i < _symbolsInSections; ++i ) + fprintf(stderr, "0x%09llX symIndex=%d sectNum=%2d, %s\n", symbolFromIndex(array[i]).n_value(), array[i], symbolFromIndex(array[i]).n_sect(), nameFromSymbol(symbolFromIndex(array[i])) ); + } } @@ -1876,21 +2000,30 @@ void Parser::makeSections() unsigned int count = 0; for (uint32_t i=0; i < _machOSectionsCount; ++i) { const macho_section

* sect = &_sectionsStart[i]; - // ignore dwarf sections if ( (sect->flags() & S_ATTR_DEBUG) != 0 ) { - // note that .o file has dwarf - _file->_debugInfoKind = ld::relocatable::File::kDebugInfoDwarf; - // save off iteresting dwarf sections - if ( strcmp(sect->sectname(), "__debug_info") == 0 ) - _file->_dwarfDebugInfoSect = sect; - else if ( strcmp(sect->sectname(), "__debug_abbrev") == 0 ) - _file->_dwarfDebugAbbrevSect = sect; - else if ( strcmp(sect->sectname(), "__debug_line") == 0 ) - _file->_dwarfDebugLineSect = sect; - else if ( strcmp(sect->sectname(), "__debug_str") == 0 ) - _file->_dwarfDebugStringSect = sect; - // linker does not propagate dwarf sections to output file - continue; + if ( strcmp(sect->segname(), "__DWARF") == 0 ) { + // note that .o file has dwarf + _file->_debugInfoKind = ld::relocatable::File::kDebugInfoDwarf; + // save off iteresting dwarf sections + if ( strcmp(sect->sectname(), "__debug_info") == 0 ) + _file->_dwarfDebugInfoSect = sect; + else if ( strcmp(sect->sectname(), "__debug_abbrev") == 0 ) + _file->_dwarfDebugAbbrevSect = sect; + else if ( strcmp(sect->sectname(), "__debug_line") == 0 ) + _file->_dwarfDebugLineSect = sect; + else if ( strcmp(sect->sectname(), "__debug_str") == 0 ) + _file->_dwarfDebugStringSect = sect; + // linker does not propagate dwarf sections to output file + continue; + } + else if ( strcmp(sect->segname(), "__LD") == 0 ) { + if ( strncmp(sect->sectname(), "__compact_unwind", 16) == 0 ) { + machOSects[count].sect = sect; + totalSectionsSize += sizeof(CUSection); + machOSects[count++].type = sectionTypeCompactUnwind; + continue; + } + } } // ignore empty __OBJC sections if ( (sect->size() == 0) && (strcmp(sect->segname(), "__OBJC") == 0) ) @@ -1918,11 +2051,11 @@ void Parser::makeSections() _file->_ojcReplacmentClass = true; if ( sect->size() > 8 ) { warning("section %s/%s has unexpectedly large size %llu in %s", - sect->segname(), sect->sectname(), sect->size(), _file->path()); + sect->segname(), Section::makeSectionName(sect), sect->size(), _file->path()); } } else { - warning("can't parse %s/%s section in %s", sect->segname(), sect->sectname(), _file->path()); + warning("can't parse %s/%s section in %s", sect->segname(), Section::makeSectionName(sect), _file->path()); } continue; } @@ -2101,6 +2234,11 @@ void Parser::makeSections() *objects++ = new (space) TLVDefsSection(*this, *_file, machOSects[i].sect); space += sizeof(TLVDefsSection); break; + case sectionTypeCompactUnwind: + _compactUnwindSection = new (space) CUSection(*this, *_file, machOSects[i].sect); + *objects++ = _compactUnwindSection; + space += sizeof(CUSection); + break; case sectionTypeTentativeDefinitions: *objects++ = new (space) TentativeDefinitionSection(*this, *_file); space += sizeof(TentativeDefinitionSection); @@ -2462,7 +2600,7 @@ void Parser::addFixups(const SourceLocation& src, ld::Fixup::Kind kind, const template uint32_t TentativeDefinitionSection::computeAtomCount(class Parser& parser, struct Parser::LabelAndCFIBreakIterator& it, - const struct Parser::CFIInfoArray&) + const struct Parser::CFI_CU_InfoArrays&) { return parser.tentativeDefinitionCount(); } @@ -2470,7 +2608,7 @@ uint32_t TentativeDefinitionSection::computeAtomCount(class Parser& parser template uint32_t TentativeDefinitionSection::appendAtoms(class Parser& parser, uint8_t* p, struct Parser::LabelAndCFIBreakIterator& it, - const struct Parser::CFIInfoArray&) + const struct Parser::CFI_CU_InfoArrays&) { this->_beginAtoms = (Atom*)p; uint32_t count = 0; @@ -2489,8 +2627,8 @@ uint32_t TentativeDefinitionSection::appendAtoms(class Parser& parser, uin ++alignP2; } // limit alignment of extremely large commons to 2^15 bytes (8-page) - if ( alignP2 > 12 ) - alignP2 = 12; + if ( alignP2 > 15 ) + alignP2 = 15; Atom* allocatedSpace = (Atom*)p; new (allocatedSpace) Atom(*this, parser.nameFromSymbol(sym), (pint_t)ULLONG_MAX, size, ld::Atom::definitionTentative, ld::Atom::combineByName, @@ -2508,7 +2646,7 @@ uint32_t TentativeDefinitionSection::appendAtoms(class Parser& parser, uin template uint32_t AbsoluteSymbolSection::computeAtomCount(class Parser& parser, struct Parser::LabelAndCFIBreakIterator& it, - const struct Parser::CFIInfoArray&) + const struct Parser::CFI_CU_InfoArrays&) { return parser.absoluteSymbolCount(); } @@ -2516,7 +2654,7 @@ uint32_t AbsoluteSymbolSection::computeAtomCount(class Parser& parser, template uint32_t AbsoluteSymbolSection::appendAtoms(class Parser& parser, uint8_t* p, struct Parser::LabelAndCFIBreakIterator& it, - const struct Parser::CFIInfoArray&) + const struct Parser::CFI_CU_InfoArrays&) { this->_beginAtoms = (Atom*)p; uint32_t count = 0; @@ -3730,9 +3868,9 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, template uint32_t CFISection::computeAtomCount(class Parser& parser, struct Parser::LabelAndCFIBreakIterator& it, - const struct Parser::CFIInfoArray& cfis) + const struct Parser::CFI_CU_InfoArrays& cfis) { - return cfis.count; + return cfis.cfiCount; } @@ -3740,12 +3878,12 @@ uint32_t CFISection::computeAtomCount(class Parser& parser, template uint32_t CFISection::appendAtoms(class Parser& parser, uint8_t* p, struct Parser::LabelAndCFIBreakIterator& it, - const struct Parser::CFIInfoArray& cfis) + const struct Parser::CFI_CU_InfoArrays& cfis) { this->_beginAtoms = (Atom*)p; // walk CFI_Atom_Info array and create atom for each entry - const CFI_Atom_Info* start = &cfis.array[0]; - const CFI_Atom_Info* end = &cfis.array[cfis.count]; + const CFI_Atom_Info* start = &cfis.cfiArray[0]; + const CFI_Atom_Info* end = &cfis.cfiArray[cfis.cfiCount]; for(const CFI_Atom_Info* a=start; a < end; ++a) { Atom* space = (Atom*)p; new (space) Atom(*this, (a->isCIE ? "CIE" : "FDE"), a->address, a->size, @@ -3755,7 +3893,7 @@ uint32_t CFISection::appendAtoms(class Parser& parser, uint8_t* p, p += sizeof(Atom); } this->_endAtoms = (Atom*)p; - return cfis.count; + return cfis.cfiCount; } @@ -3845,14 +3983,14 @@ void CFISection::addCiePersonalityFixups(class Parser& parser, const CFI_A } template -void CFISection::makeFixups(class Parser& parser, const struct Parser::CFIInfoArray& cfis) +void CFISection::makeFixups(class Parser& parser, const struct Parser::CFI_CU_InfoArrays& cfis) { ld::Fixup::Kind store32 = bigEndian() ? ld::Fixup::kindStoreBigEndian32 : ld::Fixup::kindStoreLittleEndian32; ld::Fixup::Kind store64 = bigEndian() ? ld::Fixup::kindStoreBigEndian64 : ld::Fixup::kindStoreLittleEndian64; // add all references for FDEs, including implicit group references - const CFI_Atom_Info* end = &cfis.array[cfis.count]; - for(const CFI_Atom_Info* p = &cfis.array[0]; p < end; ++p) { + const CFI_Atom_Info* end = &cfis.cfiArray[cfis.cfiCount]; + for(const CFI_Atom_Info* p = &cfis.cfiArray[0]; p < end; ++p) { if ( p->isCIE ) { // add reference to personality function if used if ( p->u.cieInfo.personality.targetAddress != CFI_INVALID_ADDRESS ) { @@ -4072,6 +4210,136 @@ typename A::P::uint_t CFISection::OAS::getEncodedP(pint_t& addr, pint_t end, return result; } +template <> +const char* CUSection::personalityName(class Parser& parser, const macho_relocation_info* reloc) +{ + assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section"); + assert((reloc->r_type() == X86_64_RELOC_UNSIGNED) && "wrong reloc type on personality column in __compact_unwind section"); + const macho_nlist

& sym = parser.symbolFromIndex(reloc->r_symbolnum()); + return parser.nameFromSymbol(sym); +} + +template <> +const char* CUSection::personalityName(class Parser& parser, const macho_relocation_info* reloc) +{ + assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section"); + assert((reloc->r_type() == GENERIC_RELOC_VANILLA) && "wrong reloc type on personality column in __compact_unwind section"); + const macho_nlist

& sym = parser.symbolFromIndex(reloc->r_symbolnum()); + return parser.nameFromSymbol(sym); +} + +template +const char* CUSection::personalityName(class Parser& parser, const macho_relocation_info

* reloc) +{ + return NULL; +} + + +template +int CUSection::infoSorter(const void* l, const void* r) +{ + // sort references by symbol index, then address + const Info* left = (Info*)l; + const Info* right = (Info*)r; + if ( left->functionSymbolIndex == right->functionSymbolIndex ) + return (left->functionStartAddress - right->functionStartAddress); + else + return (left->functionSymbolIndex - right->functionSymbolIndex); +} + +template +void CUSection::parse(class Parser& parser, uint32_t cnt, Info array[]) +{ + // walk section content and copy to Info array + const macho_compact_unwind_entry

* const entries = (macho_compact_unwind_entry

*)(this->file().fileContent() + this->_machOSection->offset()); + for (uint32_t i=0; i < cnt; ++i) { + Info* info = &array[i]; + const macho_compact_unwind_entry

* entry = &entries[i]; + info->functionStartAddress = entry->codeStart(); + info->functionSymbolIndex = 0xFFFFFFFF; + info->rangeLength = entry->codeLen(); + info->compactUnwindInfo = entry->compactUnwindInfo(); + info->personality = NULL; + info->lsdaAddress = entry->lsda(); + info->function = NULL; + info->lsda = NULL; + if ( (info->compactUnwindInfo & UNWIND_PERSONALITY_MASK) != 0 ) + warning("no bits should be set in UNWIND_PERSONALITY_MASK of compact unwind encoding in __LD,__compact_unwind section"); + if ( info->lsdaAddress != 0 ) { + info->compactUnwindInfo |= UNWIND_HAS_LSDA; + } + } + + // scan relocs, local relocs are useless - ignore them + // extern relocs are needed for personality references (possibly for function/lsda refs??) + const macho_relocation_info

* relocs = (macho_relocation_info

*)(this->file().fileContent() + this->_machOSection->reloff()); + const macho_relocation_info

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

* reloc = relocs; reloc < relocsEnd; ++reloc) { + if ( reloc->r_extern() ) { + // only expect external relocs on some colummns + if ( (reloc->r_address() % sizeof(macho_compact_unwind_entry

)) == macho_compact_unwind_entry

::personalityFieldOffset() ) { + uint32_t entryIndex = reloc->r_address() / sizeof(macho_compact_unwind_entry

); + array[entryIndex].personality = this->personalityName(parser, reloc); + } + else if ( (reloc->r_address() % sizeof(macho_compact_unwind_entry

)) == macho_compact_unwind_entry

::lsdaFieldOffset() ) { + uint32_t entryIndex = reloc->r_address() / sizeof(macho_compact_unwind_entry

); + const macho_nlist

& lsdaSym = parser.symbolFromIndex(reloc->r_symbolnum()); + if ( (lsdaSym.n_type() & N_TYPE) == N_SECT ) + array[entryIndex].lsdaAddress = lsdaSym.n_value(); + else + warning("unexpected extern relocation to lsda in __compact_unwind section"); + } + else if ( (reloc->r_address() % sizeof(macho_compact_unwind_entry

)) == macho_compact_unwind_entry

::codeStartFieldOffset() ) { + uint32_t entryIndex = reloc->r_address() / sizeof(macho_compact_unwind_entry

); + array[entryIndex].functionSymbolIndex = reloc->r_symbolnum(); + } + else { + warning("unexpected extern relocation in __compact_unwind section"); + } + } + } + + // sort array by function start address so unwind infos will be contiguous for a given function + ::qsort(array, cnt, sizeof(Info), infoSorter); +} + +template +uint32_t CUSection::count() +{ + const macho_section

* machoSect = this->machoSection(); + if ( (machoSect->size() % sizeof(macho_compact_unwind_entry

)) != 0 ) + throw "malformed __LD,__compact_unwind section, bad length"; + + return machoSect->size() / sizeof(macho_compact_unwind_entry

); +} + +template +void CUSection::makeFixups(class Parser& parser, const struct Parser::CFI_CU_InfoArrays& cus) +{ + Info* const arrayStart = cus.cuArray; + Info* const arrayEnd = &cus.cuArray[cus.cuCount]; + for (Info* info=arrayStart; info < arrayEnd; ++info) { + // if external reloc was used, real address is symbol n_value + addend + if ( info->functionSymbolIndex != 0xFFFFFFFF ) + info->functionStartAddress += parser.symbolFromIndex(info->functionSymbolIndex).n_value(); + // find function atom from address + info->function = parser.findAtomByAddress(info->functionStartAddress); + // find lsda atom from address + if ( info->lsdaAddress != 0 ) { + info->lsda = parser.findAtomByAddress(info->lsdaAddress); + // add lsda subordinate + typename Parser::SourceLocation src(info->function, info->functionStartAddress - info->function->objectAddress()); + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindNoneGroupSubordinateLSDA, info->lsda); + } + if ( info->personality != NULL ) { + // add personality subordinate + typename Parser::SourceLocation src(info->function, info->functionStartAddress - info->function->objectAddress()); + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindNoneGroupSubordinatePersonality, false, info->personality); + } + } + +} + template SymboledSection::SymboledSection(Parser& parser, File& f, const macho_section* s) : Section(f, s), _type(ld::Atom::typeUnclassified) @@ -4127,7 +4395,7 @@ bool SymboledSection::dontDeadStrip() template uint32_t SymboledSection::computeAtomCount(class Parser& parser, struct Parser::LabelAndCFIBreakIterator& it, - const struct Parser::CFIInfoArray&) + const struct Parser::CFI_CU_InfoArrays&) { const pint_t startAddr = this->_machOSection->addr(); const pint_t endAddr = startAddr + this->_machOSection->size(); @@ -4147,7 +4415,7 @@ uint32_t SymboledSection::computeAtomCount(class Parser& parser, template uint32_t SymboledSection::appendAtoms(class Parser& parser, uint8_t* p, struct Parser::LabelAndCFIBreakIterator& it, - const struct Parser::CFIInfoArray&) + const struct Parser::CFI_CU_InfoArrays&) { this->_beginAtoms = (Atom*)p; @@ -4173,8 +4441,12 @@ uint32_t SymboledSection::appendAtoms(class Parser& parser, uint8_t* p, this->_hasAliases = true; } else { + ld::Atom::SymbolTableInclusion inclusion = ld::Atom::symbolTableNotIn; + ld::Atom::ContentType ctype = this->contentType(); + if ( ctype == ld::Atom::typeLSDA ) + inclusion = ld::Atom::symbolTableInWithRandomAutoStripLabel; new (allocatedSpace) Atom(*this, "anon", addr, size, ld::Atom::definitionRegular, ld::Atom::combineNever, - ld::Atom::scopeTranslationUnit, this->contentType(), ld::Atom::symbolTableNotIn, + ld::Atom::scopeTranslationUnit, ctype, inclusion, this->dontDeadStrip(), false, false, this->alignmentForAddress(addr)); } p += sizeof(Atom); @@ -4189,7 +4461,7 @@ uint32_t SymboledSection::appendAtoms(class Parser& parser, uint8_t* p, template uint32_t ImplicitSizeSection::computeAtomCount(class Parser& parser, struct Parser::LabelAndCFIBreakIterator& it, - const struct Parser::CFIInfoArray&) + const struct Parser::CFI_CU_InfoArrays&) { uint32_t count = 0; const macho_section

* sect = this->machoSection(); @@ -4220,7 +4492,7 @@ uint32_t ImplicitSizeSection::computeAtomCount(class Parser& parser, template uint32_t ImplicitSizeSection::appendAtoms(class Parser& parser, uint8_t* p, struct Parser::LabelAndCFIBreakIterator& it, - const struct Parser::CFIInfoArray&) + const struct Parser::CFI_CU_InfoArrays&) { this->_beginAtoms = (Atom*)p; @@ -4382,6 +4654,12 @@ bool CStringSection::useElementAt(Parser& parser, struct Parser::LabelA return true; } +template +bool CStringSection::ignoreLabel(const char* label) +{ + return (label[0] == 'L') || (label[0] == 'l'); +} + template Atom* CStringSection::findAtomByAddress(pint_t addr) { @@ -4449,13 +4727,13 @@ ld::Fixup::Kind NonLazyPointerSection::fixupKind() } template <> -void NonLazyPointerSection::makeFixups(class Parser& parser, const struct Parser::CFIInfoArray&) +void NonLazyPointerSection::makeFixups(class Parser& parser, const struct Parser::CFI_CU_InfoArrays&) { assert(0 && "x86_64 should not have non-lazy-pointer sections in .o files"); } template -void NonLazyPointerSection::makeFixups(class Parser& parser, const struct Parser::CFIInfoArray&) +void NonLazyPointerSection::makeFixups(class Parser& parser, const struct Parser::CFI_CU_InfoArrays&) { // add references for each NLP atom based on indirect symbol table const macho_section

* sect = this->machoSection(); @@ -5192,7 +5470,7 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relocati const macho_nlist

& targetSymbol = parser.symbolFromIndex(reloc->r_symbolnum()); target.name = parser.nameFromSymbol(targetSymbol); target.weakImport = parser.weakImportFromSymbol(targetSymbol); - target.addend = contentValue; + target.addend = (int32_t)contentValue; } else { parser.findTargetFromAddressAndSectionNum(contentValue, reloc->r_symbolnum(), target); @@ -5247,6 +5525,7 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relocati const macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; srcAddr = sect->addr() + sreloc->r_address(); src.atom = this->findAtomByAddress(srcAddr); + assert(src.atom != NULL); src.offsetInAtom = srcAddr - src.atom->_objAddress; fixUpPtr = file().fileContent() + sect->offset() + sreloc->r_address(); uint32_t relocValue = sreloc->r_value(); @@ -5279,17 +5558,17 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relocati switch ( sreloc->r_length() ) { case 0: contentValue = srcAddr + 1 + *fixUpPtr; - target.addend = contentValue - relocValue; + target.addend = (int32_t)contentValue - (int32_t)relocValue; parser.addFixups(src, ld::Fixup::kindStoreX86PCRel8, target); break; case 1: contentValue = srcAddr + 2 + LittleEndian::get16(*((uint16_t*)fixUpPtr)); - target.addend = contentValue - relocValue; + target.addend = (int32_t)contentValue - (int32_t)relocValue; parser.addFixups(src, ld::Fixup::kindStoreX86PCRel16, target); break; case 2: contentValue = srcAddr + 4 + LittleEndian::get32(*((uint32_t*)fixUpPtr)); - target.addend = contentValue - relocValue; + target.addend = (int32_t)contentValue - (int32_t)relocValue; parser.addFixups(src, ld::Fixup::kindStoreX86PCRel32, target); break; case 3: @@ -5301,7 +5580,7 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relocati if ( sreloc->r_length() != 2 ) throwf("unsupported r_length=%d for scattered vanilla reloc", sreloc->r_length()); contentValue = LittleEndian::get32(*((uint32_t*)fixUpPtr)); - target.addend = contentValue - target.atom->objectAddress(); + target.addend = (int32_t)contentValue - (int32_t)(target.atom->objectAddress()); parser.addFixups(src, ld::Fixup::kindStoreLittleEndian32, target); } break; @@ -5327,7 +5606,7 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relocati uint32_t offsetInFrom = nextRelocValue - fromAtom->_objAddress; parser.findTargetFromAddress(sreloc->r_value(), target); // check for addend encoded in the section content - int32_t addend = contentValue - (sreloc->r_value() - nextRelocValue); + int64_t addend = (int32_t)contentValue - (int32_t)(sreloc->r_value() - nextRelocValue); if ( addend < 0 ) { // switch binding base on coalescing if ( target.atom == NULL ) { @@ -5801,12 +6080,18 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relocati if ( reloc->r_type() != ARM_RELOC_PAIR ) instruction = LittleEndian::get32(*fixUpPtr); if ( reloc->r_extern() ) { - target.atom = NULL; const macho_nlist

& targetSymbol = parser.symbolFromIndex(reloc->r_symbolnum()); - target.name = parser.nameFromSymbol(targetSymbol); - target.weakImport = parser.weakImportFromSymbol(targetSymbol); - if ( ((targetSymbol.n_type() & N_TYPE) == N_SECT) && (targetSymbol.n_desc() & N_ARM_THUMB_DEF) ) - externSymbolIsThumbDef = true; + // use direct reference for local symbols + if ( ((targetSymbol.n_type() & N_TYPE) == N_SECT) && (((targetSymbol.n_type() & N_EXT) == 0) || (parser.nameFromSymbol(targetSymbol)[0] == 'L')) ) { + parser.findTargetFromAddressAndSectionNum(targetSymbol.n_value(), targetSymbol.n_sect(), target); + } + else { + target.atom = NULL; + target.name = parser.nameFromSymbol(targetSymbol); + target.weakImport = parser.weakImportFromSymbol(targetSymbol); + if ( ((targetSymbol.n_type() & N_TYPE) == N_SECT) && (targetSymbol.n_desc() & N_ARM_THUMB_DEF) ) + externSymbolIsThumbDef = true; + } } switch ( reloc->r_type() ) { case ARM_RELOC_BR24: @@ -5892,7 +6177,7 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relocati throw "bad length for ARM_RELOC_VANILLA"; contentValue = LittleEndian::get32(*fixUpPtr); if ( reloc->r_extern() ) { - target.addend = contentValue; + target.addend = (int32_t)contentValue; if ( externSymbolIsThumbDef ) target.addend &= -2; // remove thumb bit } @@ -5939,13 +6224,27 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relocati if ( reloc->r_length() & 1 ) { // high 16 dstAddr = ((instruction16 << 16) | other16); - parser.findTargetFromAddress(dstAddr, target); + if ( reloc->r_extern() ) { + target.addend = dstAddr; + } + else { + parser.findTargetFromAddress(dstAddr, target); + if ( target.atom->isThumb() ) + target.addend &= (-2); // remove thumb bit + } parser.addFixups(src, (isThumb ? ld::Fixup::kindStoreThumbHigh16 : ld::Fixup::kindStoreARMHigh16), target); } else { // low 16 dstAddr = (other16 << 16) | instruction16; - parser.findTargetFromAddress(dstAddr, target); + if ( reloc->r_extern() ) { + target.addend = dstAddr; + } + else { + parser.findTargetFromAddress(dstAddr, target); + if ( target.atom->isThumb() ) + target.addend &= (-2); // remove thumb bit + } parser.addFixups(src, (isThumb ? ld::Fixup::kindStoreThumbLow16 : ld::Fixup::kindStoreARMLow16), target); } result = true; @@ -6052,7 +6351,7 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relocati uint32_t offsetInTarget; Atom* targetAtom = parser.findAtomByAddressOrLocalTargetOfStub(sreloc->r_value(), &offsetInTarget); // check for addend encoded in the section content - int64_t addend = contentValue - (sreloc->r_value() - nextRelocValue); + int64_t addend = (int32_t)contentValue - (int32_t)(sreloc->r_value() - nextRelocValue); if ( targetAtom->isThumb() ) addend &= -2; // remove thumb bit // if reference to LSDA, add group subordinate fixup @@ -6073,7 +6372,6 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relocati else { parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, false, targetAtom->name()); } - parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, targetAtom); parser.addFixup(src, ld::Fixup::k2of5, ld::Fixup::kindAddAddend, offsetInTarget); parser.addFixup(src, ld::Fixup::k3of5, ld::Fixup::kindSubtractTargetAddress, fromAtom); parser.addFixup(src, ld::Fixup::k4of5, ld::Fixup::kindSubtractAddend, offsetInFrom-addend); @@ -6103,8 +6401,6 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relocati uint32_t offsetInFrom = nextRelocValue - fromAtom->_objAddress; Atom* targetAtom = parser.findAtomByAddress(sreloc->r_value()); uint32_t offsetInTarget = sreloc->r_value() - targetAtom->_objAddress; - //if ( targetAtom->isThumb() ) - // addend &= -2; // remove thumb bit uint32_t instruction16; uint32_t other16 = (nextRelocAddress & 0xFFFF); bool isThumb; @@ -6126,6 +6422,8 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relocati dstAddr = ((instruction16 << 16) | other16); else dstAddr = (other16 << 16) | instruction16; + if ( targetAtom->isThumb() ) + dstAddr &= (-2); // remove thumb bit int32_t addend = dstAddr - (sreloc->r_value() - nextRelocValue); if ( targetAtom->scope() == ld::Atom::scopeTranslationUnit ) { parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, targetAtom); @@ -6369,7 +6667,7 @@ bool Objc1ClassReferences::addRelocFixup(class Parser& parser, const m template -void Section::makeFixups(class Parser& parser, const struct Parser::CFIInfoArray&) +void Section::makeFixups(class Parser& parser, const struct Parser::CFI_CU_InfoArrays&) { const macho_section

* sect = this->machoSection(); const macho_relocation_info

* relocs = (macho_relocation_info

*)(file().fileContent() + sect->reloff()); @@ -6380,7 +6678,7 @@ void Section::makeFixups(class Parser& parser, const struct Parser::CFI ++r; // skip next } catch (const char* msg) { - throwf("in section %s,%s reloc %u: %s", sect->segname(), sect->sectname(), r, msg); + throwf("in section %s,%s reloc %u: %s", sect->segname(), Section::makeSectionName(sect), r, msg); } } @@ -6541,6 +6839,9 @@ bool hasObjC2Categories(const uint8_t* fileContent) else if ( mach_o::relocatable::Parser::validFile(fileContent, false, 0) ) { return mach_o::relocatable::Parser::hasObjC2Categories(fileContent); } + else if ( mach_o::relocatable::Parser::validFile(fileContent, false, 0) ) { + return mach_o::relocatable::Parser::hasObjC2Categories(fileContent); + } return false; } diff --git a/src/ld/passes/branch_island.cpp b/src/ld/passes/branch_island.cpp index d42fa54..99b3eb8 100644 --- a/src/ld/passes/branch_island.cpp +++ b/src/ld/passes/branch_island.cpp @@ -312,7 +312,7 @@ static ld::Atom* makeBranchIsland(const Options& opts, ld::Fixup::Kind kind, int case ld::Fixup::kindStoreTargetAddressARMBranch24: case ld::Fixup::kindStoreTargetAddressThumbBranch22: if ( finalTarget.atom->isThumb() ) { - if ( opts.preferSubArchitecture() && opts.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) { + if ( opts.preferSubArchitecture() && opts.archSupportsThumb2() ) { return new Thumb2toThumbBranchIslandAtom(name, nextTarget, finalTarget); } else if ( opts.outputSlidable() ) { @@ -344,7 +344,7 @@ static uint64_t textSizeWhenMightNeedBranchIslands(const Options& opts, bool see case CPU_TYPE_ARM: if ( ! seenThumbBranch ) return 32000000; // ARM can branch +/- 32MB - else if ( opts.preferSubArchitecture() && opts.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) + else if ( opts.preferSubArchitecture() && opts.archSupportsThumb2() ) return 16000000; // thumb2 can branch +/- 16MB else return 4000000; // thumb1 can branch +/- 4MB @@ -365,7 +365,7 @@ static uint64_t maxDistanceBetweenIslands(const Options& opts, bool seenThumbBra case CPU_TYPE_ARM: if ( ! seenThumbBranch ) return 30*1024*1024; // 2MB of branch islands per 32MB - else if ( opts.preferSubArchitecture() && opts.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) + else if ( opts.preferSubArchitecture() && opts.archSupportsThumb2() ) return 14*1024*1024; // 2MB of branch islands per 16MB else return 3500000; // 0.5MB of branch islands per 4MB diff --git a/src/ld/passes/branch_shim.cpp b/src/ld/passes/branch_shim.cpp index 70ebcfc..7a2bd50 100644 --- a/src/ld/passes/branch_shim.cpp +++ b/src/ld/passes/branch_shim.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2010 Apple Inc. All rights reserved. + * Copyright (c) 2010-2011 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -42,16 +42,14 @@ namespace branch_shim { static bool _s_log = false; -static ld::Section _s_text_section("__TEXT", "__text", ld::Section::typeCode); - class Thumb2ToArmShimAtom : public ld::Atom { public: - Thumb2ToArmShimAtom(const ld::Atom* target) - : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + Thumb2ToArmShimAtom(const ld::Atom* target, const ld::Section& inSect) + : ld::Atom(inSect, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, - ld::Atom::symbolTableIn, false, true, false, ld::Atom::Alignment(1)), + ld::Atom::symbolTableIn, false, true, false, ld::Atom::Alignment(2)), _name(NULL), _target(target), _fixup1(8, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, target), @@ -90,13 +88,50 @@ private: }; +class NoPICThumb2ToArmShimAtom : public ld::Atom { +public: + NoPICThumb2ToArmShimAtom(const ld::Atom* target, const ld::Section& inSect) + : ld::Atom(inSect, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, + ld::Atom::symbolTableIn, false, true, false, ld::Atom::Alignment(2)), + _name(NULL), + _target(target), + _fixup1(8, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, target) + { asprintf((char**)&_name, "%s$shim", target->name()); } + + virtual const ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char**) const + { return false; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 12; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + // Use ARM instructions that can jump to thumb. + assert( ! _target->isThumb() ); + if (_s_log) fprintf(stderr, "3 Thumb2 instruction shim to jump to %s\n", _target->name()); + OSWriteLittleInt32(&buffer[0], 0, 0xc004f8df); // ldr ip, pc + 4 + OSWriteLittleInt16(&buffer[4], 0, 0x4760); // bx ip + OSWriteLittleInt16(&buffer[6], 0, 0x46C0); // nop + OSWriteLittleInt32(&buffer[8], 0, 0x00000000); // .long target + } + + 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 char* _name; + const ld::Atom* _target; + ld::Fixup _fixup1; +}; + class Thumb1ToArmShimAtom : public ld::Atom { public: - Thumb1ToArmShimAtom(const ld::Atom* target) - : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + Thumb1ToArmShimAtom(const ld::Atom* target, const ld::Section& inSect) + : ld::Atom(inSect, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, - ld::Atom::symbolTableIn, false, true, false, ld::Atom::Alignment(1)), + ld::Atom::symbolTableIn, false, true, false, ld::Atom::Alignment(2)), _name(NULL), _target(target), _fixup1(12, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, target), @@ -141,8 +176,8 @@ private: class ARMtoThumbShimAtom : public ld::Atom { public: - ARMtoThumbShimAtom(const ld::Atom* target) - : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ARMtoThumbShimAtom(const ld::Atom* target, const ld::Section& inSect) + : ld::Atom(inSect, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), _name(NULL), @@ -182,6 +217,41 @@ private: }; +class NoPICARMtoThumbShimAtom : public ld::Atom { +public: + NoPICARMtoThumbShimAtom(const ld::Atom* target, const ld::Section& inSect) + : ld::Atom(inSect, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, + ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), + _name(NULL), + _target(target), + _fixup1(8, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, target) + { asprintf((char**)&_name, "%s$shim", target->name()); } + + virtual const ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char**) const + { return false; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 12; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + // Use ARM instructions that can jump to thumb. + if (_s_log) fprintf(stderr, "3 ARM instruction shim to jump to %s\n", _target->name()); + OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc000); // ldr ip, pc + 4 + OSWriteLittleInt32(&buffer[ 4], 0, 0xe12fff1c); // bx ip + OSWriteLittleInt32(&buffer[ 8], 0, 0); // .long target + } + 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 char* _name; + const ld::Atom* _target; + ld::Fixup _fixup1; +}; + + @@ -216,10 +286,6 @@ static void extractTarget(ld::Fixup::iterator fixup, ld::Internal& state, const // void doPass(const Options& opts, ld::Internal& state) { - std::map atomToThumbMap; - std::map thumbToAtomMap; - std::vector shims; - // only make branch shims in final linked images if ( opts.outputKind() == Options::kObjectFile ) return; @@ -228,84 +294,108 @@ void doPass(const Options& opts, ld::Internal& state) if ( opts.architecture() != CPU_TYPE_ARM ) return; - // scan to find __text section - ld::Internal::FinalSection* textSection = NULL; + const bool makingKextBundle = (opts.outputKind() == Options::kKextBundle); + + // scan all sections for (std::vector::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; - if ( strcmp(sect->sectionName(), "__text") == 0 ) - textSection = sect; - } - if ( textSection == NULL ) - return; - - // scan __text section for branch instructions that need to switch mode - for (std::vector::iterator ait=textSection->atoms.begin(); ait != textSection->atoms.end(); ++ait) { - const ld::Atom* atom = *ait; - const ld::Atom* target; - for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { - switch ( fit->kind ) { - case ld::Fixup::kindStoreTargetAddressThumbBranch22: - extractTarget(fit, state, &target); - if ( ! target->isThumb() ) { - const uint8_t* fixUpLocation = atom->rawContentPointer() + fit->offsetInAtom; - uint32_t instruction = *((uint32_t*)fixUpLocation); - bool is_b = ((instruction & 0xD000F800) == 0x9000F000); - if ( is_b ) { - fprintf(stderr, "need to add thumb->arm instr=0x%08X shim to %s for %s\n", instruction, target->name(), atom->name()); - const Atom* shim = NULL; - std::map::iterator pos = thumbToAtomMap.find(target); - if ( pos == thumbToAtomMap.end() ) { - if ( opts.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) - shim = new Thumb2ToArmShimAtom(target); - else - shim = new Thumb1ToArmShimAtom(target); - shims.push_back(shim); - thumbToAtomMap[target] = shim; + std::map atomToThumbMap; + std::map thumbToAtomMap; + std::vector shims; + // scan section for branch instructions that need to switch mode + for (std::vector::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + const ld::Atom* target = NULL; + bool targetIsProxy; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + switch ( fit->kind ) { + case ld::Fixup::kindStoreTargetAddressThumbBranch22: + extractTarget(fit, state, &target); + targetIsProxy = (target->definition() == ld::Atom::definitionProxy); + if ( ! target->isThumb() ) { + const uint8_t* fixUpLocation = atom->rawContentPointer(); + // don't try to scan atom for branches if atom unwilling to supply raw content + if ( fixUpLocation == NULL ) + break; + fixUpLocation += fit->offsetInAtom; + uint32_t instruction = *((uint32_t*)fixUpLocation); + bool is_b = ((instruction & 0xD000F800) == 0x9000F000); + // need shim for branch from thumb to arm, or for call to function outside kext + if ( is_b || (targetIsProxy && makingKextBundle) ) { + if ( _s_log ) fprintf(stderr, "need to add thumb->arm instr=0x%08X shim to %s for %s\n", instruction, target->name(), atom->name()); + const Atom* shim = NULL; + std::map::iterator pos = thumbToAtomMap.find(target); + if ( pos == thumbToAtomMap.end() ) { + if ( opts.archSupportsThumb2() ) { + // make long-branch style shims for arm kexts + if ( makingKextBundle ) + shim = new NoPICThumb2ToArmShimAtom(target, *sect); + else + shim = new Thumb2ToArmShimAtom(target, *sect); + } + else { + shim = new Thumb1ToArmShimAtom(target, *sect); + } + shims.push_back(shim); + thumbToAtomMap[target] = shim; + } + else { + shim = pos->second; + } + fit->binding = ld::Fixup::bindingDirectlyBound; + fit->u.target = shim; } - else { - shim = pos->second; - } - fit->binding = ld::Fixup::bindingDirectlyBound; - fit->u.target = shim; } - } - break; - case ld::Fixup::kindStoreTargetAddressARMBranch24: - extractTarget(fit, state, &target); - if ( target->isThumb() ) { - const uint8_t* fixUpLocation = atom->rawContentPointer() + fit->offsetInAtom; - uint32_t instruction = *((uint32_t*)fixUpLocation); - bool is_b = ((instruction & 0x0F000000) == 0x0A000000); - if ( is_b ) { - fprintf(stderr, "need to add arm->thumb instr=0x%08X shim to %s for %s\n", instruction, target->name(), atom->name()); - const Atom* shim = NULL; - std::map::iterator pos = atomToThumbMap.find(target); - if ( pos == atomToThumbMap.end() ) { - shim = new ARMtoThumbShimAtom(target); - shims.push_back(shim); - atomToThumbMap[target] = shim; - } - else { - shim = pos->second; + break; + case ld::Fixup::kindStoreTargetAddressARMBranch24: + extractTarget(fit, state, &target); + targetIsProxy = (target->definition() == ld::Atom::definitionProxy); + if ( target->isThumb() || (targetIsProxy && makingKextBundle) ) { + const uint8_t* fixUpLocation = atom->rawContentPointer(); + // don't try to scan atom for branches if atom unwilling to supply raw content + if ( fixUpLocation == NULL ) + break; + fixUpLocation += fit->offsetInAtom; + uint32_t instruction = *((uint32_t*)fixUpLocation); + bool is_b = ((instruction & 0x0F000000) == 0x0A000000) && ((instruction & 0xF0000000) != 0xF0000000); + // need shim for branch from arm to thumb, or for call to function outside kext + if ( is_b || (targetIsProxy && makingKextBundle) ) { + if ( _s_log ) fprintf(stderr, "need to add arm->thumb instr=0x%08X shim to %s for %s\n", instruction, target->name(), atom->name()); + const Atom* shim = NULL; + std::map::iterator pos = atomToThumbMap.find(target); + if ( pos == atomToThumbMap.end() ) { + // make long-branch style shims for arm kexts + if ( makingKextBundle ) + shim = new NoPICARMtoThumbShimAtom(target, *sect); + else + shim = new ARMtoThumbShimAtom(target, *sect); + shims.push_back(shim); + atomToThumbMap[target] = shim; + } + else { + shim = pos->second; + } + fit->binding = ld::Fixup::bindingDirectlyBound; + fit->u.target = shim; } - fit->binding = ld::Fixup::bindingDirectlyBound; - fit->u.target = shim; } - } - break; - - case ld::Fixup::kindStoreARMBranch24: - case ld::Fixup::kindStoreThumbBranch22: - fprintf(stderr, "found branch-22 without store in %s\n", atom->name()); - break; - default: - break; + break; + + //case ld::Fixup::kindStoreARMBranch24: + //case ld::Fixup::kindStoreThumbBranch22: + // Note: these fixups will only be seen if the the b/bl is to a symbol plus addend + // for now we don't handle making shims. If a shim is needed there will + // be an error later. + // break; + default: + break; + } } } - } - // append all new shims to end of __text - textSection->atoms.insert(textSection->atoms.end(), shims.begin(), shims.end()); + // append all new shims to end of __text + sect->atoms.insert(sect->atoms.end(), shims.begin(), shims.end()); + } } diff --git a/src/ld/passes/compact_unwind.cpp b/src/ld/passes/compact_unwind.cpp index 5b9434e..86c8eec 100644 --- a/src/ld/passes/compact_unwind.cpp +++ b/src/ld/passes/compact_unwind.cpp @@ -706,6 +706,11 @@ static void getAllUnwindInfos(const ld::Internal& state, std::vectorbinding == ld::Fixup::bindingDirectlyBound); lsda = fit->u.target; break; + case ld::Fixup::kindNoneGroupSubordinatePersonality: + assert(fit->binding == ld::Fixup::bindingDirectlyBound); + personalityPointer = fit->u.target; + assert(personalityPointer->section().type() == ld::Section::typeNonLazyPointer); + break; default: break; } @@ -753,16 +758,8 @@ static void getAllUnwindInfos(const ld::Internal& state, std::vector entries; entries.reserve(64); @@ -789,6 +786,146 @@ void doPass(const Options& opts, ld::Internal& state) } + +template +class CompactUnwindAtom : public ld::Atom { +public: + CompactUnwindAtom(ld::Internal& state,const ld::Atom* funcAtom, + uint32_t startOffset, uint32_t len, uint32_t cui); + ~CompactUnwindAtom() {} + + virtual const ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char**) const + { return false; } + virtual const char* name() const { return "compact unwind info"; } + virtual uint64_t size() const { return sizeof(macho_compact_unwind_entry

); } + 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*)&_fixups[0]; } + virtual ld::Fixup::iterator fixupsEnd() const { return (ld::Fixup*)&_fixups[_fixups.size()]; } + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + + const ld::Atom* _atom; + const uint32_t _startOffset; + const uint32_t _len; + const uint32_t _compactUnwindInfo; + std::vector _fixups; + + static ld::Fixup::Kind _s_pointerKind; + static ld::Fixup::Kind _s_pointerStoreKind; + static ld::Section _s_section; +}; + + +template +ld::Section CompactUnwindAtom::_s_section("__LD", "__compact_unwind", ld::Section::typeDebug); + +template <> ld::Fixup::Kind CompactUnwindAtom::_s_pointerKind = ld::Fixup::kindStoreLittleEndian32; +template <> ld::Fixup::Kind CompactUnwindAtom::_s_pointerStoreKind = ld::Fixup::kindStoreTargetAddressLittleEndian32; +template <> ld::Fixup::Kind CompactUnwindAtom::_s_pointerKind = ld::Fixup::kindStoreLittleEndian64; +template <> ld::Fixup::Kind CompactUnwindAtom::_s_pointerStoreKind = ld::Fixup::kindStoreTargetAddressLittleEndian64; + +template +CompactUnwindAtom::CompactUnwindAtom(ld::Internal& state,const ld::Atom* funcAtom, uint32_t startOffset, + uint32_t len, uint32_t cui) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, ld::Atom::typeUnclassified, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)), + _atom(funcAtom), _startOffset(startOffset), _len(len), _compactUnwindInfo(cui) +{ + _fixups.push_back(ld::Fixup(macho_compact_unwind_entry

::codeStartFieldOffset(), ld::Fixup::k1of3, ld::Fixup::kindSetTargetAddress, funcAtom)); + _fixups.push_back(ld::Fixup(macho_compact_unwind_entry

::codeStartFieldOffset(), ld::Fixup::k2of3, ld::Fixup::kindAddAddend, _startOffset)); + _fixups.push_back(ld::Fixup(macho_compact_unwind_entry

::codeStartFieldOffset(), ld::Fixup::k3of3, _s_pointerKind)); + // see if atom has subordinate personality function or lsda + for (ld::Fixup::iterator fit = funcAtom->fixupsBegin(), end=funcAtom->fixupsEnd(); fit != end; ++fit) { + switch ( fit->kind ) { + case ld::Fixup::kindNoneGroupSubordinatePersonality: + assert(fit->binding == ld::Fixup::bindingsIndirectlyBound); + _fixups.push_back(ld::Fixup(macho_compact_unwind_entry

::personalityFieldOffset(), ld::Fixup::k1of1, _s_pointerStoreKind, state.indirectBindingTable[fit->u.bindingIndex])); + break; + case ld::Fixup::kindNoneGroupSubordinateLSDA: + assert(fit->binding == ld::Fixup::bindingDirectlyBound); + _fixups.push_back(ld::Fixup(macho_compact_unwind_entry

::lsdaFieldOffset(), ld::Fixup::k1of1, _s_pointerStoreKind, fit->u.target)); + break; + default: + break; + } + } + +} + +template +void CompactUnwindAtom::copyRawContent(uint8_t buffer[]) const +{ + macho_compact_unwind_entry

* buf = (macho_compact_unwind_entry

*)buffer; + buf->set_codeStart(0); + buf->set_codeLen(_len); + buf->set_compactUnwindInfo(_compactUnwindInfo); + buf->set_personality(0); + buf->set_lsda(0); +} + + +static void makeCompactUnwindAtom(const Options& opts, ld::Internal& state, const ld::Atom* atom, + uint32_t startOffset, uint32_t endOffset, uint32_t cui) +{ + switch ( opts.architecture() ) { + case CPU_TYPE_X86_64: + state.addAtom(*new CompactUnwindAtom(state, atom, startOffset, endOffset-startOffset, cui)); + break; + case CPU_TYPE_I386: + state.addAtom(*new CompactUnwindAtom(state, atom, startOffset, endOffset-startOffset, cui)); + break; + } +} + +static void makeRelocateableCompactUnwindSection(const Options& opts, ld::Internal& state) +{ + // can't add CompactUnwindAtom atoms will iterating, so pre-scan + std::vector atomsWithUnwind; + for (std::vector::const_iterator sit=state.sections.begin(); sit != state.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->beginUnwind() != atom->endUnwind() ) + atomsWithUnwind.push_back(atom); + } + } + // make one CompactUnwindAtom for each compact unwind range in each atom + for (std::vector::iterator it = atomsWithUnwind.begin(); it != atomsWithUnwind.end(); ++it) { + const ld::Atom* atom = *it; + uint32_t lastOffset = 0; + uint32_t lastCUE = 0; + bool first = true; + for (ld::Atom::UnwindInfo::iterator uit=atom->beginUnwind(); uit != atom->endUnwind(); ++uit) { + if ( !first ) { + makeCompactUnwindAtom(opts, state, atom, lastOffset, uit->startOffset, lastCUE); + } + lastOffset = uit->startOffset; + lastCUE = uit->unwindInfo; + first = false; + } + makeCompactUnwindAtom(opts, state, atom, lastOffset, (uint32_t)atom->size(), lastCUE); + } +} + + +void doPass(const Options& opts, ld::Internal& state) +{ + if ( opts.outputKind() == Options::kObjectFile ) + makeRelocateableCompactUnwindSection(opts, state); + + else if ( opts.needsUnwindInfoSection() ) + makeFinalLinkedImageCompactUnwindSection(opts, state); +} + + } // namespace compact_unwind } // namespace passes } // namespace ld diff --git a/src/ld/passes/dylibs.cpp b/src/ld/passes/dylibs.cpp index 2b4452b..47b1647 100644 --- a/src/ld/passes/dylibs.cpp +++ b/src/ld/passes/dylibs.cpp @@ -72,12 +72,61 @@ void doPass(const Options& opts, ld::Internal& state) // set "willRemoved" bit on any unused explicit when -dead_strip_dylibs is used if ( opts.deadStripDylibs() && !aDylib->providedExportAtom() ) aDylib->setWillBeRemoved(true); - } - - + } // remove unused dylibs state.dylibs.erase(std::remove_if(state.dylibs.begin(), state.dylibs.end(), WillBeUsed()), state.dylibs.end()); + + // automatically weak-import dylibs when all symbols from it are weak-imported + for (std::vector::iterator sit=state.sections.begin(); sit != state.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; + const ld::Atom* target = NULL; + bool targetIsWeakImport = false; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + if ( fit->firstInCluster() ) + target = NULL; + switch ( fit->binding ) { + case ld::Fixup::bindingsIndirectlyBound: + target = state.indirectBindingTable[fit->u.bindingIndex]; + targetIsWeakImport = fit->weakImport; + break; + case ld::Fixup::bindingDirectlyBound: + target = fit->u.target; + targetIsWeakImport = fit->weakImport; + break; + default: + break; + } + if ( (target != NULL) && (target->definition() == ld::Atom::definitionProxy) ) { + ld::Atom::WeakImportState curWI = target->weakImportState(); + if ( curWI == ld::Atom::weakImportUnset ) { + // first use of this proxy, set weak-import based on this usage + (const_cast(target))->setWeakImportState(targetIsWeakImport); + } + else { + // proxy already has weak-importness set, check for weakness mismatch + bool curIsWeakImport = (curWI == ld::Atom::weakImportTrue); + if ( curIsWeakImport != targetIsWeakImport ) { + // found mismatch + switch ( opts.weakReferenceMismatchTreatment() ) { + case Options::kWeakReferenceMismatchError: + throwf("mismatching weak references for symbol: %s", target->name()); + case Options::kWeakReferenceMismatchWeak: + (const_cast(target))->setWeakImportState(true); + break; + case Options::kWeakReferenceMismatchNonWeak: + (const_cast(target))->setWeakImportState(false); + break; + } + } + } + } + } + } + } + } diff --git a/src/ld/passes/got.cpp b/src/ld/passes/got.cpp index ff18e00..bb94ddf 100644 --- a/src/ld/passes/got.cpp +++ b/src/ld/passes/got.cpp @@ -105,6 +105,9 @@ static bool gotFixup(const Options& opts, ld::Internal& internal, const ld::Atom case ld::Fixup::kindStoreX86PCRel32GOT: *optimizable = false; return true; + case ld::Fixup::kindNoneGroupSubordinatePersonality: + *optimizable = false; + return true; default: break; } @@ -128,8 +131,8 @@ void doPass(const Options& opts, ld::Internal& internal) if ( opts.outputKind() == Options::kObjectFile ) return; - // walk all atoms and fixups looking for stubable references - // don't create stubs inline because that could invalidate the sections walk + // walk all atoms and fixups looking for GOT-able references + // don't create GOT atoms during this loop because that could invalidate the sections iterator std::vector atomsReferencingGOT; std::map gotMap; std::map weakImportMap; @@ -140,15 +143,18 @@ void doPass(const Options& opts, ld::Internal& internal) const ld::Atom* atom = *ait; bool atomUsesGOT = false; const ld::Atom* targetOfGOT = NULL; + bool targetIsWeakImport = false; for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { if ( fit->firstInCluster() ) targetOfGOT = NULL; switch ( fit->binding ) { case ld::Fixup::bindingsIndirectlyBound: targetOfGOT = internal.indirectBindingTable[fit->u.bindingIndex]; + targetIsWeakImport = fit->weakImport; break; case ld::Fixup::bindingDirectlyBound: targetOfGOT = fit->u.target; + targetIsWeakImport = fit->weakImport; break; default: break; @@ -183,19 +189,12 @@ void doPass(const Options& opts, ld::Internal& internal) std::map::iterator pos = weakImportMap.find(targetOfGOT); if ( pos == weakImportMap.end() ) { // target not in weakImportMap, so add - weakImportMap[targetOfGOT] = fit->weakImport; - // If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB - const ld::dylib::File* dylib = dynamic_cast(targetOfGOT->file()); - if ( dylib != NULL ) { - if ( fit->weakImport ) - (const_cast(dylib))->setUsingWeakImportedSymbols(); - else - (const_cast(dylib))->setUsingNonWeakImportedSymbols(); - } + if ( log ) fprintf(stderr, "weakImportMap[%s] = %d\n", targetOfGOT->name(), targetIsWeakImport); + weakImportMap[targetOfGOT] = targetIsWeakImport; } else { // target in weakImportMap, check for weakness mismatch - if ( pos->second != fit->weakImport ) { + if ( pos->second != targetIsWeakImport ) { // found mismatch switch ( opts.weakReferenceMismatchTreatment() ) { case Options::kWeakReferenceMismatchError: diff --git a/src/ld/passes/objc.cpp b/src/ld/passes/objc.cpp index f420b9f..ed59193 100644 --- a/src/ld/passes/objc.cpp +++ b/src/ld/passes/objc.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2010 Apple Inc. All rights reserved. + * Copyright (c) 2010-2011 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -918,6 +918,7 @@ MethodListAtom::MethodListAtom(ld::Internal& state, const ld::Atom* baseMetho symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), _file(NULL), _methodCount(0) { unsigned int fixupCount = 0; + std::set baseMethodListMethodNameAtoms; // if base class has method list, then associate new method list with file defining class if ( baseMethodList != NULL ) { _file = baseMethodList->file(); @@ -925,6 +926,14 @@ MethodListAtom::MethodListAtom(ld::Internal& state, const ld::Atom* baseMetho _methodCount = MethodList::count(state, baseMethodList); deadAtoms.insert(baseMethodList); fixupCount = baseMethodList->fixupsEnd() - baseMethodList->fixupsBegin(); + for (ld::Fixup::iterator fit=baseMethodList->fixupsBegin(); fit != baseMethodList->fixupsEnd(); ++fit) { + if ( (fit->offsetInAtom - 8) % (3*sizeof(pint_t)) == 0 ) { + assert(fit->binding == ld::Fixup::bindingsIndirectlyBound && "malformed method list"); + const ld::Atom* target = state.indirectBindingTable[fit->u.bindingIndex]; + assert(target->contentType() == ld::Atom::typeCString && "malformed method list"); + baseMethodListMethodNameAtoms.insert(target); + } + } } for (std::vector::const_iterator ait=categories->begin(); ait != categories->end(); ++ait) { const ld::Atom* categoryMethodListAtom; @@ -950,6 +959,7 @@ MethodListAtom::MethodListAtom(ld::Internal& state, const ld::Atom* baseMetho // copy fixups and adjust offsets (in reverse order to simulator objc runtime) _fixups.reserve(fixupCount); uint32_t slide = 0; + std::set categoryMethodNameAtoms; for (std::vector::const_reverse_iterator rit=categories->rbegin(); rit != categories->rend(); ++rit) { const ld::Atom* categoryMethodListAtom; if ( meta ) @@ -961,8 +971,24 @@ MethodListAtom::MethodListAtom(ld::Internal& state, const ld::Atom* baseMetho ld::Fixup fixup = *fit; fixup.offsetInAtom += slide; _fixups.push_back(fixup); - //if ( fixup.binding == ld::Fixup::bindingDirectlyBound ) - // fprintf(stderr, "offset=0x%08X, name=%s\n", fixup.offsetInAtom, fixup.u.target->name()); + if ( (fixup.offsetInAtom - 8) % (3*sizeof(pint_t)) == 0 ) { + // warning when a method is overridden in a category in the same link unit + assert(fixup.binding == ld::Fixup::bindingsIndirectlyBound && "malformed category method list"); + const ld::Atom* target = state.indirectBindingTable[fixup.u.bindingIndex]; + assert(target->contentType() == ld::Atom::typeCString && "malformed method list"); + // this objc pass happens after cstrings are coalesced, so we can just compare the atom addres instead of its content + if ( baseMethodListMethodNameAtoms.count(target) != 0 ) { + warning("%s method '%s' in category from %s overrides method from class in %s", + (meta ? "meta" : "instance"), target->rawContentPointer(), + categoryMethodListAtom->file()->path(), baseMethodList->file()->path() ); + } + if ( categoryMethodNameAtoms.count(target) != 0 ) { + warning("%s method '%s' in category from %s conflicts with same method from another category", + (meta ? "meta" : "instance"), target->rawContentPointer(), + categoryMethodListAtom->file()->path()); + } + categoryMethodNameAtoms.insert(target); + } } slide += 3*sizeof(pint_t) * MethodList::count(state, categoryMethodListAtom); } diff --git a/src/ld/passes/order_file.cpp b/src/ld/passes/order_file.cpp index 9e336ae..5e814bd 100644 --- a/src/ld/passes/order_file.cpp +++ b/src/ld/passes/order_file.cpp @@ -274,13 +274,25 @@ void Layout::buildNameTable() if ( pos == _nameTable.end() ) _nameTable[name] = atom; else { - _nameTable[name] = NULL; // collision, denote with NULL + const ld::Atom* existing = _nameTable[name]; + if ( existing != NULL ) { + _nameCollisionAtoms.push_back(existing); + _nameTable[name] = NULL; // collision, denote with NULL + } _nameCollisionAtoms.push_back(atom); } } } } } + if ( _s_log ) { + fprintf(stderr, "buildNameTable() _nameTable:\n"); + for(NameToAtom::iterator it=_nameTable.begin(); it != _nameTable.end(); ++it) + fprintf(stderr, " %p <- %s\n", it->second, it->first); + fprintf(stderr, "buildNameTable() _nameCollisionAtoms:\n"); + for(std::vector::iterator it=_nameCollisionAtoms.begin(); it != _nameCollisionAtoms.end(); ++it) + fprintf(stderr, " %p, %s\n", *it, (*it)->name()); + } } @@ -295,13 +307,14 @@ const ld::Atom* Layout::findAtom(const Options::OrderedSymbol& orderedSymbol) } if ( pos->second == NULL ) { // name is in hash table, but atom is NULL, so that means there are duplicates, so we use super slow way + if ( ( orderedSymbol.objectFileName == NULL) && _options.printOrderFileStatistics() ) { + warning("%s specified in order_file but it exists in multiple .o files. " + "Prefix symbol with .o filename in order_file to disambiguate", orderedSymbol.symbolName); + } for (std::vector::iterator it=_nameCollisionAtoms.begin(); it != _nameCollisionAtoms.end(); it++) { const ld::Atom* atom = *it; if ( strcmp(atom->name(), orderedSymbol.symbolName) == 0 ) { if ( matchesObjectFile(atom, orderedSymbol.objectFileName) ) { - if ( _options.printOrderFileStatistics() ) - warning("%s specified in order_file but it exists in multiple .o files. " - "Prefix symbol with .o filename in order_file to disambiguate", orderedSymbol.symbolName); return atom; } } diff --git a/src/ld/passes/stubs/stubs.cpp b/src/ld/passes/stubs/stubs.cpp index 0e23c27..274878b 100644 --- a/src/ld/passes/stubs/stubs.cpp +++ b/src/ld/passes/stubs/stubs.cpp @@ -171,6 +171,11 @@ ld::Atom* Pass::makeStub(const ld::Atom& target, bool weakImport) forLazyDylib = true; bool stubToResolver = (target.contentType() == ld::Atom::typeResolver); + if ( usingCompressedLINKEDIT() && !forLazyDylib ) { + if ( _internal->compressedFastBinderProxy == NULL ) + throwf("symbol dyld_stub_binder not found (normally in libSystem.dylib). Needed to perform lazy binding to function %s", target.name()); + } + switch ( _architecture ) { case CPU_TYPE_POWERPC: if ( _pic ) @@ -275,14 +280,6 @@ void Pass::process(ld::Internal& state) if ( pos == weakImportMap.end() ) { // target not in weakImportMap, so add weakImportMap[stubableTargetOfFixup] = fit->weakImport; - // If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB - const ld::dylib::File* dylib = dynamic_cast(stubableTargetOfFixup->file()); - if ( dylib != NULL ) { - if ( fit->weakImport ) - (const_cast(dylib))->setUsingWeakImportedSymbols(); - else - (const_cast(dylib))->setUsingNonWeakImportedSymbols(); - } } else { // target in weakImportMap, check for weakness mismatch @@ -329,9 +326,30 @@ void Pass::process(ld::Internal& state) if ( !_options.makeCompressedDyldInfo() && (state.classicBindingHelper == NULL) ) throw "symbol dyld_stub_binding_helper not found, normally in crt1.o/dylib1.o/bundle1.o"; - // disable close stubs when branch islands might be needed - if ( (_architecture == CPU_TYPE_ARM) && (codeSize > 4*1024*1024) ) - _largeText = true; + // disable arm close stubs in some cases + if ( _architecture == CPU_TYPE_ARM ) { + if ( codeSize > 4*1024*1024 ) + _largeText = true; + else { + for (std::vector::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() == ld::Section::typeMachHeader ) + continue; + if ( strcmp(sect->segmentName(), "__TEXT") == 0) { + for (std::vector::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + if ( atom->alignment().powerOf2 > 10 ) { + // overaligned section means might not be able to keep closestubs sect pushed to end of __TEXT + //warning("alignment 1<<%d in atom %s in section %s disables close stubs optimization", + // atom->alignment().powerOf2, atom->name(), sect->segmentName()); + _largeText = true; + break; + } + } + } + } + } + } // make stub atoms for (std::map::iterator it = stubFor.begin(); it != stubFor.end(); ++it) { diff --git a/src/ld/passes/tlvp.cpp b/src/ld/passes/tlvp.cpp index f40d018..a102e97 100644 --- a/src/ld/passes/tlvp.cpp +++ b/src/ld/passes/tlvp.cpp @@ -172,14 +172,6 @@ void doPass(const Options& opts, ld::Internal& internal) if ( pos == weakImportMap.end() ) { // target not in weakImportMap, so add weakImportMap[it->targetOfTLV] = it->fixupWithTarget->weakImport; - // If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB - const ld::dylib::File* dylib = dynamic_cast(it->targetOfTLV->file()); - if ( dylib != NULL ) { - if ( it->fixupWithTarget->weakImport ) - (const_cast(dylib))->setUsingWeakImportedSymbols(); - else - (const_cast(dylib))->setUsingNonWeakImportedSymbols(); - } } else { // target in weakImportMap, check for weakness mismatch diff --git a/src/other/ObjectDump.cpp b/src/other/ObjectDump.cpp index abf7162..3d7ac5e 100644 --- a/src/other/ObjectDump.cpp +++ b/src/other/ObjectDump.cpp @@ -42,8 +42,9 @@ static bool sNMmode = false; static bool sShowSection = true; static bool sShowDefinitionKind = true; static bool sShowCombineKind = true; +static bool sShowLineInfo = true; -static cpu_type_t sPreferredArch = CPU_TYPE_I386; +static cpu_type_t sPreferredArch = 0xFFFFFFFF; static cpu_subtype_t sPreferredSubArch = 0xFFFFFFFF; static const char* sMatchName = NULL; static int sPrintRestrict; @@ -966,7 +967,20 @@ void dumper::dumpAtom(const ld::Atom& atom) if ( sShowSection ) printf("section: %s,%s\n", atom.section().segmentName(), atom.section().sectionName()); if ( atom.beginUnwind() != atom.endUnwind() ) { - printf("unwind: 0x%08X\n", atom.beginUnwind()->unwindInfo); + uint32_t lastOffset = 0; + uint32_t lastCUE = 0; + bool first = true; + const char* label = "unwind:"; + for (ld::Atom::UnwindInfo::iterator it=atom.beginUnwind(); it != atom.endUnwind(); ++it) { + if ( !first ) { + printf("%s 0x%08X -> 0x%08X: 0x%08X\n", label, lastOffset, it->startOffset, lastCUE); + label = " "; + } + lastOffset = it->startOffset; + lastCUE = it->unwindInfo; + first = false; + } + printf("%s 0x%08X -> 0x%08X: 0x%08X\n", label, lastOffset, (uint32_t)atom.size(), lastCUE); } if ( atom.contentType() == ld::Atom::typeCString ) { uint8_t buffer[atom.size()+2]; @@ -1028,13 +1042,15 @@ void dumper::dumpAtom(const ld::Atom& atom) } } } - if ( atom.beginLineInfo() != atom.endLineInfo() ) { - printf("line info:\n"); - for (ld::Atom::LineInfo::iterator it = atom.beginLineInfo(); it != atom.endLineInfo(); ++it) { - printf(" offset 0x%04X, line %d, file %s\n", it->atomOffset, it->lineNumber, it->fileName); + if ( sShowLineInfo ) { + if ( atom.beginLineInfo() != atom.endLineInfo() ) { + printf("line info:\n"); + for (ld::Atom::LineInfo::iterator it = atom.beginLineInfo(); it != atom.endLineInfo(); ++it) { + printf(" offset 0x%04X, line %d, file %s\n", it->atomOffset, it->lineNumber, it->fileName); + } } } - + printf("\n"); } @@ -1088,14 +1104,27 @@ static ld::relocatable::File* createReader(const char* path) if ( mh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { const struct fat_header* fh = (struct fat_header*)p; const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); - for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { - if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)sPreferredArch ) { - if ( ((uint32_t)sPreferredSubArch == 0xFFFFFFFF) || ((uint32_t)sPreferredSubArch == OSSwapBigToHostInt32(archs[i].cpusubtype)) ) { - p = p + OSSwapBigToHostInt32(archs[i].offset); - mh = (struct mach_header*)p; - fileLen = OSSwapBigToHostInt32(archs[i].size); - foundFatSlice = true; - break; + if ( (uint32_t)sPreferredArch == 0xFFFFFFFF ) { + // just dump first slice of fat .o file + if ( OSSwapBigToHostInt32(fh->nfat_arch) > 0 ) { + p = p + OSSwapBigToHostInt32(archs[0].offset); + mh = (struct mach_header*)p; + fileLen = OSSwapBigToHostInt32(archs[0].size); + sPreferredArch = OSSwapBigToHostInt32(archs[0].cputype); + sPreferredSubArch = OSSwapBigToHostInt32(archs[0].cpusubtype); + foundFatSlice = true; + } + } + else { + for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { + if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)sPreferredArch ) { + if ( ((uint32_t)sPreferredSubArch == 0xFFFFFFFF) || ((uint32_t)sPreferredSubArch == OSSwapBigToHostInt32(archs[i].cpusubtype)) ) { + p = p + OSSwapBigToHostInt32(archs[i].offset); + mh = (struct mach_header*)p; + fileLen = OSSwapBigToHostInt32(archs[i].size); + foundFatSlice = true; + break; + } } } } @@ -1186,6 +1215,9 @@ int main(int argc, const char* argv[]) else if ( strcmp(arg, "-no_combine") == 0 ) { sShowCombineKind = false; } + else if ( strcmp(arg, "-no_line_info") == 0 ) { + 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; + } + } + if ( !found ) + throwf("unknown architecture %s", arch); } - else - throwf("unknown architecture %s", arch); } else if ( strcmp(arg, "-only") == 0 ) { sMatchName = ++i* make(const uint8_t* fileContent, uint32_t fileLength, const char* path) - { return new DyldInfoPrinter(fileContent, fileLength, path); } + static DyldInfoPrinter* make(const uint8_t* fileContent, uint32_t fileLength, const char* path, bool printArch) + { return new DyldInfoPrinter(fileContent, fileLength, path, printArch); } virtual ~DyldInfoPrinter() {} @@ -91,7 +91,7 @@ private: typedef __gnu_cxx::hash_set, CStringEquals> StringSet; - DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path); + DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path, bool printArch); void printRebaseInfo(); void printRebaseInfoOpcodes(); void printBindingInfo(); @@ -246,7 +246,7 @@ bool DyldInfoPrinter::validFile(const uint8_t* fileContent) } template -DyldInfoPrinter::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path) +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), @@ -336,6 +336,28 @@ DyldInfoPrinter::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLen 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; + } + } + } + } + + if ( printRebase ) { if ( fInfo != NULL ) printRebaseInfo(); @@ -1428,6 +1450,60 @@ const uint8_t* DyldInfoPrinter::printSharedRegionInfoForEachULEB128Address(co case 4: kindStr = "32-bit offset to IMPORT"; break; + case 5: + kindStr = "thumb2 movw"; + break; + case 6: + kindStr = "ARM movw"; + break; + case 0x10: + kindStr = "thumb2 movt low high 4 bits=0"; + break; + case 0x11: + kindStr = "thumb2 movt low high 4 bits=1"; + break; + case 0x12: + kindStr = "thumb2 movt low high 4 bits=2"; + break; + case 0x13: + kindStr = "thumb2 movt low high 4 bits=3"; + break; + case 0x14: + kindStr = "thumb2 movt low high 4 bits=4"; + break; + case 0x15: + kindStr = "thumb2 movt low high 4 bits=5"; + break; + case 0x16: + kindStr = "thumb2 movt low high 4 bits=6"; + break; + case 0x17: + kindStr = "thumb2 movt low high 4 bits=7"; + break; + case 0x18: + kindStr = "thumb2 movt low high 4 bits=8"; + break; + case 0x19: + kindStr = "thumb2 movt low high 4 bits=9"; + break; + case 0x1A: + kindStr = "thumb2 movt low high 4 bits=0xA"; + break; + case 0x1B: + kindStr = "thumb2 movt low high 4 bits=0xB"; + break; + case 0x1C: + kindStr = "thumb2 movt low high 4 bits=0xC"; + break; + case 0x1D: + kindStr = "thumb2 movt low high 4 bits=0xD"; + break; + case 0x1E: + kindStr = "thumb2 movt low high 4 bits=0xE"; + break; + case 0x1F: + kindStr = "thumb2 movt low high 4 bits=0xF"; + break; } uint64_t address = 0; uint64_t delta = 0; @@ -1723,22 +1799,32 @@ void DyldInfoPrinter::printSymbolTableExportInfo() template const char* DyldInfoPrinter::symbolNameForAddress(uint64_t addr) { - // find exact match in globals - const macho_nlist

* lastExport = &fSymbols[fDynamicSymbolTable->iextdefsym()+fDynamicSymbolTable->nextdefsym()]; - for (const macho_nlist

* sym = &fSymbols[fDynamicSymbolTable->iextdefsym()]; sym < lastExport; ++sym) { - if ( (sym->n_value() == addr) && ((sym->n_type() & N_TYPE) == N_SECT) && ((sym->n_type() & N_STAB) == 0) ) { - return &fStrings[sym->n_strx()]; + if ( fDynamicSymbolTable != NULL ) { + // find exact match in globals + const macho_nlist

* lastExport = &fSymbols[fDynamicSymbolTable->iextdefsym()+fDynamicSymbolTable->nextdefsym()]; + for (const macho_nlist

* sym = &fSymbols[fDynamicSymbolTable->iextdefsym()]; sym < lastExport; ++sym) { + if ( (sym->n_value() == addr) && ((sym->n_type() & N_TYPE) == N_SECT) && ((sym->n_type() & N_STAB) == 0) ) { + return &fStrings[sym->n_strx()]; + } + } + // find exact match in local symbols + const macho_nlist

* lastLocal = &fSymbols[fDynamicSymbolTable->ilocalsym()+fDynamicSymbolTable->nlocalsym()]; + for (const macho_nlist

* sym = &fSymbols[fDynamicSymbolTable->ilocalsym()]; sym < lastLocal; ++sym) { + if ( (sym->n_value() == addr) && ((sym->n_type() & N_TYPE) == N_SECT) && ((sym->n_type() & N_STAB) == 0) ) { + return &fStrings[sym->n_strx()]; + } } } - // find exact match in local symbols - const macho_nlist

* lastLocal = &fSymbols[fDynamicSymbolTable->ilocalsym()+fDynamicSymbolTable->nlocalsym()]; - for (const macho_nlist

* sym = &fSymbols[fDynamicSymbolTable->ilocalsym()]; sym < lastLocal; ++sym) { - if ( (sym->n_value() == addr) && ((sym->n_type() & N_TYPE) == N_SECT) && ((sym->n_type() & N_STAB) == 0) ) { - return &fStrings[sym->n_strx()]; + else { + // find exact match in all symbols + const macho_nlist

* lastSym = &fSymbols[fSymbolCount]; + for (const macho_nlist

* sym = &fSymbols[0]; sym < lastSym; ++sym) { + if ( (sym->n_value() == addr) && ((sym->n_type() & N_TYPE) == N_SECT) && ((sym->n_type() & N_STAB) == 0) ) { + return &fStrings[sym->n_strx()]; + } } } - return "?"; } @@ -1889,36 +1975,37 @@ static void dump(const char* path) size_t size = OSSwapBigToHostInt32(archs[i].size); cpu_type_t cputype = OSSwapBigToHostInt32(archs[i].cputype); cpu_type_t cpusubtype = OSSwapBigToHostInt32(archs[i].cpusubtype); - if ( (cputype == sPreferredArch) - && ((sPreferredSubArch==0) || (sPreferredSubArch==cpusubtype)) ) { + if ( ((cputype == sPreferredArch) + && ((sPreferredSubArch==0) || (sPreferredSubArch==cpusubtype))) + || (sPreferredArch == 0) ) { switch(cputype) { case CPU_TYPE_POWERPC: if ( DyldInfoPrinter::validFile(p + offset) ) - DyldInfoPrinter::make(p + offset, size, path); + DyldInfoPrinter::make(p + offset, size, path, (sPreferredArch == 0)); else throw "in universal file, ppc slice does not contain ppc mach-o"; break; case CPU_TYPE_I386: if ( DyldInfoPrinter::validFile(p + offset) ) - DyldInfoPrinter::make(p + offset, size, path); + DyldInfoPrinter::make(p + offset, size, path, (sPreferredArch == 0)); else throw "in universal file, i386 slice does not contain i386 mach-o"; break; case CPU_TYPE_POWERPC64: if ( DyldInfoPrinter::validFile(p + offset) ) - DyldInfoPrinter::make(p + offset, size, path); + DyldInfoPrinter::make(p + offset, size, path, (sPreferredArch == 0)); else throw "in universal file, ppc64 slice does not contain ppc64 mach-o"; break; case CPU_TYPE_X86_64: if ( DyldInfoPrinter::validFile(p + offset) ) - DyldInfoPrinter::make(p + offset, size, path); + DyldInfoPrinter::make(p + offset, size, path, (sPreferredArch == 0)); else throw "in universal file, x86_64 slice does not contain x86_64 mach-o"; break; case CPU_TYPE_ARM: - if ( DyldInfoPrinter::validFile(p + offset) ) - DyldInfoPrinter::make(p + offset, size, path); + 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; @@ -1929,19 +2016,19 @@ static void dump(const char* path) } } else if ( DyldInfoPrinter::validFile(p) ) { - DyldInfoPrinter::make(p, length, path); + DyldInfoPrinter::make(p, length, path, false); } else if ( DyldInfoPrinter::validFile(p) ) { - DyldInfoPrinter::make(p, length, path); + DyldInfoPrinter::make(p, length, path, false); } else if ( DyldInfoPrinter::validFile(p) ) { - DyldInfoPrinter::make(p, length, path); + DyldInfoPrinter::make(p, length, path, false); } else if ( DyldInfoPrinter::validFile(p) ) { - DyldInfoPrinter::make(p, length, path); + DyldInfoPrinter::make(p, length, path, false); } else if ( DyldInfoPrinter::validFile(p) ) { - DyldInfoPrinter::make(p, length, path); + DyldInfoPrinter::make(p, length, path, false); } else { throw "not a known file type"; @@ -1990,26 +2077,19 @@ int main(int argc, const char* argv[]) sPreferredArch = CPU_TYPE_I386; else if ( strcmp(arch, "x86_64") == 0 ) sPreferredArch = CPU_TYPE_X86_64; - else if ( strcmp(arch, "arm") == 0 ) - sPreferredArch = CPU_TYPE_ARM; - else if ( strcmp(arch, "armv4t") == 0 ) { - sPreferredArch = CPU_TYPE_ARM; - sPreferredSubArch = CPU_SUBTYPE_ARM_V4T; - } - else if ( strcmp(arch, "armv5") == 0 ) { - sPreferredArch = CPU_TYPE_ARM; - sPreferredSubArch = CPU_SUBTYPE_ARM_V5TEJ; - } - else if ( strcmp(arch, "armv6") == 0 ) { - sPreferredArch = CPU_TYPE_ARM; - sPreferredSubArch = CPU_SUBTYPE_ARM_V6; - } - else if ( strcmp(arch, "armv7") == 0 ) { - sPreferredArch = CPU_TYPE_ARM; - sPreferredSubArch = CPU_SUBTYPE_ARM_V7; + else { + bool found = false; + for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) { + if ( strcmp(t->subTypeName,arch) == 0 ) { + sPreferredArch = CPU_TYPE_ARM; + sPreferredSubArch = t->subType; + found = true; + break; + } + } + if ( !found ) + throwf("unknown architecture %s", arch); } - else - throwf("unknown architecture %s", arch); } else if ( strcmp(arg, "-rebase") == 0 ) { printRebase = true; diff --git a/src/other/machochecker.cpp b/src/other/machochecker.cpp index 295fa19..4841c7c 100644 --- a/src/other/machochecker.cpp +++ b/src/other/machochecker.cpp @@ -73,6 +73,26 @@ static uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end) return result; } + +static int64_t read_sleb128(const uint8_t*& p, const uint8_t* end) +{ + int64_t result = 0; + int bit = 0; + uint8_t byte; + do { + if (p == end) + throwf("malformed sleb128"); + byte = *p++; + result |= ((byte & 0x7f) << bit); + bit += 7; + } while (byte & 0x80); + // sign extend negative numbers + if ( (byte & 0x40) != 0 ) + result |= (-1LL) << bit; + return result; +} + + template class MachOChecker { @@ -102,6 +122,7 @@ private: void checkSection(const macho_segment_command

* segCmd, const macho_section

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

* reloc); @@ -110,7 +131,13 @@ private: bool addressInWritableSegment(pint_t address); bool hasTextRelocInRange(pint_t start, pint_t end); pint_t segStartAddress(uint8_t segIndex); - + bool addressIsRebaseSite(pint_t addr); + bool addressIsBindingSite(pint_t addr); + pint_t getInitialStackPointer(const macho_thread_command

*); + pint_t getEntryPoint(const macho_thread_command

*); + + + const char* fPath; const macho_header

* fHeader; uint32_t fLength; @@ -126,8 +153,10 @@ private: const macho_relocation_info

* fExternalRelocations; uint32_t fExternalRelocationsCount; bool fWriteableSegmentWithAddrOver4G; + bool fSlidableImage; const macho_segment_command

* fFirstSegment; const macho_segment_command

* fFirstWritableSegment; + const macho_segment_command

* fTEXTSegment; const macho_dyld_info_command

* fDyldInfo; uint32_t fSectionCount; std::vector*>fSegments; @@ -231,11 +260,78 @@ template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x03; } template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x07; } template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x03; } + +template <> +ppc::P::uint_t MachOChecker::getInitialStackPointer(const macho_thread_command* threadInfo) +{ + return threadInfo->thread_register(3); +} + +template <> +ppc64::P::uint_t MachOChecker::getInitialStackPointer(const macho_thread_command* threadInfo) +{ + return threadInfo->thread_register(3); +} + +template <> +x86::P::uint_t MachOChecker::getInitialStackPointer(const macho_thread_command* threadInfo) +{ + return threadInfo->thread_register(7); +} + +template <> +x86_64::P::uint_t MachOChecker::getInitialStackPointer(const macho_thread_command* threadInfo) +{ + return threadInfo->thread_register(7); +} + +template <> +arm::P::uint_t MachOChecker::getInitialStackPointer(const macho_thread_command* threadInfo) +{ + return threadInfo->thread_register(13); +} + + + + + +template <> +ppc::P::uint_t MachOChecker::getEntryPoint(const macho_thread_command* threadInfo) +{ + return threadInfo->thread_register(0); +} + +template <> +ppc64::P::uint_t MachOChecker::getEntryPoint(const macho_thread_command* threadInfo) +{ + return threadInfo->thread_register(0); +} + +template <> +x86::P::uint_t MachOChecker::getEntryPoint(const macho_thread_command* threadInfo) +{ + return threadInfo->thread_register(10); +} + +template <> +x86_64::P::uint_t MachOChecker::getEntryPoint(const macho_thread_command* threadInfo) +{ + return threadInfo->thread_register(16); +} + +template <> +arm::P::uint_t MachOChecker::getEntryPoint(const macho_thread_command* threadInfo) +{ + return threadInfo->thread_register(15); +} + + template MachOChecker::MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path) : fHeader(NULL), fLength(fileLength), fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fDynamicSymbolTable(NULL), fIndirectTableCount(0), fLocalRelocations(NULL), fLocalRelocationsCount(0), fExternalRelocations(NULL), fExternalRelocationsCount(0), - fWriteableSegmentWithAddrOver4G(false), fFirstSegment(NULL), fFirstWritableSegment(NULL), fDyldInfo(NULL), fSectionCount(0) + fWriteableSegmentWithAddrOver4G(false), fSlidableImage(false), fFirstSegment(NULL), fFirstWritableSegment(NULL), + fTEXTSegment(NULL), fDyldInfo(NULL), fSectionCount(0) { // sanity check if ( ! validFile(fileContent) ) @@ -255,6 +351,8 @@ MachOChecker::MachOChecker(const uint8_t* fileContent, uint32_t fileLength, c checkRelocations(); checkSymbolTable(); + + checkInitTerms(); } @@ -270,6 +368,18 @@ void MachOChecker::checkMachHeader() throw "invalid bits in mach_header flags"; if ( (flags & MH_NO_REEXPORTED_DYLIBS) && (fHeader->filetype() != MH_DYLIB) ) throw "MH_NO_REEXPORTED_DYLIBS bit of mach_header flags only valid for dylibs"; + + switch ( fHeader->filetype() ) { + case MH_EXECUTE: + fSlidableImage = ( flags & MH_PIE ); + break; + case MH_DYLIB: + case MH_BUNDLE: + fSlidableImage = true; + break; + default: + throw "not a mach-o file type supported by this tool"; + } } template @@ -277,6 +387,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 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(); @@ -294,7 +405,6 @@ void MachOChecker::checkLoadCommands() switch ( cmd->cmd() ) { case macho_segment_command

::CMD: case LC_SYMTAB: - case LC_UNIXTHREAD: case LC_DYSYMTAB: case LC_LOAD_DYLIB: case LC_ID_DYLIB: @@ -315,6 +425,7 @@ void MachOChecker::checkLoadCommands() case LC_VERSION_MIN_MACOSX: case LC_VERSION_MIN_IPHONEOS: case LC_FUNCTION_STARTS: + case LC_RPATH: break; case LC_DYLD_INFO: case LC_DYLD_INFO_ONLY: @@ -328,6 +439,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_UNIXTHREAD: + if ( fHeader->filetype() != MH_EXECUTE ) + throw "LC_UNIXTHREAD can only be used in MH_EXECUTE file types"; + threadInfo = (macho_thread_command

*)cmd; + break; default: throwf("load command #%d is an unknown kind 0x%X", i, cmd->cmd()); } @@ -339,6 +455,7 @@ void MachOChecker::checkLoadCommands() std::vector > segmentAddressRanges; std::vector > segmentFileOffsetRanges; const macho_segment_command

* linkEditSegment = NULL; + const macho_segment_command

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

::CMD ) { const macho_segment_command

* segCmd = (const macho_segment_command

*)cmd; @@ -389,32 +506,37 @@ void MachOChecker::checkLoadCommands() if ( endOffset > fLength ) throw "segment fileoff+filesize does not fit in file"; - // keep LINKEDIT segment + // record special segments if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) linkEditSegment = segCmd; + else if ( strcmp(segCmd->segname(), "__UNIXSTACK") == 0 ) + stackSegment = segCmd; // cache interesting segments if ( fFirstSegment == NULL ) fFirstSegment = segCmd; + if ( (fTEXTSegment == NULL) && (strcmp(segCmd->segname(), "__TEXT") == 0) ) + fTEXTSegment = segCmd; if ( (segCmd->initprot() & VM_PROT_WRITE) != 0 ) { if ( fFirstWritableSegment == NULL ) fFirstWritableSegment = segCmd; if ( segCmd->vmaddr() > 0x100000000ULL ) fWriteableSegmentWithAddrOver4G = true; } - + // check section ranges const macho_section

* const sectionsStart = (macho_section

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

)); const macho_section

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

* sect = sectionsStart; sect < sectionsEnd; ++sect) { - // check all sections are within segment + // check all non-zero sized sections are within segment if ( sect->addr() < startAddr ) throwf("section %s vm address not within segment", sect->sectname()); if ( (sect->addr()+sect->size()) > endAddr ) throwf("section %s vm address not within segment", sect->sectname()); if ( ((sect->flags() & SECTION_TYPE) != S_ZEROFILL) && ((sect->flags() & SECTION_TYPE) != S_THREAD_LOCAL_ZEROFILL) - && (segCmd->filesize() != 0) ) { + && (segCmd->filesize() != 0) + && (sect->size() != 0) ) { if ( sect->offset() < startOffset ) throwf("section %s file offset not within segment", sect->sectname()); if ( (sect->offset()+sect->size()) > endOffset ) @@ -431,6 +553,45 @@ void MachOChecker::checkLoadCommands() if ( linkEditSegment == NULL ) throw "no __LINKEDIT segment"; + // verify there was an executable __TEXT segment and load commands are in it + if ( fTEXTSegment == NULL ) + throw "no __TEXT segment"; + if ( fTEXTSegment->initprot() != (VM_PROT_READ|VM_PROT_EXECUTE) ) + throw "__TEXT segment does not have r-x init permissions"; + //if ( fTEXTSegment->maxprot() != (VM_PROT_READ|VM_PROT_EXECUTE|VM_PROT_WRITE) ) + // throw "__TEXT segment does not have rwx max permissions"; + if ( fTEXTSegment->fileoff() != 0 ) + throw "__TEXT segment does not start at mach_header"; + if ( fTEXTSegment->filesize() < (sizeof(macho_header

)+fHeader->sizeofcmds()) ) + throw "__TEXT segment smaller than load commands"; + + // verify if custom stack used, that stack is in __UNIXSTACK segment + if ( threadInfo != NULL ) { + pint_t initialSP = getInitialStackPointer(threadInfo); + if ( initialSP != 0 ) { + if ( stackSegment == NULL ) + throw "LC_UNIXTHREAD specifics custom initial stack pointer, but no __UNIXSTACK segment"; + if ( (initialSP < stackSegment->vmaddr()) || (initialSP > (stackSegment->vmaddr()+stackSegment->vmsize())) ) + throw "LC_UNIXTHREAD specifics custom initial stack pointer which does not point into __UNIXSTACK segment"; + } + } + + // verify __UNIXSTACK is zero fill + if ( stackSegment != NULL ) { + if ( (stackSegment->filesize() != 0) || (stackSegment->fileoff() != 0) ) + throw "__UNIXSTACK is not a zero-fill segment"; + if ( stackSegment->vmsize() < 4096 ) + throw "__UNIXSTACK segment is too small"; + } + + // verify entry point is in __TEXT segment + if ( threadInfo != NULL ) { + pint_t initialPC = getEntryPoint(threadInfo); + if ( (initialPC < fTEXTSegment->vmaddr()) || (initialPC >= (fTEXTSegment->vmaddr()+fTEXTSegment->vmsize())) ) + throwf("entry point 0x%0llX is outside __TEXT segment", (long long)initialPC); + } + + // checks for executables bool isStaticExecutable = false; if ( fHeader->filetype() == MH_EXECUTE ) { @@ -583,9 +744,16 @@ void MachOChecker::checkSection(const macho_segment_command

* segCmd, const throwf("section offset should be zero for zero-fill section %s", sect->sectname()); } + // check section's segment name matches segment +// if ( strncmp(sect->segname(), segCmd->segname(), 16) != 0 ) +// throwf("section %s in segment %s has wrong segment name", sect->sectname(), segCmd->segname()); + // more section tests here } + + + template void MachOChecker::checkIndirectSymbolTable() { @@ -629,6 +797,8 @@ void MachOChecker::checkIndirectSymbolTable() } + + template void MachOChecker::checkSymbolTable() { @@ -674,6 +844,62 @@ void MachOChecker::checkSymbolTable() } +template +void MachOChecker::checkInitTerms() +{ + const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)fHeader + sizeof(macho_header

)); + const uint32_t cmd_count = fHeader->ncmds(); + const macho_load_command

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

::CMD ) { + const macho_segment_command

* segCmd = (const macho_segment_command

*)cmd; + const macho_section

* const sectionsStart = (macho_section

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

)); + const macho_section

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

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + // make sure all magic sections that use indirect symbol table fit within it + uint32_t count; + pint_t* arrayStart; + pint_t* arrayEnd; + const char* kind = "initializer"; + switch ( sect->flags() & SECTION_TYPE ) { + case S_MOD_TERM_FUNC_POINTERS: + kind = "terminator"; + // fall through + case S_MOD_INIT_FUNC_POINTERS: + count = sect->size() / sizeof(pint_t); + if ( (count*sizeof(pint_t)) != sect->size() ) + throwf("%s section size is not an even multiple of element size", sect->sectname()); + if ( (sect->addr() % sizeof(pint_t)) != 0 ) + throwf("%s section size is not pointer size aligned", sect->sectname()); + // check each pointer in array points within TEXT + arrayStart = (pint_t*)((char*)fHeader + sect->offset()); + arrayEnd = (pint_t*)((char*)fHeader + sect->offset() + sect->size()); + for (pint_t* p=arrayStart; p < arrayEnd; ++p) { + pint_t pointer = P::getP(*p); + if ( (pointer < fTEXTSegment->vmaddr()) || (pointer >= (fTEXTSegment->vmaddr()+fTEXTSegment->vmsize())) ) + throwf("%s 0x%08llX points outside __TEXT segment", kind, (long long)pointer); + } + // check each pointer in array will be rebased and not bound + if ( fSlidableImage ) { + pint_t sectionBeginAddr = sect->addr(); + pint_t sectionEndddr = sect->addr() + sect->size(); + for(pint_t addr = sectionBeginAddr; addr < sectionEndddr; addr += sizeof(pint_t)) { + if ( addressIsBindingSite(addr) ) + throwf("%s at 0x%0llX has binding to external symbol", kind, (long long)addr); + if ( ! addressIsRebaseSite(addr) ) + throwf("%s at 0x%0llX is not rebased", kind, (long long)addr); + } + } + break; + } + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + +} + + template <> ppc::P::uint_t MachOChecker::relocBase() { @@ -1052,6 +1278,200 @@ bool MachOChecker::hasTextRelocInRange(pint_t rangeStart, pint_t rangeEnd) } } +template +bool MachOChecker::addressIsRebaseSite(pint_t targetAddr) +{ + // look at local relocs + const macho_relocation_info

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

* reloc = fLocalRelocations; reloc < localRelocsEnd; ++reloc) { + pint_t relocAddress = reloc->r_address() + this->relocBase(); + if ( relocAddress == targetAddr ) + return true; + } + // look rebase info + if ( fDyldInfo != NULL ) { + const uint8_t* p = (uint8_t*)fHeader + fDyldInfo->rebase_off(); + const uint8_t* end = &p[fDyldInfo->rebase_size()]; + + uint8_t type = 0; + uint64_t segOffset = 0; + uint32_t count; + uint32_t skip; + int segIndex; + pint_t segStartAddr = 0; + pint_t addr; + bool done = false; + while ( !done && (p < end) ) { + uint8_t immediate = *p & REBASE_IMMEDIATE_MASK; + uint8_t opcode = *p & REBASE_OPCODE_MASK; + ++p; + switch (opcode) { + case REBASE_OPCODE_DONE: + done = true; + break; + case REBASE_OPCODE_SET_TYPE_IMM: + type = immediate; + break; + case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segIndex = immediate; + segStartAddr = segStartAddress(segIndex); + segOffset = read_uleb128(p, end); + break; + case REBASE_OPCODE_ADD_ADDR_ULEB: + segOffset += read_uleb128(p, end); + break; + case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: + segOffset += immediate*sizeof(pint_t); + break; + case REBASE_OPCODE_DO_REBASE_IMM_TIMES: + for (int i=0; i < immediate; ++i) { + addr = segStartAddr+segOffset; + if ( addr == targetAddr ) + return true; + //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName); + segOffset += sizeof(pint_t); + } + break; + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: + count = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + addr = segStartAddr+segOffset; + if ( addr == targetAddr ) + return true; + //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName); + segOffset += sizeof(pint_t); + } + break; + case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: + addr = segStartAddr+segOffset; + if ( addr == targetAddr ) + return true; + //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName); + segOffset += read_uleb128(p, end) + sizeof(pint_t); + break; + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: + count = read_uleb128(p, end); + skip = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + addr = segStartAddr+segOffset; + if ( addr == targetAddr ) + return true; + //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName); + segOffset += skip + sizeof(pint_t); + } + break; + default: + throwf("bad rebase opcode %d", *p); + } + } + } + return false; +} + + +template +bool MachOChecker::addressIsBindingSite(pint_t targetAddr) +{ + // look at external relocs + const macho_relocation_info

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

* reloc = fExternalRelocations; reloc < externRelocsEnd; ++reloc) { + pint_t relocAddress = reloc->r_address() + this->relocBase(); + if ( relocAddress == targetAddr ) + return true; + } + // look bind info + if ( fDyldInfo != NULL ) { + const uint8_t* p = (uint8_t*)fHeader + fDyldInfo->bind_off(); + const uint8_t* end = &p[fDyldInfo->bind_size()]; + + uint8_t type = 0; + uint64_t segOffset = 0; + uint32_t count; + uint32_t skip; + uint8_t flags; + const char* symbolName = NULL; + int libraryOrdinal = 0; + int segIndex; + int64_t addend = 0; + pint_t segStartAddr = 0; + pint_t addr; + bool done = false; + while ( !done && (p < end) ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + done = true; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + libraryOrdinal = immediate; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + libraryOrdinal = read_uleb128(p, end); + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + // the special ordinals are negative numbers + if ( immediate == 0 ) + libraryOrdinal = 0; + else { + int8_t signExtended = BIND_OPCODE_MASK | immediate; + libraryOrdinal = signExtended; + } + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + symbolName = (char*)p; + while (*p != '\0') + ++p; + ++p; + break; + case BIND_OPCODE_SET_TYPE_IMM: + type = immediate; + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + addend = read_sleb128(p, end); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segIndex = immediate; + segStartAddr = segStartAddress(segIndex); + segOffset = read_uleb128(p, end); + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + segOffset += read_uleb128(p, end); + break; + case BIND_OPCODE_DO_BIND: + if ( (segStartAddr+segOffset) == targetAddr ) + return true; + segOffset += sizeof(pint_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + if ( (segStartAddr+segOffset) == targetAddr ) + return true; + segOffset += read_uleb128(p, end) + sizeof(pint_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + if ( (segStartAddr+segOffset) == targetAddr ) + return true; + segOffset += immediate*sizeof(pint_t) + sizeof(pint_t); + break; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + count = read_uleb128(p, end); + skip = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + if ( (segStartAddr+segOffset) == targetAddr ) + return true; + segOffset += skip + sizeof(pint_t); + } + break; + default: + throwf("bad bind opcode %d", *p); + } + } + } + return false; +} + + static void check(const char* path) { struct stat stat_buf; @@ -1139,28 +1559,34 @@ static void check(const char* path) int main(int argc, const char* argv[]) { - try { - for(int i=1; i < argc; ++i) { - const char* arg = argv[i]; - if ( arg[0] == '-' ) { - if ( strcmp(arg, "-no_content") == 0 ) { - - } - else { - throwf("unknown option: %s\n", arg); - } + bool progress = false; + int result = 0; + for(int i=1; i < argc; ++i) { + const char* arg = argv[i]; + if ( arg[0] == '-' ) { + if ( strcmp(arg, "-progress") == 0 ) { + progress = true; } else { + throwf("unknown option: %s\n", arg); + } + } + else { + bool success = true; + try { check(arg); } + catch (const char* msg) { + fprintf(stderr, "machocheck failed: %s %s\n", arg, msg); + result = 1; + success = false; + } + if ( success && progress ) + printf("ok: %s\n", arg); } } - catch (const char* msg) { - fprintf(stderr, "machocheck failed: %s\n", msg); - return 1; - } - return 0; + return result; } diff --git a/src/other/rebase.cpp b/src/other/rebase.cpp index c04fb0a..d69fc3c 100644 --- a/src/other/rebase.cpp +++ b/src/other/rebase.cpp @@ -1023,12 +1023,18 @@ int main(int argc, const char* argv[]) onlyArchs.insert(CPU_TYPE_I386); else if ( strcmp(arch, "x86_64") == 0 ) onlyArchs.insert(CPU_TYPE_X86_64); - else if ( strcmp(arch, "arm") == 0 ) - onlyArchs.insert(CPU_TYPE_ARM); - else if ( strcmp(arch, "armv6") == 0 ) - onlyArchs.insert(CPU_TYPE_ARM); - else - throwf("unknown architecture %s", arch); + else { + 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; + } + } + if ( !found ) + throwf("unknown architecture %s", arch); + } } else { usage(); diff --git a/src/other/unwinddump.cpp b/src/other/unwinddump.cpp index 81e677d..d24e62c 100644 --- a/src/other/unwinddump.cpp +++ b/src/other/unwinddump.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2008 Apple Inc. All rights reserved. + * Copyright (c) 2008-2011 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -83,8 +83,12 @@ private: const char* path, bool showFunctionNames); bool findUnwindSection(); void printUnwindSection(bool showFunctionNames); + void printObjectUnwindSection(bool showFunctionNames); void getSymbolTableInfo(); - const char* functionName(pint_t addr); + const char* functionName(pint_t addr, uint32_t* offset=NULL); + const char* personalityName(const macho_relocation_info* reloc); + bool hasExernReloc(uint64_t sectionOffset, const char** personalityStr, pint_t* addr=NULL); + static const char* archName(); static void decode(uint32_t encoding, const uint8_t* funcStart, char* str); @@ -106,41 +110,6 @@ template <> const char* UnwindPrinter::archName() { return "i386"; } template <> const char* UnwindPrinter::archName() { return "x86_64"; } template <> const char* UnwindPrinter::archName() { return "arm"; } -template <> -bool UnwindPrinter::validFile(const uint8_t* fileContent) -{ - const macho_header

* header = (const macho_header

*)fileContent; - if ( header->magic() != MH_MAGIC ) - return false; - if ( header->cputype() != CPU_TYPE_POWERPC ) - return false; - switch (header->filetype()) { - case MH_EXECUTE: - case MH_DYLIB: - case MH_BUNDLE: - case MH_DYLINKER: - return true; - } - return false; -} - -template <> -bool UnwindPrinter::validFile(const uint8_t* fileContent) -{ - const macho_header

* header = (const macho_header

*)fileContent; - if ( header->magic() != MH_MAGIC_64 ) - return false; - if ( header->cputype() != CPU_TYPE_POWERPC64 ) - return false; - switch (header->filetype()) { - case MH_EXECUTE: - case MH_DYLIB: - case MH_BUNDLE: - case MH_DYLINKER: - return true; - } - return false; -} template <> bool UnwindPrinter::validFile(const uint8_t* fileContent) @@ -155,6 +124,7 @@ bool UnwindPrinter::validFile(const uint8_t* fileContent) case MH_DYLIB: case MH_BUNDLE: case MH_DYLINKER: + case MH_OBJECT: return true; } return false; @@ -173,24 +143,7 @@ bool UnwindPrinter::validFile(const uint8_t* fileContent) case MH_DYLIB: case MH_BUNDLE: case MH_DYLINKER: - return true; - } - return false; -} - -template <> -bool UnwindPrinter::validFile(const uint8_t* fileContent) -{ - const macho_header

* header = (const macho_header

*)fileContent; - if ( header->magic() != MH_MAGIC ) - return false; - if ( header->cputype() != CPU_TYPE_ARM ) - return false; - switch (header->filetype()) { - case MH_EXECUTE: - case MH_DYLIB: - case MH_BUNDLE: - case MH_DYLINKER: + case MH_OBJECT: return true; } return false; @@ -211,8 +164,12 @@ UnwindPrinter::UnwindPrinter(const uint8_t* fileContent, uint32_t fileLength, getSymbolTableInfo(); - if ( findUnwindSection() ) - printUnwindSection(showFunctionNames); + if ( findUnwindSection() ) { + if ( fHeader->filetype() == MH_OBJECT ) + printObjectUnwindSection(showFunctionNames); + else + printUnwindSection(showFunctionNames); + } } @@ -243,8 +200,11 @@ void UnwindPrinter::getSymbolTableInfo() } template -const char* UnwindPrinter::functionName(pint_t addr) +const char* UnwindPrinter::functionName(pint_t addr, uint32_t* offset) { + const macho_nlist

* closestSymbol = NULL; + if ( offset != NULL ) + *offset = 0; for (uint32_t i=0; i < fSymbolCount; ++i) { uint8_t type = fSymbols[i].n_type(); if ( ((type & N_STAB) == 0) && ((type & N_TYPE) == N_SECT) ) { @@ -253,8 +213,22 @@ const char* UnwindPrinter::functionName(pint_t addr) //fprintf(stderr, "addr=0x%08llX, i=%u, n_type=0x%0X, r=%s\n", (long long)(fSymbols[i].n_value()), i, fSymbols[i].n_type(), r); return r; } + else if ( offset != NULL ) { + if ( closestSymbol == NULL ) { + if ( fSymbols[i].n_value() < addr ) + closestSymbol = &fSymbols[i]; + } + else { + if ( (fSymbols[i].n_value() < addr) && (fSymbols[i].n_value() > closestSymbol->n_value()) ) + closestSymbol = &fSymbols[i]; + } + } } } + if ( closestSymbol != NULL ) { + *offset = addr - closestSymbol->n_value(); + return &fStrings[closestSymbol->n_strx()]; + } return "--anonymous function--"; } @@ -263,6 +237,12 @@ const char* UnwindPrinter::functionName(pint_t addr) template bool UnwindPrinter::findUnwindSection() { + const char* unwindSectionName = "__unwind_info"; + const char* unwindSegmentName = "__TEXT"; + if ( fHeader->filetype() == MH_OBJECT ) { + unwindSectionName = "__compact_unwind"; + unwindSegmentName = "__LD"; + } 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(); @@ -280,7 +260,7 @@ bool UnwindPrinter::findUnwindSection() const macho_section

* const sectionsStart = (macho_section

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

)); const macho_section

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

* sect = sectionsStart; sect < sectionsEnd; ++sect) { - if ( (strcmp(sect->sectname(), "__unwind_info") == 0) && (strcmp(sect->segname(), "__TEXT") == 0) ) { + if ( (strncmp(sect->sectname(), unwindSectionName, 16) == 0) && (strcmp(sect->segname(), unwindSegmentName) == 0) ) { fUnwindSection = sect; fMachHeaderAddress = segCmd->vmaddr(); return fUnwindSection; @@ -659,13 +639,97 @@ void UnwindPrinter::decode(uint32_t encoding, const uint8_t* funcStart, cha } + +template <> +const char* UnwindPrinter::personalityName(const macho_relocation_info* reloc) +{ + //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section"); + //assert((reloc->r_type() == X86_64_RELOC_UNSIGNED) && "wrong reloc type on personality column in __compact_unwind section"); + const macho_nlist

& sym = fSymbols[reloc->r_symbolnum()]; + return &fStrings[sym.n_strx()]; +} + +template <> +const char* UnwindPrinter::personalityName(const macho_relocation_info* reloc) +{ + //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section"); + //assert((reloc->r_type() == GENERIC_RELOC_VANILLA) && "wrong reloc type on personality column in __compact_unwind section"); + const macho_nlist

& sym = fSymbols[reloc->r_symbolnum()]; + return &fStrings[sym.n_strx()]; +} + template -void UnwindPrinter::decode(uint32_t encoding, const uint8_t* funcStart, char* str) +bool UnwindPrinter::hasExernReloc(uint64_t sectionOffset, const char** personalityStr, pint_t* addr) { - + const macho_relocation_info

* relocs = (macho_relocation_info

*)((uint8_t*)fHeader + fUnwindSection->reloff()); + const macho_relocation_info

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

* reloc = relocs; reloc < relocsEnd; ++reloc) { + if ( reloc->r_extern() && (reloc->r_address() == sectionOffset) ) { + *personalityStr = this->personalityName(reloc); + if ( addr != NULL ) + *addr = fSymbols[reloc->r_symbolnum()].n_value(); + return true; + } + } + return false; +} + +template +void UnwindPrinter::printObjectUnwindSection(bool showFunctionNames) +{ + printf("Arch: %s, Section: __LD,__compact_unwind (size=0x%08llX, => %lld entries)\n", + archName(), fUnwindSection->size(), fUnwindSection->size() / sizeof(macho_compact_unwind_entry

)); + + const macho_compact_unwind_entry

* const entriesStart = (macho_compact_unwind_entry

*)((uint8_t*)fHeader + fUnwindSection->offset()); + const macho_compact_unwind_entry

* const entriesEnd = (macho_compact_unwind_entry

*)((uint8_t*)fHeader + fUnwindSection->offset() + fUnwindSection->size()); + for (const macho_compact_unwind_entry

* entry=entriesStart; entry < entriesEnd; ++entry) { + uint64_t entryAddress = ((char*)entry - (char*)entriesStart) + fUnwindSection->addr(); + printf("0x%08llX:\n", entryAddress); + const char* functionNameStr; + pint_t funcAddress; + uint32_t offsetInFunction; + if ( hasExernReloc(((char*)entry-(char*)entriesStart)+macho_compact_unwind_entry

::codeStartFieldOffset(), &functionNameStr, &funcAddress) ) { + offsetInFunction = entry->codeStart(); + } + else { + functionNameStr = this->functionName(entry->codeStart(), &offsetInFunction); + } + if ( offsetInFunction == 0 ) + printf(" start: 0x%08llX %s\n", (uint64_t)funcAddress, functionNameStr); + else + printf(" start: 0x%08llX %s+0x%X\n", (uint64_t)funcAddress+offsetInFunction, functionNameStr, offsetInFunction); + + printf(" end: 0x%08llX (len=0x%08X)\n", (uint64_t)(funcAddress+offsetInFunction+entry->codeLen()), entry->codeLen()); + + char encodingString[200]; + this->decode(entry->compactUnwindInfo(), ((const uint8_t*)fHeader), encodingString); + printf(" unwind info: 0x%08X %s\n", entry->compactUnwindInfo(), encodingString); + + const char* personalityNameStr; + if ( hasExernReloc(((char*)entry-(char*)entriesStart)+macho_compact_unwind_entry

::personalityFieldOffset(), &personalityNameStr) ) { + printf(" personality: %s\n", personalityNameStr); + } + else { + printf(" personality:\n"); + } + if ( entry->lsda() == 0 ) { + printf(" lsda:\n"); + } + else { + uint32_t lsdaOffset; + const char* lsdaName = this->functionName(entry->lsda(), &lsdaOffset); + if ( lsdaOffset == 0 ) + printf(" lsda: 0x%08llX %s\n", (uint64_t)entry->lsda(), lsdaName); + else + printf(" lsda: 0x%08llX %s+0x%X\n", (uint64_t)entry->lsda(), lsdaName, lsdaOffset); + } + } + } + + template void UnwindPrinter::printUnwindSection(bool showFunctionNames) { @@ -809,36 +873,18 @@ static void dump(const char* path, const std::set& onlyArchs, bool s unsigned int cputype = OSSwapBigToHostInt32(archs[i].cputype); if ( onlyArchs.count(cputype) ) { switch(cputype) { - case CPU_TYPE_POWERPC: - if ( UnwindPrinter::validFile(p + offset) ) - UnwindPrinter::make(p + offset, size, path, showFunctionNames); - else - throw "in universal file, ppc slice does not contain ppc mach-o"; - break; case CPU_TYPE_I386: if ( UnwindPrinter::validFile(p + offset) ) UnwindPrinter::make(p + offset, size, path, showFunctionNames); else throw "in universal file, i386 slice does not contain i386 mach-o"; break; - case CPU_TYPE_POWERPC64: - if ( UnwindPrinter::validFile(p + offset) ) - UnwindPrinter::make(p + offset, size, path, showFunctionNames); - else - throw "in universal file, ppc64 slice does not contain ppc64 mach-o"; - break; case CPU_TYPE_X86_64: if ( UnwindPrinter::validFile(p + offset) ) UnwindPrinter::make(p + offset, size, path, showFunctionNames); else throw "in universal file, x86_64 slice does not contain x86_64 mach-o"; break; - case CPU_TYPE_ARM: - if ( UnwindPrinter::validFile(p + offset) ) - UnwindPrinter::make(p + offset, size, path, showFunctionNames); - else - throw "in universal file, arm slice does not contain arm mach-o"; - break; default: throwf("in universal file, unknown architecture slice 0x%x\n", cputype); } @@ -848,18 +894,9 @@ static void dump(const char* path, const std::set& onlyArchs, bool s else if ( UnwindPrinter::validFile(p) && onlyArchs.count(CPU_TYPE_I386) ) { UnwindPrinter::make(p, length, path, showFunctionNames); } - else if ( UnwindPrinter::validFile(p) && onlyArchs.count(CPU_TYPE_POWERPC) ) { - UnwindPrinter::make(p, length, path, showFunctionNames); - } - else if ( UnwindPrinter::validFile(p) && onlyArchs.count(CPU_TYPE_POWERPC64) ) { - UnwindPrinter::make(p, length, path, showFunctionNames); - } else if ( UnwindPrinter::validFile(p) && onlyArchs.count(CPU_TYPE_X86_64) ) { UnwindPrinter::make(p, length, path, showFunctionNames); } - else if ( UnwindPrinter::validFile(p) && onlyArchs.count(CPU_TYPE_ARM) ) { - UnwindPrinter::make(p, length, path, showFunctionNames); - } else { throw "not a known file type"; } @@ -882,16 +919,10 @@ int main(int argc, const char* argv[]) if ( arg[0] == '-' ) { if ( strcmp(arg, "-arch") == 0 ) { const char* arch = argv[++i]; - if ( strcmp(arch, "ppc") == 0 ) - onlyArchs.insert(CPU_TYPE_POWERPC); - else if ( strcmp(arch, "ppc64") == 0 ) - onlyArchs.insert(CPU_TYPE_POWERPC64); - else if ( strcmp(arch, "i386") == 0 ) + if ( strcmp(arch, "i386") == 0 ) onlyArchs.insert(CPU_TYPE_I386); else if ( strcmp(arch, "x86_64") == 0 ) onlyArchs.insert(CPU_TYPE_X86_64); - else if ( strcmp(arch, "arm") == 0 ) - onlyArchs.insert(CPU_TYPE_ARM); else throwf("unknown architecture %s", arch); } @@ -909,11 +940,8 @@ int main(int argc, const char* argv[]) // use all architectures if no restrictions specified if ( onlyArchs.size() == 0 ) { - onlyArchs.insert(CPU_TYPE_POWERPC); - onlyArchs.insert(CPU_TYPE_POWERPC64); onlyArchs.insert(CPU_TYPE_I386); onlyArchs.insert(CPU_TYPE_X86_64); - onlyArchs.insert(CPU_TYPE_ARM); } // process each file diff --git a/unit-tests/include/common.makefile b/unit-tests/include/common.makefile index 642e491..adb5468 100644 --- a/unit-tests/include/common.makefile +++ b/unit-tests/include/common.makefile @@ -8,6 +8,8 @@ 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 OBJECTDUMP = ObjectDump @@ -19,7 +21,8 @@ DYLDINFO = dyldinfo ifdef BUILT_PRODUCTS_DIR # if run within Xcode, add the just built tools to the command path PATH := ${BUILT_PRODUCTS_DIR}:${MYDIR}:${PATH} - COMPILER_PATH := ${BUILT_PRODUCTS_DIR}:${MYDIR}:${COMPILER_PATH} + COMPILER_PATH := ${BUILT_PRODUCTS_DIR}:${COMPILER_PATH} + LD_PATH = ${BUILT_PRODUCTS_DIR} LD = ${BUILT_PRODUCTS_DIR}/ld OBJECTDUMP = ${BUILT_PRODUCTS_DIR}/ObjectDump MACHOCHECK = ${BUILT_PRODUCTS_DIR}/machocheck @@ -29,11 +32,11 @@ ifdef BUILT_PRODUCTS_DIR else ifneq "$(findstring /unit-tests/test-cases/, $(shell pwd))" "" # if run from Terminal inside unit-test directory - RELEASEDIR=$(shell cd ../../../build/Release;pwd) RELEASEADIR=$(shell cd ../../../build/Release-assert;pwd) DEBUGDIR=$(shell cd ../../../build/Debug;pwd) PATH := ${RELEASEADIR}:${RELEASEDIR}:${DEBUGDIR}:${MYDIR}:${PATH} - COMPILER_PATH := ${RELEASEADIR}:${RELEASEDIR}:${DEBUGDIR}:${MYDIR}:${COMPILER_PATH} + COMPILER_PATH := ${RELEASEADIR}:${RELEASEDIR}:${DEBUGDIR}:${COMPILER_PATH} + LD_PATH = ${DEBUGDIR} LD = ${DEBUGDIR}/ld OBJECTDUMP = ${DEBUGDIR}/ObjectDump MACHOCHECK = ${DEBUGDIR}/machocheck @@ -47,53 +50,76 @@ else endif export PATH export COMPILER_PATH +export GCC_EXEC_PREFIX=garbage ifeq ($(ARCH),ppc) SDKExtra = -isysroot /Developer/SDKs/MacOSX10.6.sdk endif CC = cc -arch ${ARCH} ${SDKExtra} -CCFLAGS = -Wall -std=c99 +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} CXXFLAGS = -Wall ifeq ($(ARCH),armv6) - SDKExtra = -isysroot /Developer/SDKs/Extra - LDFLAGS := -syslibroot /Developer/SDKs/Extra + 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) + VERSION_NEW_LINKEDIT = -miphoneos-version-min=4.0 + VERSION_OLD_LINKEDIT = -miphoneos-version-min=3.0 + LD_SYSROOT = -syslibroot $(IOS_SDK) + LD_NEW_LINKEDIT = -ios_version_min 4.0 else FILEARCH = $(ARCH) endif ifeq ($(ARCH),armv7) - SDKExtra = -isysroot /Developer/SDKs/Extra - LDFLAGS := -syslibroot /Developer/SDKs/Extra + 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) + VERSION_NEW_LINKEDIT = -miphoneos-version-min=4.0 + VERSION_OLD_LINKEDIT = -miphoneos-version-min=3.0 + LD_SYSROOT = -syslibroot $(IOS_SDK) + LD_NEW_LINKEDIT = -ios_version_min 4.0 else FILEARCH = $(ARCH) endif ifeq ($(ARCH),thumb) - SDKExtra = -isysroot /Developer/SDKs/Extra - LDFLAGS := -syslibroot /Developer/SDKs/Extra + LDFLAGS := -syslibroot $(IOS_SDK) CCFLAGS += -mthumb 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) + VERSION_NEW_LINKEDIT = -miphoneos-version-min=4.0 + VERSION_OLD_LINKEDIT = -miphoneos-version-min=3.0 + LD_SYSROOT = -syslibroot $(IOS_SDK) + LD_NEW_LINKEDIT = -ios_version_min 4.0 else FILEARCH = $(ARCH) endif ifeq ($(ARCH),thumb2) - SDKExtra = -isysroot /Developer/SDKs/Extra - LDFLAGS := -syslibroot /Developer/SDKs/Extra + LDFLAGS := -syslibroot $(IOS_SDK) CCFLAGS += -mthumb CXXFLAGS += -mthumb override ARCH = armv7 override FILEARCH = arm - CC = /Volumes/Leopard/Developer/Platforms/iPhoneOS.platform/usr/bin/gcc-4.2 -arch ${ARCH} + 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) + VERSION_NEW_LINKEDIT = -miphoneos-version-min=4.0 + VERSION_OLD_LINKEDIT = -miphoneos-version-min=3.0 + LD_SYSROOT = -syslibroot $(IOS_SDK) + LD_NEW_LINKEDIT = -ios_version_min 4.0 else FILEARCH = $(ARCH) endif diff --git a/unit-tests/run-all-unit-tests b/unit-tests/run-all-unit-tests index f6275a7..c07c947 100755 --- a/unit-tests/run-all-unit-tests +++ b/unit-tests/run-all-unit-tests @@ -12,9 +12,15 @@ cd `echo "$0" | sed 's/run-all-unit-tests/test-cases/'` [ "$PROCTORRUN" ] && exec ../proctor-run -all_archs="x86_64 ppc i386 " -#armv6 thumb -valid_archs="x86_64 armv6 ppc i386 " +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 ] +then + all_archs="${all_archs} armv7" + valid_archs="${valid_archs} armv7" +fi + # clean first ../bin/make-recursive.pl clean > /dev/null diff --git a/unit-tests/test-cases/absolute-symbol/Makefile b/unit-tests/test-cases/absolute-symbol/Makefile index 806c64e..bbdfd11 100644 --- a/unit-tests/test-cases/absolute-symbol/Makefile +++ b/unit-tests/test-cases/absolute-symbol/Makefile @@ -30,10 +30,10 @@ include ${TESTROOT}/include/common.makefile all: ${CC} ${CCFLAGS} main.c -static -c ${CC} ${CCFLAGS} abs.s -static -c - ${LD} -arch ${ARCH} -static main.o abs.o -e _main -o main -new_linker - ${LD} -arch ${ARCH} -static -r main.o abs.o -o all.o -new_linker + ${LD} -arch ${ARCH} -static main.o abs.o -e _main -o main -new_linker -macosx_version_min 10.6 + ${LD} -arch ${ARCH} -static -r main.o abs.o -o all.o -new_linker nm -m all.o | grep _myAbs | grep absolute | ${FAIL_IF_EMPTY} - ${LD} -arch ${ARCH} -static all.o -e _main -o main2 -new_linker + ${LD} -arch ${ARCH} -static all.o -e _main -o main2 -new_linker -macosx_version_min 10.6 ${PASS_IFF_GOOD_MACHO} main2 clean: diff --git a/unit-tests/test-cases/absolute-symbol/abs.s b/unit-tests/test-cases/absolute-symbol/abs.s index 1aee96f..dc5496f 100644 --- a/unit-tests/test-cases/absolute-symbol/abs.s +++ b/unit-tests/test-cases/absolute-symbol/abs.s @@ -1,7 +1,7 @@ .globl _myAbs #if __LP64__ - _myAbs = 0x012345678 + _myAbs = 0x102345678 #else _myAbs = 0xfe000000 #endif diff --git a/unit-tests/test-cases/alias-command-line/aliases.s b/unit-tests/test-cases/alias-command-line/aliases.s index 48e5961..3f3a9a6 100644 --- a/unit-tests/test-cases/alias-command-line/aliases.s +++ b/unit-tests/test-cases/alias-command-line/aliases.s @@ -22,7 +22,8 @@ */ .text - + .align 4 + _temp: nop nop diff --git a/unit-tests/test-cases/alias-objects/aliases.s b/unit-tests/test-cases/alias-objects/aliases.s index b21f8c7..5e92d8d 100644 --- a/unit-tests/test-cases/alias-objects/aliases.s +++ b/unit-tests/test-cases/alias-objects/aliases.s @@ -22,7 +22,8 @@ */ .text - + .align 4 + _temp: nop nop diff --git a/unit-tests/test-cases/archive-image_info/Makefile b/unit-tests/test-cases/archive-image_info/Makefile index 5bb026f..ae87948 100644 --- a/unit-tests/test-cases/archive-image_info/Makefile +++ b/unit-tests/test-cases/archive-image_info/Makefile @@ -32,7 +32,7 @@ IMAGE_INFO = "__image_info" ifeq ($(ARCH),x86_64) IMAGE_INFO = "__objc_imageinfo" endif -ifeq ($(ARCH),armv6) +ifeq (${FILEARCH},arm) IMAGE_INFO = "__objc_imageinfo" endif diff --git a/unit-tests/test-cases/archive-order/Makefile b/unit-tests/test-cases/archive-order/Makefile new file mode 100644 index 0000000..a1b6a3f --- /dev/null +++ b/unit-tests/test-cases/archive-order/Makefile @@ -0,0 +1,47 @@ +## +# 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 the order of functions from archives. +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -c -o foo.o + ${CC} ${CCFLAGS} foo2.c -c -o foo2.o + ${CC} ${CCFLAGS} foo3.c -c -o foo3.o + ${CC} ${CCFLAGS} bar.c -c -o bar.o + ${CC} ${CCFLAGS} bar2.c -c -o bar2.o + ${CC} ${CCFLAGS} bar3.c -c -o bar3.o + libtool -static foo.o foo2.o foo3.o -o libfoo.a + libtool -static bar3.o bar2.o bar.o -o libbar.a + ${CC} ${CCFLAGS} main.c -lbar -lfoo -L. -o main + nm -njg main | egrep 'foo|bar' > found.order + diff found.order expected.order + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main *.o *.a found.order diff --git a/unit-tests/test-cases/archive-order/bar.c b/unit-tests/test-cases/archive-order/bar.c new file mode 100644 index 0000000..7fe6403 --- /dev/null +++ b/unit-tests/test-cases/archive-order/bar.c @@ -0,0 +1 @@ +int bar() { return 0; } diff --git a/unit-tests/test-cases/archive-order/bar2.c b/unit-tests/test-cases/archive-order/bar2.c new file mode 100644 index 0000000..54e8ace --- /dev/null +++ b/unit-tests/test-cases/archive-order/bar2.c @@ -0,0 +1 @@ +int bar2() { return 0; } diff --git a/unit-tests/test-cases/archive-order/bar3.c b/unit-tests/test-cases/archive-order/bar3.c new file mode 100644 index 0000000..0b44057 --- /dev/null +++ b/unit-tests/test-cases/archive-order/bar3.c @@ -0,0 +1 @@ +int bar3() { return 0; } diff --git a/unit-tests/test-cases/archive-order/expected.order b/unit-tests/test-cases/archive-order/expected.order new file mode 100644 index 0000000..f060170 --- /dev/null +++ b/unit-tests/test-cases/archive-order/expected.order @@ -0,0 +1,6 @@ +_bar3 +_bar2 +_bar +_foo +_foo2 +_foo3 diff --git a/unit-tests/test-cases/archive-order/foo.c b/unit-tests/test-cases/archive-order/foo.c new file mode 100644 index 0000000..a60f28c --- /dev/null +++ b/unit-tests/test-cases/archive-order/foo.c @@ -0,0 +1 @@ +int foo() { return 1; } diff --git a/unit-tests/test-cases/archive-order/foo2.c b/unit-tests/test-cases/archive-order/foo2.c new file mode 100644 index 0000000..492879f --- /dev/null +++ b/unit-tests/test-cases/archive-order/foo2.c @@ -0,0 +1 @@ +int foo2() { return 1; } diff --git a/unit-tests/test-cases/archive-order/foo3.c b/unit-tests/test-cases/archive-order/foo3.c new file mode 100644 index 0000000..1de0bce --- /dev/null +++ b/unit-tests/test-cases/archive-order/foo3.c @@ -0,0 +1 @@ +int foo3() { return 1; } diff --git a/unit-tests/test-cases/archive-order/main.c b/unit-tests/test-cases/archive-order/main.c new file mode 100644 index 0000000..242b32a --- /dev/null +++ b/unit-tests/test-cases/archive-order/main.c @@ -0,0 +1,41 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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@ + */ + +extern int foo(); +extern int foo2(); +extern int foo3(); +extern int bar(); +extern int bar2(); +extern int bar3(); + +int main() +{ + foo(); + bar(); + foo2(); + bar2(); + foo3(); + bar3(); + return 0; +} \ No newline at end of file diff --git a/unit-tests/test-cases/auto-arch/Makefile b/unit-tests/test-cases/auto-arch/Makefile index 3b1cc7c..06ca6ed 100644 --- a/unit-tests/test-cases/auto-arch/Makefile +++ b/unit-tests/test-cases/auto-arch/Makefile @@ -23,6 +23,12 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile +ifeq ($(FILEARCH),arm) + LD_VERS = -ios_version_min 4.0 -syslibroot $(IOS_SDK) +else + LD_VERS = -macosx_version_min 10.6 +endif + # # Check that ld can figure out architecture from .o files without -arch option @@ -33,8 +39,8 @@ run: all all: ${CC} ${CCFLAGS} hello.c -c -o hello.o ${FAIL_IF_BAD_OBJ} hello.o - ${LD} ${LDFLAGS} -lcrt1.o hello.o -o hello -lSystem - file hello | grep ${FILEARCH} | ${PASS_IFF_STDIN} + ${LD} ${LDFLAGS} hello.o -dylib -o hello.dylib -lSystem $(LD_VERS) + file hello.dylib | grep ${FILEARCH} | ${PASS_IFF_STDIN} clean: - rm -rf hello.o hello + rm -rf hello.o hello.dylib diff --git a/unit-tests/test-cases/auto-arch/hello.c b/unit-tests/test-cases/auto-arch/hello.c index 14d9363..463663a 100644 --- a/unit-tests/test-cases/auto-arch/hello.c +++ b/unit-tests/test-cases/auto-arch/hello.c @@ -26,4 +26,5 @@ int main() { fprintf(stdout, "hello\n"); + return 0; } diff --git a/unit-tests/test-cases/bind_at_load/Makefile b/unit-tests/test-cases/bind_at_load/Makefile index 007feb5..3cc0bf2 100644 --- a/unit-tests/test-cases/bind_at_load/Makefile +++ b/unit-tests/test-cases/bind_at_load/Makefile @@ -33,8 +33,8 @@ all: ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib ${CC} ${CCFLAGS} main.c libfoo.dylib libbar.dylib -o main -Wl,-bind_at_load - dyldinfo -lazy_bind main | grep "no compressed lazy binding info" | ${FAIL_IF_EMPTY} - dyldinfo -bind main | grep _bar | ${FAIL_IF_EMPTY} + ${DYLDINFO} -lazy_bind main | grep "no compressed lazy binding info" | ${FAIL_IF_EMPTY} + ${DYLDINFO} -bind main | grep _bar | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} main clean: diff --git a/unit-tests/test-cases/branch-distance/bar.s b/unit-tests/test-cases/branch-distance/bar.s index 5b1f6dd..999166a 100644 --- a/unit-tests/test-cases/branch-distance/bar.s +++ b/unit-tests/test-cases/branch-distance/bar.s @@ -1,5 +1,7 @@ .text + .align 4 + #if __thumb__ .thumb_func _bar .code 16 diff --git a/unit-tests/test-cases/branch-distance/foo.s b/unit-tests/test-cases/branch-distance/foo.s index 18f3a81..73d0140 100644 --- a/unit-tests/test-cases/branch-distance/foo.s +++ b/unit-tests/test-cases/branch-distance/foo.s @@ -1,6 +1,7 @@ .text + .align 4 #if __thumb__ .thumb_func _foo @@ -14,7 +15,7 @@ _foo: blx _bar // b _bar - + .align 4 _space1: #if __thumb2__ diff --git a/unit-tests/test-cases/branch-interworking/Makefile b/unit-tests/test-cases/branch-interworking/Makefile index c83e25e..d5ad615 100644 --- a/unit-tests/test-cases/branch-interworking/Makefile +++ b/unit-tests/test-cases/branch-interworking/Makefile @@ -35,6 +35,7 @@ run: all all: ${CC} ${CCFLAGS} mythumb.s -c ${CC} ${CCFLAGS} myarm.s -c + ${LD} -arch ${ARCH} mythumb.o myarm.o -r -o all.o ${CC} ${CCFLAGS} mythumb.o myarm.o -dynamiclib -o liball.dylib ${PASS_IFF_GOOD_MACHO} liball.dylib diff --git a/unit-tests/test-cases/branch-interworking/myarm.s b/unit-tests/test-cases/branch-interworking/myarm.s index a3c7818..df759e6 100644 --- a/unit-tests/test-cases/branch-interworking/myarm.s +++ b/unit-tests/test-cases/branch-interworking/myarm.s @@ -1,5 +1,6 @@ .text + .align 4 #if __arm__ .code 32 .align 2 diff --git a/unit-tests/test-cases/branch-interworking/mythumb.s b/unit-tests/test-cases/branch-interworking/mythumb.s index a58c28f..32e3d4b 100644 --- a/unit-tests/test-cases/branch-interworking/mythumb.s +++ b/unit-tests/test-cases/branch-interworking/mythumb.s @@ -1,5 +1,11 @@ .text + .align 4 +_junk: + nop + nop + nop + nop #if __arm__ .syntax unified .thumb_func _mythumb diff --git a/unit-tests/test-cases/branch-islands/hello.c b/unit-tests/test-cases/branch-islands/hello.c index 89caa9d..3037663 100644 --- a/unit-tests/test-cases/branch-islands/hello.c +++ b/unit-tests/test-cases/branch-islands/hello.c @@ -6,5 +6,6 @@ int main() { fprintf(stdout, "hello\n"); foo(); + return 0; } diff --git a/unit-tests/test-cases/check-init-abs/.mod_init_func b/unit-tests/test-cases/check-init-abs/.mod_init_func new file mode 100644 index 0000000..f0e588d --- /dev/null +++ b/unit-tests/test-cases/check-init-abs/.mod_init_func @@ -0,0 +1,2 @@ + .long 0x12340000 + diff --git a/unit-tests/test-cases/check-init-abs/Makefile b/unit-tests/test-cases/check-init-abs/Makefile new file mode 100644 index 0000000..f0dbf19 --- /dev/null +++ b/unit-tests/test-cases/check-init-abs/Makefile @@ -0,0 +1,21 @@ + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# Verify machochecker catchs init routines to abolute addresses outside image +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c init.s -o init.exe + ${FAIL_IF_SUCCESS} ${MACHOCHECK} init.exe 2>/dev/null + ${CC} ${CCFLAGS} main.c term.s -o term.exe + ${FAIL_IF_SUCCESS} ${MACHOCHECK} term.exe 2>/dev/null + ${PASS_IFF} /usr/bin/true + +clean: + rm -f init.exe term.exe diff --git a/unit-tests/test-cases/check-init-abs/init.s b/unit-tests/test-cases/check-init-abs/init.s new file mode 100644 index 0000000..55e93c6 --- /dev/null +++ b/unit-tests/test-cases/check-init-abs/init.s @@ -0,0 +1,10 @@ + + + .mod_init_func +#if __LP64__ + .quad 0x7FFF123400000000 +#else + .long 0x12340000 +#endif + + diff --git a/unit-tests/test-cases/prebound-main/main.c b/unit-tests/test-cases/check-init-abs/main.c similarity index 96% rename from unit-tests/test-cases/prebound-main/main.c rename to unit-tests/test-cases/check-init-abs/main.c index 251979e..b54a220 100644 --- a/unit-tests/test-cases/prebound-main/main.c +++ b/unit-tests/test-cases/check-init-abs/main.c @@ -1,3 +1,2 @@ - int main() { return 0; } diff --git a/unit-tests/test-cases/check-init-abs/term.s b/unit-tests/test-cases/check-init-abs/term.s new file mode 100644 index 0000000..c69be4f --- /dev/null +++ b/unit-tests/test-cases/check-init-abs/term.s @@ -0,0 +1,10 @@ + + + .mod_term_func +#if __LP64__ + .quad 0x7FFF123400000000 +#else + .long 0x12340000 +#endif + + diff --git a/unit-tests/test-cases/check-init-bind/.mod_init_func b/unit-tests/test-cases/check-init-bind/.mod_init_func new file mode 100644 index 0000000..f0e588d --- /dev/null +++ b/unit-tests/test-cases/check-init-bind/.mod_init_func @@ -0,0 +1,2 @@ + .long 0x12340000 + diff --git a/unit-tests/test-cases/check-init-bind/Makefile b/unit-tests/test-cases/check-init-bind/Makefile new file mode 100644 index 0000000..95c3ce8 --- /dev/null +++ b/unit-tests/test-cases/check-init-bind/Makefile @@ -0,0 +1,21 @@ + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# Verify machochecker catchs init routines with bindings to external symbols +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c init.s -Wl,-pie -o init.exe + ${FAIL_IF_SUCCESS} ${MACHOCHECK} init.exe 2>/dev/null + ${CC} ${CCFLAGS} main.c term.s -Wl,-pie -o term.exe + ${FAIL_IF_SUCCESS} ${MACHOCHECK} term.exe 2>/dev/null + ${PASS_IFF} /usr/bin/true + +clean: + rm -f init.exe term.exe diff --git a/unit-tests/test-cases/check-init-bind/init.s b/unit-tests/test-cases/check-init-bind/init.s new file mode 100644 index 0000000..f9dce33 --- /dev/null +++ b/unit-tests/test-cases/check-init-bind/init.s @@ -0,0 +1,10 @@ + + + .mod_init_func +#if __LP64__ + .quad _malloc + 0x100000010 +#else + .long _malloc + 0x1010 +#endif + + diff --git a/unit-tests/test-cases/check-init-bind/main.c b/unit-tests/test-cases/check-init-bind/main.c new file mode 100644 index 0000000..b54a220 --- /dev/null +++ b/unit-tests/test-cases/check-init-bind/main.c @@ -0,0 +1,2 @@ +int main() { return 0; } + diff --git a/unit-tests/test-cases/check-init-bind/term.s b/unit-tests/test-cases/check-init-bind/term.s new file mode 100644 index 0000000..bbdd265 --- /dev/null +++ b/unit-tests/test-cases/check-init-bind/term.s @@ -0,0 +1,10 @@ + + + .mod_term_func +#if __LP64__ + .quad _malloc + 0x100000010 +#else + .long _malloc + 0x1010 +#endif + + diff --git a/unit-tests/test-cases/check-init-no-rebase/.mod_init_func b/unit-tests/test-cases/check-init-no-rebase/.mod_init_func new file mode 100644 index 0000000..f0e588d --- /dev/null +++ b/unit-tests/test-cases/check-init-no-rebase/.mod_init_func @@ -0,0 +1,2 @@ + .long 0x12340000 + diff --git a/unit-tests/test-cases/check-init-no-rebase/Makefile b/unit-tests/test-cases/check-init-no-rebase/Makefile new file mode 100644 index 0000000..ff7d09a --- /dev/null +++ b/unit-tests/test-cases/check-init-no-rebase/Makefile @@ -0,0 +1,21 @@ + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# Verify machochecker catchs init routines in slidable images that are missing rebasing info +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c init.s -Wl,-pie -o init.exe + ${FAIL_IF_SUCCESS} ${MACHOCHECK} init.exe 2>/dev/null + ${CC} ${CCFLAGS} main.c term.s -Wl,-pie -o term.exe + ${FAIL_IF_SUCCESS} ${MACHOCHECK} term.exe 2>/dev/null + ${PASS_IFF} /usr/bin/true + +clean: + rm -f init.exe term.exe diff --git a/unit-tests/test-cases/check-init-no-rebase/init.s b/unit-tests/test-cases/check-init-no-rebase/init.s new file mode 100644 index 0000000..5b282d0 --- /dev/null +++ b/unit-tests/test-cases/check-init-no-rebase/init.s @@ -0,0 +1,10 @@ + + + .mod_init_func +#if __LP64__ + .quad 0x100000010 +#else + .long 0x1010 +#endif + + diff --git a/unit-tests/test-cases/check-init-no-rebase/main.c b/unit-tests/test-cases/check-init-no-rebase/main.c new file mode 100644 index 0000000..b54a220 --- /dev/null +++ b/unit-tests/test-cases/check-init-no-rebase/main.c @@ -0,0 +1,2 @@ +int main() { return 0; } + diff --git a/unit-tests/test-cases/check-init-no-rebase/term.s b/unit-tests/test-cases/check-init-no-rebase/term.s new file mode 100644 index 0000000..cfe0b3a --- /dev/null +++ b/unit-tests/test-cases/check-init-no-rebase/term.s @@ -0,0 +1,10 @@ + + + .mod_term_func +#if __LP64__ + .quad 0x100000010 +#else + .long 0x1010 +#endif + + diff --git a/unit-tests/test-cases/coalesce_weak_def_in_dylib/Makefile b/unit-tests/test-cases/coalesce_weak_def_in_dylib/Makefile index cdfe6ac..1f9f2e4 100644 --- a/unit-tests/test-cases/coalesce_weak_def_in_dylib/Makefile +++ b/unit-tests/test-cases/coalesce_weak_def_in_dylib/Makefile @@ -27,13 +27,13 @@ include ${TESTROOT}/include/common.makefile # Test the if all symbols from a dylib are weak_import, that the whole dylib is weakly loaded # -run: all-${ARCH} +run: all-${FILEARCH} all-ppc: ${PASS_IFF} true -all-armv6: +all-arm: ${PASS_IFF} true all-i386: all-real @@ -46,7 +46,7 @@ all-real: ${FAIL_IF_BAD_MACHO} libfoo.dylib ${CC} ${CCFLAGS} main.c -o main libfoo.dylib - dyldinfo -bind main | grep wfoo | ${FAIL_IF_EMPTY} + ${DYLDINFO} -bind main | grep wfoo | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} main diff --git a/unit-tests/test-cases/commons-alignment/Makefile b/unit-tests/test-cases/commons-alignment/Makefile index d073c0e..15e1abd 100644 --- a/unit-tests/test-cases/commons-alignment/Makefile +++ b/unit-tests/test-cases/commons-alignment/Makefile @@ -29,9 +29,12 @@ include ${TESTROOT}/include/common.makefile all: ${CC} ${CCFLAGS} foo.s -c -o foo.o - nm -m foo.o | grep '(alignment 2^6)' | ${FAIL_IF_EMPTY} + nm -m foo.o | grep _mycomm64aligned | grep '(alignment 2^6)' | ${FAIL_IF_EMPTY} + nm -m foo.o | grep _mycomm16kaligned | grep '(alignment 2^14)' | ${FAIL_IF_EMPTY} ${LD} foo.o -r -o foo2.o - nm -m foo2.o | grep '(alignment 2^6)' | ${PASS_IFF_STDIN} + nm -m foo2.o | grep _mycomm64aligned | grep '(alignment 2^6)' | ${FAIL_IF_EMPTY} + nm -m foo2.o | grep _mycomm16kaligned | grep '(alignment 2^14)' | ${FAIL_IF_EMPTY} + ${PASS_IFF} true clean: rm -rf foo.o foo2.o diff --git a/unit-tests/test-cases/commons-alignment/foo.s b/unit-tests/test-cases/commons-alignment/foo.s index 03c28fc..2b11717 100644 --- a/unit-tests/test-cases/commons-alignment/foo.s +++ b/unit-tests/test-cases/commons-alignment/foo.s @@ -1,2 +1,3 @@ .comm _mycomm64aligned,15,6 + .comm _mycomm16kaligned,256,14 diff --git a/unit-tests/test-cases/compact-unwind-basic/Makefile b/unit-tests/test-cases/compact-unwind-basic/Makefile new file mode 100644 index 0000000..10d741e --- /dev/null +++ b/unit-tests/test-cases/compact-unwind-basic/Makefile @@ -0,0 +1,47 @@ +## +# 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 compiler emitted compact unwind info +# + +all: all-${FILEARCH} + +all-i386: all-cu +all-x86_64: all-cu +all-arm: all-good + +all-cu: + ${CC} ${CCFLAGS} test.s -c -o test.o + ${OBJECTDUMP} test.o > test.o.dump + ${LD} -r -arch ${ARCH} test.o -o test-r.o + ${OBJECTDUMP} test-r.o > test-r.o.dump + ${PASS_IFF} diff test.o.dump test-r.o.dump + +all-good: + ${PASS_IFF} true + +clean: + rm -f test.o test-r.o test.o.dump test-r.o.dump diff --git a/unit-tests/test-cases/compact-unwind-basic/test.s b/unit-tests/test-cases/compact-unwind-basic/test.s new file mode 100644 index 0000000..e694feb --- /dev/null +++ b/unit-tests/test-cases/compact-unwind-basic/test.s @@ -0,0 +1,99 @@ +#if __LP64__ + #define pointer quad +#else + #define pointer long +#endif + + + + .text + +_basic: + nop + nop +Lbasicend: + + +_multi: + nop + nop +Lmulti1: + nop +Lmulti1a: + nop + nop +Lmulti2: + nop +Lmultiend: + + +_person: + nop + nop +Lpersonend: + + +_person_lsda: + nop + nop +Lpersonlsdaend: + + + .section __TEXT,__gcc_except_tab +_lsda1: + .long 1 + .long 2 + + + .section __LD,__compact_unwind,regular,debug + + .pointer _basic + .set L1,Lbasicend-_basic + .long L1 + .long 0 + .pointer 0 + .pointer 0 + + .pointer _multi + .set L2,Lmulti1-_multi + .long L2 + .long 1 + .pointer 0 + .pointer 0 + + .pointer Lmulti1 + .set L3,Lmulti2-Lmulti1 + .long L3 + .long 2 + .pointer 0 + .pointer 0 + + .pointer Lmulti2 + .set L4,Lmultiend-Lmulti2 + .long L4 + .long 3 + .pointer 0 + .pointer 0 + + + .pointer _person + .set L5,Lpersonend-_person + .long L5 + .long 0 + .pointer _gxx_personality_v0 + .pointer 0 + + + .pointer _person_lsda + .set L6,Lpersonlsdaend-_person_lsda + .long L6 + .long 0 + .pointer _gxx_personality_v0 + .pointer _lsda1 + + + .subsections_via_symbols + + + + diff --git a/unit-tests/test-cases/cpu-sub-types-preference/Makefile b/unit-tests/test-cases/cpu-sub-types-preference/Makefile index 7586a51..5f3ca5d 100644 --- a/unit-tests/test-cases/cpu-sub-types-preference/Makefile +++ b/unit-tests/test-cases/cpu-sub-types-preference/Makefile @@ -38,12 +38,15 @@ test-i386: test-x86_64: ${PASS_IFF} true -test-armv6: - gcc foo.c -arch armv4t -c -DAAA=1 -o foo4.o - gcc foo.c -arch armv5 -c -DBBB=1 -o foo5.o - gcc foo.c -arch armv6 -c -DCCC=1 -o foo6.o - gcc foo.c -arch xscale -c -DDDD=1 -o foox.o - lipo foo4.o foo5.o foo6.o foox.o -create -output foo.o +test-armv6: test-arm +test-armv7: test-arm + +test-arm: + clang foo.c -arch armv4t -c -DAAA=1 -o foo4.o + clang foo.c -arch armv5 -c -DBBB=1 -o foo5.o + clang foo.c -arch armv6 -c -DCCC=1 -o foo6.o + clang foo.c -arch armv7 -c -DDDD=1 -o foo7.o + lipo foo4.o foo5.o foo6.o foo7.o -create -output foo.o # check -arch armv4t pulls out V4 slice ${LD} -r -arch armv4t foo.o -o fooa.o @@ -60,10 +63,10 @@ test-armv6: otool -hv fooc.o | grep V6 | ${FAIL_IF_EMPTY} nm fooc.o | grep _ccc | ${FAIL_IF_EMPTY} - # check -arch xscale pulls out xscale slice - ${LD} -r -arch xscale foo.o -o fooxx.o - otool -hv fooxx.o | grep XSCALE | ${FAIL_IF_EMPTY} - nm fooxx.o | grep _ddd | ${FAIL_IF_EMPTY} + # check -arch armv7 pulls out V7 slice + ${LD} -r -arch armv7 foo.o -o fooc.o + otool -hv fooc.o | grep V7 | ${FAIL_IF_EMPTY} + nm fooc.o | grep _ddd | ${FAIL_IF_EMPTY} ${PASS_IFF} true diff --git a/unit-tests/test-cases/cpu-sub-types/Makefile b/unit-tests/test-cases/cpu-sub-types/Makefile index 695ffba..f773c11 100644 --- a/unit-tests/test-cases/cpu-sub-types/Makefile +++ b/unit-tests/test-cases/cpu-sub-types/Makefile @@ -38,23 +38,30 @@ test-i386: test-x86_64: ${PASS_IFF} true -test-armv6: - gcc foo.c -arch armv4t -c -o foo-v4.o +test-armv6: test-arm +test-armv7: test-arm + +test-arm: + clang foo.c -arch armv4t -c -o foo-v4.o ${FAIL_IF_BAD_OBJ} foo-v4.o - gcc foo.c -arch armv5 -c -o foo-v5.o + clang foo.c -arch armv5 -c -o foo-v5.o ${FAIL_IF_BAD_OBJ} foo-v5.o - gcc foo.c -arch armv6 -c -o foo-v6.o + clang foo.c -arch armv6 -c -o foo-v6.o ${FAIL_IF_BAD_OBJ} foo-v6.o - gcc foo.c -arch xscale -c -o foo-xscale.o + clang foo.c -arch armv7 -c -o foo-v7.o + ${FAIL_IF_BAD_OBJ} foo-v7.o + clang foo.c -arch xscale -c -o foo-xscale.o ${FAIL_IF_BAD_OBJ} foo-xscale.o - gcc main.c -arch armv4t -c -o main-v4.o + clang main.c -arch armv4t -c -o main-v4.o ${FAIL_IF_BAD_OBJ} main-v4.o - gcc main.c -arch armv5 -c -o main-v5.o + clang main.c -arch armv5 -c -o main-v5.o ${FAIL_IF_BAD_OBJ} main-v5.o - gcc main.c -arch armv6 -c -o main-v6.o + clang main.c -arch armv6 -c -o main-v6.o ${FAIL_IF_BAD_OBJ} main-v6.o - gcc main.c -arch xscale -c -o main-xscale.o + clang main.c -arch xscale -c -o main-xscale.o ${FAIL_IF_BAD_OBJ} main-xscale.o + clang main.c -arch armv7 -c -o main-v7.o + ${FAIL_IF_BAD_OBJ} main-v7.o # check V4+V4 -> V4 ${LD} -r main-v4.o foo-v4.o -o all.o @@ -62,19 +69,19 @@ test-armv6: otool -hv all.o | grep V4T | ${FAIL_IF_EMPTY} # check V4+V5 -> V5 - ${LD} -r main-v4.o foo-v5.o -o all.o - ${FAIL_IF_BAD_OBJ} all.o - otool -hv all.o | grep V5 | ${FAIL_IF_EMPTY} + #${LD} -r main-v4.o foo-v5.o -o all.o + #${FAIL_IF_BAD_OBJ} all.o + #otool -hv all.o | grep V5 | ${FAIL_IF_EMPTY} # check V4+V6 -> V6 - ${LD} -r main-v4.o foo-v6.o -o all.o - ${FAIL_IF_BAD_OBJ} all.o - otool -hv all.o | grep V6 | ${FAIL_IF_EMPTY} + #${LD} -r main-v4.o foo-v6.o -o all.o + #${FAIL_IF_BAD_OBJ} all.o + #otool -hv all.o | grep V6 | ${FAIL_IF_EMPTY} # check V4+xscale -> xscale - ${LD} -r main-v4.o foo-xscale.o -o all.o - ${FAIL_IF_BAD_OBJ} all.o - otool -hv all.o | grep XSCALE | ${FAIL_IF_EMPTY} + #${LD} -r main-v4.o foo-xscale.o -o all.o + #${FAIL_IF_BAD_OBJ} all.o + #otool -hv all.o | grep XSCALE | ${FAIL_IF_EMPTY} # check V5+V5 -> V5 ${LD} -r main-v5.o foo-v5.o -o all.o @@ -82,24 +89,29 @@ test-armv6: otool -hv all.o | grep V5 | ${FAIL_IF_EMPTY} # check V5+V6 -> V6 - ${LD} -r main-v5.o foo-v6.o -o all.o - ${FAIL_IF_BAD_OBJ} all.o - otool -hv all.o | grep V6 | ${FAIL_IF_EMPTY} + #${LD} -r main-v5.o foo-v6.o -o all.o + #${FAIL_IF_BAD_OBJ} all.o + #otool -hv all.o | grep V6 | ${FAIL_IF_EMPTY} # check V5+xscale -> xscale - ${LD} -r main-v5.o foo-xscale.o -o all.o - ${FAIL_IF_BAD_OBJ} all.o - otool -hv all.o | grep XSCALE | ${FAIL_IF_EMPTY} + #${LD} -r main-v5.o foo-xscale.o -o all.o + #${FAIL_IF_BAD_OBJ} all.o + #otool -hv all.o | grep XSCALE | ${FAIL_IF_EMPTY} # check V6+V6 -> V6 ${LD} -r main-v6.o foo-v6.o -o all.o ${FAIL_IF_BAD_OBJ} all.o otool -hv all.o | grep V6 | ${FAIL_IF_EMPTY} - # check xscale+xscale -> xscale - ${LD} -r main-xscale.o foo-xscale.o -o all.o + # check V7+V7 -> V7 + ${LD} -r main-v7.o foo-v7.o -o all.o ${FAIL_IF_BAD_OBJ} all.o - otool -hv all.o | grep XSCALE | ${FAIL_IF_EMPTY} + otool -hv all.o | grep V7 | ${FAIL_IF_EMPTY} + + # check xscale+xscale -> xscale + #${LD} -r main-xscale.o foo-xscale.o -o all.o + #${FAIL_IF_BAD_OBJ} all.o + #otool -hv all.o | grep XSCALE | ${FAIL_IF_EMPTY} ${PASS_IFF} true diff --git a/unit-tests/test-cases/cstring-labels/Makefile b/unit-tests/test-cases/cstring-labels/Makefile index 1c6ee7d..76db33a 100644 --- a/unit-tests/test-cases/cstring-labels/Makefile +++ b/unit-tests/test-cases/cstring-labels/Makefile @@ -30,9 +30,12 @@ include ${TESTROOT}/include/common.makefile # makes them weak/hidden. Test to make sure that weak/hidden does not leak out. # +STRING_LABEL_COUNT = 0 + ifeq (${ARCH},x86_64) STRING_LABEL_COUNT = 3 -else +endif +ifeq (${ARCH},i386) STRING_LABEL_COUNT = 1 endif @@ -49,7 +52,7 @@ all: nm -nm foobar.o | grep __cstring | grep "weak private external" | ${FAIL_IF_STDIN} nm -m foobar.o | grep __cstring | wc -l | grep ${STRING_LABEL_COUNT} | ${FAIL_IF_EMPTY} ${CC} ${CCFLAGS} foo.o bar.o -dynamiclib -o libfoobar.dylib - nm -m libfoobar.dylib | grep __cstring | wc -l | grep 1 | ${FAIL_IF_EMPTY} + nm -m libfoobar.dylib | grep __cstring | wc -l | egrep '[01]' | ${FAIL_IF_EMPTY} ${PASS_IFF} /usr/bin/true clean: diff --git a/unit-tests/test-cases/custom-segment-layout/Makefile b/unit-tests/test-cases/custom-segment-layout/Makefile index 1dfb23f..e5b13c2 100644 --- a/unit-tests/test-cases/custom-segment-layout/Makefile +++ b/unit-tests/test-cases/custom-segment-layout/Makefile @@ -5,11 +5,16 @@ include ${TESTROOT}/include/common.makefile # # Check that a large page zero works # +ifeq (,${findstring "macosx","$(VERSION_NEW_LINKEDIT)"}) + SEG_ADDR = 0x20000000 +else + SEG_ADDR = 0xb8000000 +endif run: all all: - ${CC} ${CCFLAGS} main.c zero.s -o main -pagezero_size 0 -segaddr __TEXT 0xb8000000 -segaddr __MYZEROPAGE 0 + ${CC} ${CCFLAGS} main.c zero.s -o main -pagezero_size 0 -segaddr __TEXT $(SEG_ADDR) -segaddr __MYZEROPAGE 0 ${PASS_IFF_GOOD_MACHO} main clean: diff --git a/unit-tests/test-cases/custom-segment-layout/zero.s b/unit-tests/test-cases/custom-segment-layout/zero.s index 86bde83..94e1a50 100644 --- a/unit-tests/test-cases/custom-segment-layout/zero.s +++ b/unit-tests/test-cases/custom-segment-layout/zero.s @@ -2,9 +2,11 @@ .section __MYZEROPAGE,_data _min: .long 0 - +#if __arm__ + .zerofill __MYZEROPAGE,__zerofill,_padding,536870910 +#else .zerofill __MYZEROPAGE,__zerofill,_padding,2147483644 - +#endif diff --git a/unit-tests/test-cases/dead_strip-archive/Makefile b/unit-tests/test-cases/dead_strip-archive/Makefile index 1542d78..54812f3 100644 --- a/unit-tests/test-cases/dead_strip-archive/Makefile +++ b/unit-tests/test-cases/dead_strip-archive/Makefile @@ -36,7 +36,7 @@ all: ${CC} ${CCFLAGS} foo.c -c -o foo.o ${FAIL_IF_BAD_OBJ} foo.o libtool -static foo.o -o libfoo.a - ${CC} ${CCFLAGS} main.c -mdynamic-no-pic -Os libfoo.a -dead_strip -o main + ${CC} ${CCFLAGS} main.c -Os libfoo.a -dead_strip -o main ${PASS_IFF_GOOD_MACHO} main clean: diff --git a/unit-tests/test-cases/dead_strip-entry-archive/Makefile b/unit-tests/test-cases/dead_strip-entry-archive/Makefile index 1317c48..75b7988 100644 --- a/unit-tests/test-cases/dead_strip-entry-archive/Makefile +++ b/unit-tests/test-cases/dead_strip-entry-archive/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2009 Apple Inc. All rights reserved. +# Copyright (c) 2009-2011 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -25,6 +25,7 @@ include ${TESTROOT}/include/common.makefile # # ld64 can not find a -e entry point from an archive +# "start" and other special symbols should start out initially undefined # run: all @@ -32,6 +33,7 @@ run: all all: ${CC} ${CCFLAGS} foo.c -c -o foo.o libtool -static foo.o -o libfoo.a + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} bar.c -Os libfoo.a -o foo -Wl,-e,_foo -nostartfiles ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} bar.c -Os libfoo.a -dead_strip -o foo -Wl,-e,_foo ${PASS_IFF_GOOD_MACHO} foo diff --git a/unit-tests/test-cases/demangle/Makefile b/unit-tests/test-cases/demangle/Makefile index 6ed52b7..018bd4c 100644 --- a/unit-tests/test-cases/demangle/Makefile +++ b/unit-tests/test-cases/demangle/Makefile @@ -35,9 +35,9 @@ run: all all: ${CXX} ${CXXFLAGS} main.cxx -c - ${FAIL_IF_SUCCESS} ${LD} -arch ${ARCH} -dylib main.o -lSystem -o main 2>main1.fail + ${FAIL_IF_SUCCESS} ${LD} -arch ${ARCH} -dylib main.o ${LD_SYSROOT} ${LD_NEW_LINKEDIT} -lSystem -o main 2>main1.fail grep __ZN3Foo4doitEv main1.fail | ${FAIL_IF_EMPTY} - ${FAIL_IF_SUCCESS} ${LD} -arch ${ARCH} -dylib main.o -lSystem -demangle -o main 2>main2.fail + ${FAIL_IF_SUCCESS} ${LD} -arch ${ARCH} -dylib main.o ${LD_SYSROOT} ${LD_NEW_LINKEDIT} -lSystem -demangle -o main 2>main2.fail grep 'Foo::doit()' main2.fail | ${PASS_IFF_STDIN} diff --git a/unit-tests/test-cases/dependency-logging/Makefile b/unit-tests/test-cases/dependency-logging/Makefile index e861226..7f4cf64 100644 --- a/unit-tests/test-cases/dependency-logging/Makefile +++ b/unit-tests/test-cases/dependency-logging/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2010 Apple Inc. All rights reserved. +# Copyright (c) 2010-2011 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -35,6 +35,10 @@ all: rm -rf trace_ars export LD_TRACE_ARCHIVES=1 && export LD_TRACE_FILE=trace_ars && ${CC} ${CCFLAGS} main.c libfoo.a -o main grep 'libfoo.a' trace_ars | ${FAIL_IF_EMPTY} + export LD_TRACE_ARCHIVES=1 && export LD_TRACE_FILE=trace_ars2 && ${CC} ${CCFLAGS} main.c -Wl,-force_load,libfoo.a -o main + grep 'libfoo.a' trace_ars2 | ${FAIL_IF_EMPTY} + export LD_TRACE_ARCHIVES=1 && export LD_TRACE_FILE=trace_ars3 && ${CC} ${CCFLAGS} main.c libfoo.a -o main -all_load + grep 'libfoo.a' trace_ars3 | ${FAIL_IF_EMPTY} ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib rm -rf trace_dylibs export LD_TRACE_DYLIBS=1 && export LD_TRACE_FILE=trace_dylibs && ${CC} ${CCFLAGS} main.c libfoo.dylib -o main @@ -43,4 +47,4 @@ all: clean: - rm -rf foo.o libfoo.a libfoo.dylib main trace_ars trace_dylibs + rm -rf foo.o libfoo.a libfoo.dylib main trace_ars trace_ars2 trace_ars3 trace_dylibs diff --git a/unit-tests/test-cases/dwarf-debug-notes/Makefile b/unit-tests/test-cases/dwarf-debug-notes/Makefile index b4fa7ab..ea9dc56 100644 --- a/unit-tests/test-cases/dwarf-debug-notes/Makefile +++ b/unit-tests/test-cases/dwarf-debug-notes/Makefile @@ -33,9 +33,9 @@ include ${TESTROOT}/include/common.makefile run: all all: - ${CXX} ${CCXXFLAGS} -gdwarf-2 hello.cxx -c -o hello.o -mdynamic-no-pic - ${CXX} ${CCXXFLAGS} -gdwarf-2 other.cxx -c -o other.o -mdynamic-no-pic - ${CXX} ${CCXXFLAGS} -gdwarf-2 hello.o other.o -o hello + ${CXX} ${CCXXFLAGS} -gdwarf-2 hello.cxx -c -o hello.o + ${CXX} ${CCXXFLAGS} -gdwarf-2 other.cxx -c -o other.o + ${CXX} ${CCXXFLAGS} -gdwarf-2 hello.o other.o -o hello -Wl,-order_file,hello.order ${FAIL_IF_BAD_MACHO} hello nm -ap hello | ./stabs-filter.pl > hello.stabs ${PASS_IFF} diff hello.stabs expected-stabs diff --git a/unit-tests/test-cases/dwarf-debug-notes/expected-stabs b/unit-tests/test-cases/dwarf-debug-notes/expected-stabs index 95eb33d..f7eb45a 100644 --- a/unit-tests/test-cases/dwarf-debug-notes/expected-stabs +++ b/unit-tests/test-cases/dwarf-debug-notes/expected-stabs @@ -7,7 +7,7 @@ 0000 ENSYM 0000 BNSYM 0000 FUN __Z3fooi -0000 SOL CWD//header.h +0000 SOL CWD/header.h 0000 FUN 0000 ENSYM 0000 SO diff --git a/unit-tests/test-cases/dwarf-debug-notes/hello.order b/unit-tests/test-cases/dwarf-debug-notes/hello.order new file mode 100644 index 0000000..2860890 --- /dev/null +++ b/unit-tests/test-cases/dwarf-debug-notes/hello.order @@ -0,0 +1,16 @@ + +# order __data section so even if different compiler lays out data +# differently, linker lays out the same, so stabs are in same order +.my_non_standard_name_static +.my_non_standard_name +__ZZ3bariE8bar_init +_init +_uninit +__ZZ3bariE10bar_uninit +__ZL7suninit +__ZL5sinit + + + + + diff --git a/unit-tests/test-cases/dwarf-debug-notes/other.cxx b/unit-tests/test-cases/dwarf-debug-notes/other.cxx index b1b0e77..a6b403b 100644 --- a/unit-tests/test-cases/dwarf-debug-notes/other.cxx +++ b/unit-tests/test-cases/dwarf-debug-notes/other.cxx @@ -3,10 +3,10 @@ int uninit; int init = 1; -static int custom _asm(".my_non_standard_name") = 1; +static int custom __asm__(".my_non_standard_name") = 1; static int suninit; static int sinit=0; -static int scustominit _asm(".my_non_standard_name_static") = 1; +static int scustominit __asm__(".my_non_standard_name_static") = 1; int bar(int x) { @@ -19,9 +19,9 @@ int bar(int x) bar_init + bar_uninit + foo(x); } -extern void disappear() _asm("lbegone"); +extern void disappear() __asm__("lbegone"); void disappear() {} -extern void foo() _asm(".my_non_standard_function_name"); +extern void foo() __asm__(".my_non_standard_function_name"); void foo() { disappear(); } diff --git a/unit-tests/test-cases/dwarf-ignore/hello.c b/unit-tests/test-cases/dwarf-ignore/hello.c index e2f0fe9..4750b51 100644 --- a/unit-tests/test-cases/dwarf-ignore/hello.c +++ b/unit-tests/test-cases/dwarf-ignore/hello.c @@ -26,4 +26,5 @@ int main() { fprintf(stdout, "hello\n"); + return 0; } \ No newline at end of file diff --git a/unit-tests/test-cases/dwarf-strip-objc/Makefile b/unit-tests/test-cases/dwarf-strip-objc/Makefile index 3812933..866d178 100644 --- a/unit-tests/test-cases/dwarf-strip-objc/Makefile +++ b/unit-tests/test-cases/dwarf-strip-objc/Makefile @@ -10,7 +10,7 @@ IMAGE_INFO = "__image_info" ifeq ($(ARCH),x86_64) IMAGE_INFO = "__objc_imageinfo" endif -ifeq ($(ARCH),armv6) +ifeq ($(FILEARCH),arm) IMAGE_INFO = "__objc_imageinfo" endif diff --git a/unit-tests/test-cases/dwarf-strip/hello.c b/unit-tests/test-cases/dwarf-strip/hello.c index ddca819..68509d7 100644 --- a/unit-tests/test-cases/dwarf-strip/hello.c +++ b/unit-tests/test-cases/dwarf-strip/hello.c @@ -26,4 +26,5 @@ int main() { fprintf(stdout, "hello\n"); + return 0; } \ No newline at end of file diff --git a/unit-tests/test-cases/dylib-re-export-cycle/Makefile b/unit-tests/test-cases/dylib-re-export-cycle/Makefile index 057ac03..86248c2 100644 --- a/unit-tests/test-cases/dylib-re-export-cycle/Makefile +++ b/unit-tests/test-cases/dylib-re-export-cycle/Makefile @@ -30,9 +30,15 @@ SHELL = bash # use bash shell so we can redirect just stderr # OpenGL.framework and X11 both have a libGL.dylib which can cause ld to segfault if both are found # -run: all +all: all-${ARCH} -all: +all-i386: all-mac +all-x86_64: all-mac +all-armv6: all-good +all-armv7: all-good + + +all-mac: mkdir -p other ${CC} foo.c -dynamiclib -o other/libfoo.dylib -mmacosx-version-min=10.4 ${CC} bar.c -dynamiclib -o libbar.dylib other/libfoo.dylib -sub_library libfoo -mmacosx-version-min=10.4 @@ -40,7 +46,8 @@ all: ${CC} main.c libfoo.dylib -o main -L. 2>errmsg || true grep "cycle" errmsg | ${PASS_IFF_STDIN} - +all-good: + ${PASS_IFF} true clean: diff --git a/unit-tests/test-cases/efi-basic/Makefile b/unit-tests/test-cases/efi-basic/Makefile index 1f392c5..99f2fa6 100644 --- a/unit-tests/test-cases/efi-basic/Makefile +++ b/unit-tests/test-cases/efi-basic/Makefile @@ -59,7 +59,7 @@ all: libtool -static -o LibTest1.lib LibTest1.obj ${CC} -arch $(ARCH) -o MtocTest1.obj $(LOCAL_CC_FLAGS) -c MtocTest.c libtool -static -o MtocTest1.lib MtocTest1.obj - ld -arch $(ARCH) -o MtocTest1.macho -u __ModuleEntryPoint -e __ModuleEntryPoint $(LD_ARG) -preload -segalign 0x20 -pie -seg1addr 0x240 -map MtocTest1.map LibTest1.lib MtocTest1.lib + ${LD} -arch $(ARCH) -o MtocTest1.macho -u __ModuleEntryPoint -e __ModuleEntryPoint $(LD_ARG) -preload -segalign 0x20 -pie -seg1addr 0x240 -map MtocTest1.map LibTest1.lib MtocTest1.lib /usr/local/efi/bin/mtoc -subsystem application -align 0x20 -d MtocTest1.macho MtocTest1.macho MtocTest1.pecoff otool -rR MtocTest1.macho > otool-reloc.log otool -l MtocTest1.macho > otool-load.log diff --git a/unit-tests/test-cases/efi-basic/MtocTest.c b/unit-tests/test-cases/efi-basic/MtocTest.c index 5e3ebd5..1d076ba 100644 --- a/unit-tests/test-cases/efi-basic/MtocTest.c +++ b/unit-tests/test-cases/efi-basic/MtocTest.c @@ -12,7 +12,7 @@ int gWhyAreUninitializedGlobalsBad; void _ModuleEntryPoint(void) { - __asm__ __volatile__ ("int $3"); + //__asm__ __volatile__ ("int $3"); LibInit (); gWhyAreUninitializedGlobalsBad =1; diff --git a/unit-tests/test-cases/eh-stripped-symbols/Makefile b/unit-tests/test-cases/eh-stripped-symbols/Makefile index 15819f0..404cc41 100644 --- a/unit-tests/test-cases/eh-stripped-symbols/Makefile +++ b/unit-tests/test-cases/eh-stripped-symbols/Makefile @@ -7,9 +7,17 @@ include ${TESTROOT}/include/common.makefile # the linker can still process unwind info from .o files # correctly -run: all +ifeq (${FILEARCH},arm) + FILE_TYPE = KEXTBUNDLE +endif -all: + +all: all-${FILEARCH} +all-i386: all-zce +all-x86_64: all-zce +all-arm: all-good + +all-zce: ${CXX} ${CCXXFLAGS} main.cxx -g -c -o main1.o -Os #strip main1.o -u -s keep.exp -o main2.o ${LD} main1.o -r -x -exported_symbols_list keep.exp -o main2.o @@ -19,5 +27,8 @@ all: ${UNWINDDUMP} -arch ${ARCH} -no_symbols main2 > main2.unwind ${PASS_IFF} diff main1.unwind main2.unwind +all-good: + ${PASS_IFF} true + clean: rm -f main1* main2* diff --git a/unit-tests/test-cases/filelist/hello.c b/unit-tests/test-cases/filelist/hello.c index 14d9363..463663a 100644 --- a/unit-tests/test-cases/filelist/hello.c +++ b/unit-tests/test-cases/filelist/hello.c @@ -26,4 +26,5 @@ int main() { fprintf(stdout, "hello\n"); + return 0; } diff --git a/unit-tests/test-cases/flat-dylib/main.c b/unit-tests/test-cases/flat-dylib/main.c index 6014829..affbf90 100644 --- a/unit-tests/test-cases/flat-dylib/main.c +++ b/unit-tests/test-cases/flat-dylib/main.c @@ -30,4 +30,5 @@ int main() { foo(); fprintf(stdout, "hello\n"); + return 0; } diff --git a/unit-tests/test-cases/flat-main/main.c b/unit-tests/test-cases/flat-main/main.c index 6014829..affbf90 100644 --- a/unit-tests/test-cases/flat-main/main.c +++ b/unit-tests/test-cases/flat-main/main.c @@ -30,4 +30,5 @@ int main() { foo(); fprintf(stdout, "hello\n"); + return 0; } diff --git a/unit-tests/test-cases/function-starts/Makefile b/unit-tests/test-cases/function-starts/Makefile index 0b43cd5..531bb7a 100644 --- a/unit-tests/test-cases/function-starts/Makefile +++ b/unit-tests/test-cases/function-starts/Makefile @@ -22,8 +22,15 @@ all: # as dylib with aliases ${CC} ${CCFLAGS} main.c -dynamiclib -o libmain3.dylib -Wl,-function_starts -Wl,-alias,_mid,midalias ${DYLDINFO} -function_starts libmain3.dylib | grep _bar | ${FAIL_IF_EMPTY} + # as static main executable + ${CC} ${CCFLAGS} main.c -static -e _main -o main_static -nostdlib -Wl,-new_linker -Wl,-alias,_myexit,_exit + ${DYLDINFO} -function_starts main_static | grep _bar | ${FAIL_IF_STDIN} + ${CC} ${CCFLAGS} main.c -static -e _main -o main_static -nostdlib -Wl,-new_linker -Wl,-alias,_myexit,_exit -Wl,-function_starts + ${DYLDINFO} -function_starts main_static | grep _bar | ${FAIL_IF_EMPTY} + + ${PASS_IFF_GOOD_MACHO} main clean: - rm main libmain.dylib libmain2.dylib libmain3.dylib + rm main libmain.dylib libmain2.dylib libmain3.dylib main_static diff --git a/unit-tests/test-cases/function-starts/main.c b/unit-tests/test-cases/function-starts/main.c index 4ff1717..be69aaa 100644 --- a/unit-tests/test-cases/function-starts/main.c +++ b/unit-tests/test-cases/function-starts/main.c @@ -8,3 +8,7 @@ static void bar() { foo(); } int main() { bar(); return 0; } +#if __STATIC__ +void myexit() {} +#endif + diff --git a/unit-tests/test-cases/got-elimination/Makefile b/unit-tests/test-cases/got-elimination/Makefile index 6fae5ee..e3b687b 100644 --- a/unit-tests/test-cases/got-elimination/Makefile +++ b/unit-tests/test-cases/got-elimination/Makefile @@ -27,9 +27,9 @@ include ${TESTROOT}/include/common.makefile # Check that ld can remove non-lazy pointers for x86_64 # -all: all-${ARCH} +all: all-${FILEARCH} -all-armv6: all-true +all-arm: all-true all-ppc: all-true diff --git a/unit-tests/test-cases/header-pad/hello.c b/unit-tests/test-cases/header-pad/hello.c index 14d9363..463663a 100644 --- a/unit-tests/test-cases/header-pad/hello.c +++ b/unit-tests/test-cases/header-pad/hello.c @@ -26,4 +26,5 @@ int main() { fprintf(stdout, "hello\n"); + return 0; } diff --git a/unit-tests/test-cases/hello-world/hello.c b/unit-tests/test-cases/hello-world/hello.c index e2f0fe9..4750b51 100644 --- a/unit-tests/test-cases/hello-world/hello.c +++ b/unit-tests/test-cases/hello-world/hello.c @@ -26,4 +26,5 @@ int main() { fprintf(stdout, "hello\n"); + return 0; } \ No newline at end of file diff --git a/unit-tests/test-cases/implicit_dylib/Makefile b/unit-tests/test-cases/implicit_dylib/Makefile index c982885..b2cfe02 100644 --- a/unit-tests/test-cases/implicit_dylib/Makefile +++ b/unit-tests/test-cases/implicit_dylib/Makefile @@ -35,7 +35,7 @@ run: all all: ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib -install_name /usr/lib/libbar.dylib - ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -reexport_library libbar.dylib -sub_library libbar + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -Wl,-reexport_library,libbar.dylib -sub_library libbar # verify that main gets bar from libbar ${CC} ${CCFLAGS} main.c libfoo.dylib -o main -L. nm -m main | grep _bar | grep libbar | ${FAIL_IF_EMPTY} diff --git a/unit-tests/test-cases/kext-basic/Makefile b/unit-tests/test-cases/kext-basic/Makefile index 4844cd7..e33b66c 100644 --- a/unit-tests/test-cases/kext-basic/Makefile +++ b/unit-tests/test-cases/kext-basic/Makefile @@ -5,20 +5,22 @@ include ${TESTROOT}/include/common.makefile # # Sanity check that ld can link a kext # - +FILE_TYPE = OBJECT ifeq (${ARCH},x86_64) FILE_TYPE = KEXTBUNDLE -else - FILE_TYPE = OBJECT +endif +ifeq (${FILEARCH},arm) + FILE_TYPE = KEXTBUNDLE endif +#CC = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/clang run: all all: - ${CC} ${CCFLAGS} -static -fno-common -mkernel -c mykext.c -o mykext.o - ${CC} ${CCFLAGS} -static -fno-common -mkernel -c mykextinfo.c -o mykextinfo.o - unset LD_NO_CLASSIC_LINKER_STATIC && ${CC} ${CCFLAGS} -Wl,-kext mykext.o mykextinfo.o -nostdlib -lkmodc++ -lkmod -lcc_kext -o mykext -Wl,-w + ${CC} ${CCFLAGS} -static -mkernel -c mykext.c -o mykext.o + ${CC} ${CCFLAGS} -static -mkernel -c mykextinfo.c -o mykextinfo.o + ${CC} ${CCFLAGS} -Wl,-kext mykext.o mykextinfo.o -nostdlib -lkmodc++ -lkmod -lcc_kext -o mykext -Wl,-w -Wl,-new_linker otool -hv mykext | grep ${FILE_TYPE} | ${FAIL_IF_EMPTY} nm -nm mykext | grep '(undefined) external _extern_global' | ${FAIL_IF_EMPTY} nm -nm mykext | grep '(__DATA,__data) external _my_global' | ${FAIL_IF_EMPTY} diff --git a/unit-tests/test-cases/kext-undefined-export/Makefile b/unit-tests/test-cases/kext-undefined-export/Makefile new file mode 100644 index 0000000..b6acfd4 --- /dev/null +++ b/unit-tests/test-cases/kext-undefined-export/Makefile @@ -0,0 +1,35 @@ + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Sanity check that ld can link a kext +# + +EXPORT_LIST = mykext.exp +FILE_TYPE = OBJECT +ifeq (${ARCH},x86_64) + FILE_TYPE = KEXTBUNDLE +endif +ifeq (${FILEARCH},arm) + FILE_TYPE = KEXTBUNDLE +endif +ifeq (${ARCH},i386) + EXPORT_LIST = mykext-i386.exp +endif + +run: all + +all: + ${CC} ${CCFLAGS} -static -mkernel -c mykext.c -o mykext.o + ${CC} ${CCFLAGS} -static -mkernel -c mykextinfo.c -o mykextinfo.o + ${CC} ${CCFLAGS} -Wl,-kext mykext.o mykextinfo.o -nostdlib -lkmodc++ -lkmod -lcc_kext -exported_symbols_list $(EXPORT_LIST) -o mykext -Wl,-w -Wl,-new_linker + otool -hv mykext | grep ${FILE_TYPE} | ${FAIL_IF_EMPTY} + nm -nm mykext | grep '(undefined) external _extern_global' | ${FAIL_IF_EMPTY} + nm -nm mykext | grep '(undefined) external _foo' | ${FAIL_IF_STDIN} + nm -nm mykext | grep '(undefined) external _my_used_external_global' | ${FAIL_IF_EMPTY} + otool -rv mykext | grep '_extern_global' | ${FAIL_IF_EMPTY} + ${PASS_IFF} true + +clean: + rm -f mykext.o mykextinfo.o mykext diff --git a/unit-tests/test-cases/kext-undefined-export/mykext-i386.exp b/unit-tests/test-cases/kext-undefined-export/mykext-i386.exp new file mode 100644 index 0000000..2449aba --- /dev/null +++ b/unit-tests/test-cases/kext-undefined-export/mykext-i386.exp @@ -0,0 +1 @@ +_kmod_info diff --git a/unit-tests/test-cases/kext-undefined-export/mykext.c b/unit-tests/test-cases/kext-undefined-export/mykext.c new file mode 100644 index 0000000..d01996f --- /dev/null +++ b/unit-tests/test-cases/kext-undefined-export/mykext.c @@ -0,0 +1,27 @@ +#include + +extern void extern_func(); + +int my_global = 3; +extern int extern_global; +extern int extern_unused_global; +extern int my_used_external_global; + +kern_return_t mykext_start (kmod_info_t * ki, void * d) { + ++my_global; + ++extern_global; + ++my_used_external_global; + extern_func(); + return KERN_SUCCESS; +} + + +kern_return_t mykext_stop (kmod_info_t * ki, void * d) { + --my_global; + --extern_global; + return KERN_SUCCESS; +} + +void my_dead_code() { + ++extern_unused_global; +} \ No newline at end of file diff --git a/unit-tests/test-cases/kext-undefined-export/mykext.exp b/unit-tests/test-cases/kext-undefined-export/mykext.exp new file mode 100644 index 0000000..eccfe2a --- /dev/null +++ b/unit-tests/test-cases/kext-undefined-export/mykext.exp @@ -0,0 +1,3 @@ +_kmod_info +_foo +_my_used_external_global diff --git a/unit-tests/test-cases/kext-undefined-export/mykextinfo.c b/unit-tests/test-cases/kext-undefined-export/mykextinfo.c new file mode 100644 index 0000000..fe31e7b --- /dev/null +++ b/unit-tests/test-cases/kext-undefined-export/mykextinfo.c @@ -0,0 +1,12 @@ +#include + +extern kern_return_t _start(kmod_info_t *ki, void *data); +extern kern_return_t _stop(kmod_info_t *ki, void *data); +__private_extern__ kern_return_t mykext_start(kmod_info_t *ki, void *data); +__private_extern__ kern_return_t mykext_stop(kmod_info_t *ki, void *data); + +KMOD_EXPLICIT_DECL(com.yourcompany.kext.mykext, "1.0.0d1", _start, _stop) +__private_extern__ kmod_start_func_t *_realmain = mykext_start; +__private_extern__ kmod_stop_func_t *_antimain = mykext_stop; +__private_extern__ int _kext_apple_cc = __APPLE_CC__ ; + diff --git a/unit-tests/test-cases/label-on-end-of-section-order/Makefile b/unit-tests/test-cases/label-on-end-of-section-order/Makefile new file mode 100755 index 0000000..7ec72bc --- /dev/null +++ b/unit-tests/test-cases/label-on-end-of-section-order/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 + +# +# assert when wacky section order and label at end of section +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.s -c -o foo.o + ${PASS_IFF} ${LD} -r foo.o -arch ${ARCH} -o foo-r.o + +clean: + rm -f foo.o foo-r.o diff --git a/unit-tests/test-cases/label-on-end-of-section-order/foo.s b/unit-tests/test-cases/label-on-end-of-section-order/foo.s new file mode 100755 index 0000000..e0fd49c --- /dev/null +++ b/unit-tests/test-cases/label-on-end-of-section-order/foo.s @@ -0,0 +1,24 @@ + .lcomm _mybss ,4, 2 + + .text + .align 4 + .globl _main +_main: +#if __x86_64__ + movl $0, _mybss(%rip) +#elif __i386__ + movl $0, _mybss +#elif __arm__ + .long _mybss +#endif + + .section __DATA, _stuff + .align 4 +_start_stuff: + .long 0x0 + .long 0x0 +_end_stuff: + + + .subsections_via_symbols + \ No newline at end of file diff --git a/unit-tests/test-cases/llvm-integration/Makefile b/unit-tests/test-cases/llvm-integration/Makefile index 513dccc..8f33f36 100644 --- a/unit-tests/test-cases/llvm-integration/Makefile +++ b/unit-tests/test-cases/llvm-integration/Makefile @@ -27,8 +27,6 @@ include ${TESTROOT}/include/common.makefile SHELL = bash # use bash shell so we can redirect just stderr -LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH} -LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH} LLVMAR = /usr/local/bin/llvm-ar # @@ -52,10 +50,10 @@ zero: # MachO : main.c : Ufoo2, Ufoo3 # #echo "Zero..." - ${LLVMGCC} ${CCFLAGS} --emit-llvm a.c -c -o a.o - ${LLVMGCC} ${CCFLAGS} --emit-llvm b.c -c -o b.o - ${LLVMGCC} ${CCFLAGS} main.c -c -o main.o - ${LLVMGCC} ${CCFLAGS} a.o b.o main.o -o main.exe + ${CC} ${CCFLAGS} -flto a.c -c -o a.o + ${CC} ${CCFLAGS} -flto b.c -c -o b.o + ${CC} ${CCFLAGS} main.c -c -o main.o + ${CC} ${CCFLAGS} a.o b.o main.o -o main.exe ${PASS_IFF_GOOD_MACHO} main.exe one: @@ -65,10 +63,10 @@ one: # MachO : main1.c : Dfoo4, Ufoo2, Ufoo3 # #echo "One..." - ${LLVMGCC} ${CCFLAGS} --emit-llvm a1.c -c -o a1.o - ${LLVMGCC} ${CCFLAGS} --emit-llvm b1.c -c -o b1.o - ${LLVMGCC} ${CCFLAGS} main1.c -c -o main1.o - ${LLVMGCC} ${CCFLAGS} a1.o b1.o main1.o -o main1.exe + ${CC} ${CCFLAGS} -flto a1.c -c -o a1.o + ${CC} ${CCFLAGS} -flto b1.c -c -o b1.o + ${CC} ${CCFLAGS} main1.c -c -o main1.o + ${CC} ${CCFLAGS} a1.o b1.o main1.o -o main1.exe ${PASS_IFF_GOOD_MACHO} main1.exe two: @@ -78,10 +76,10 @@ two: # MachO : main2.c : Ufoo2, Ufoo3 # #echo "Two..." - ${LLVMGCC} ${CCFLAGS} --emit-llvm a2.c -c -o a2.o - ${LLVMGCC} ${CCFLAGS} --emit-llvm b2.c -c -o b2.o - ${LLVMGCC} ${CCFLAGS} main2.c -c -o main2.o - ${LLVMGCC} ${CCFLAGS} a2.o b2.o main2.o -o main2.exe + ${CC} ${CCFLAGS} -flto a2.c -c -o a2.o + ${CC} ${CCFLAGS} -flto b2.c -c -o b2.o + ${CC} ${CCFLAGS} main2.c -c -o main2.o + ${CC} ${CCFLAGS} a2.o b2.o main2.o -o main2.exe ${PASS_IFF_GOOD_MACHO} main2.exe three: @@ -91,10 +89,10 @@ three: # MachO : main3.c : Ufoo1, Ufoo2, Ubar # #echo "Three..." - ${LLVMGCC} ${CCFLAGS} --emit-llvm a3.c -c -o a3.o - ${LLVMGCC} ${CCFLAGS} --emit-llvm b3.c -c -o b3.o - ${LLVMGCC} ${CCFLAGS} main3.c -c -o main3.o - ${LLVMGCC} ${CCFLAGS} a3.o b3.o main3.o -o main3.exe + ${CC} ${CCFLAGS} -flto a3.c -c -o a3.o + ${CC} ${CCFLAGS} -flto b3.c -c -o b3.o + ${CC} ${CCFLAGS} main3.c -c -o main3.o + ${CC} ${CCFLAGS} a3.o b3.o main3.o -o main3.exe ${PASS_IFF_GOOD_MACHO} main3.exe four: @@ -104,10 +102,10 @@ four: # MachO : main4.c : Dfoo4, Ufoo2, Ufoo3 # #echo "Four..." - ${LLVMGCC} ${CCFLAGS} --emit-llvm a4.c -c -o a4.o - ${LLVMGCC} ${CCFLAGS} --emit-llvm b4.c -c -o b4.o - ${LLVMGCC} ${CCFLAGS} main4.c -c -o main4.o - ${LLVMGCC} ${CCFLAGS} a4.o b4.o main4.o -o main4.exe + ${CC} ${CCFLAGS} -flto a4.c -c -o a4.o + ${CC} ${CCFLAGS} -flto b4.c -c -o b4.o + ${CC} ${CCFLAGS} main4.c -c -o main4.o + ${CC} ${CCFLAGS} a4.o b4.o main4.o -o main4.exe ${PASS_IFF_GOOD_MACHO} main4.exe five: @@ -117,10 +115,10 @@ five: # MachO : main5.c : Dfoo3, Ufoo1 # #echo "verify that if call to mach-o is optimized away, mach-o func is dead stripped" - ${LLVMGCC} ${CCFLAGS} --emit-llvm a5.c -c -o a5.o - ${LLVMGCC} ${CCFLAGS} --emit-llvm b5.c -c -o b5.o - ${LLVMGCC} ${CCFLAGS} main5.c -c -o main5.o - ${LLVMGCC} ${CCFLAGS} a5.o b5.o main5.o -o main5.exe -Wl,-dead_strip + ${CC} ${CCFLAGS} -flto a5.c -c -o a5.o + ${CC} ${CCFLAGS} -flto b5.c -c -o b5.o + ${CC} ${CCFLAGS} main5.c -c -o main5.o + ${CC} ${CCFLAGS} a5.o b5.o main5.o -o main5.exe -Wl,-dead_strip ${OTOOL} -tV main5.exe | grep foo3 | ${PASS_IFF_EMPTY} ${PASS_IFF_GOOD_MACHO} main5.exe @@ -130,9 +128,9 @@ six: # MachO : main6.c : Ufoo1 # #echo "verify dead stripping of foo2 in main executable" - ${LLVMGCC} ${CCFLAGS} --emit-llvm a6.c -c -o a6.o - ${LLVMGCC} ${CCFLAGS} main6.c -c -o main6.o - ${LLVMGCC} ${CCFLAGS} a6.o main6.o -o main6.exe -Wl,-dead_strip + ${CC} ${CCFLAGS} -flto a6.c -c -o a6.o + ${CC} ${CCFLAGS} main6.c -c -o main6.o + ${CC} ${CCFLAGS} a6.o main6.o -o main6.exe -Wl,-dead_strip ${PASS_IFF_GOOD_MACHO} main6.exe ${OTOOL} -tV main6.exe | grep foo2 | ${PASS_IFF_EMPTY} @@ -143,10 +141,10 @@ seven: # MachO : main7.c : Ufoo1 # #echo "Seven..." - ${LLVMGCC} ${CCFLAGS} --emit-llvm a7.c -c -o a7.o - ${LLVMGCC} ${CCFLAGS} --emit-llvm b7.c -c -o b7.o - ${LLVMGCC} ${CCFLAGS} main7.c -c -o main7.o - ${LLVMGCC} ${CCFLAGS} a7.o b7.o main7.o -o main7.exe + ${CC} ${CCFLAGS} -flto a7.c -c -o a7.o + ${CC} ${CCFLAGS} -flto b7.c -c -o b7.o + ${CC} ${CCFLAGS} main7.c -c -o main7.o + ${CC} ${CCFLAGS} a7.o b7.o main7.o -o main7.exe ${PASS_IFF_GOOD_MACHO} main7.exe eight: @@ -155,9 +153,9 @@ eight: # MachO : main8.c : Ufoo1 # #echo "Eight..." - ${LLVMGCC} ${CCFLAGS} --emit-llvm a8.c -c -o a8.o - ${LLVMGCC} ${CCFLAGS} main8.c -c -o main8.o - ${LLVMGCC} ${CCFLAGS} a8.o main8.o -o main8.exe -Wl,-dead_strip + ${CC} ${CCFLAGS} -flto a8.c -c -o a8.o + ${CC} ${CCFLAGS} main8.c -c -o main8.o + ${CC} ${CCFLAGS} a8.o main8.o -o main8.exe -Wl,-dead_strip ${OTOOL} -tV main8.exe | grep foo2 | ${PASS_IFF_EMPTY} ${OTOOL} -tV main8.exe | grep unnamed_2_1 | ${PASS_IFF_EMPTY} @@ -167,9 +165,9 @@ nine: # MachO : main9.c : Ufoo1, Dfoo4 # #echo "Nine..." - ${LLVMGCC} ${CCFLAGS} --emit-llvm a9.c -c -o a9.o - ${LLVMGCC} ${CCFLAGS} main9.c -c -o main9.o - ${LLVMGCC} ${CCFLAGS} a9.o main9.o -o main9.exe -Wl,-dead_strip + ${CC} ${CCFLAGS} -flto a9.c -c -o a9.o + ${CC} ${CCFLAGS} main9.c -c -o main9.o + ${CC} ${CCFLAGS} a9.o main9.o -o main9.exe -Wl,-dead_strip ${OTOOL} -tV main9.exe | grep foo2 | ${PASS_IFF_EMPTY} ${OTOOL} -tV main9.exe | grep foo4 | ${PASS_IFF_EMPTY} ${OTOOL} -tV main9.exe | grep unnamed_2_1 | ${PASS_IFF_EMPTY} @@ -181,10 +179,10 @@ ten: # MachO : main10.c # #echo "Ten..." - ${LLVMGCC} ${CCFLAGS} --emit-llvm a10.c -c -o a10.o - ${LLVMGCC} ${CCFLAGS} --emit-llvm b10.c -c -o b10.o - ${LLVMGCC} ${CCFLAGS} main10.c -c -o main10.o - ${LLVMGCC} ${CCFLAGS} a10.o b10.o main10.o -o main10.exe + ${CC} ${CCFLAGS} -flto a10.c -c -o a10.o + ${CC} ${CCFLAGS} -flto b10.c -c -o b10.o + ${CC} ${CCFLAGS} main10.c -c -o main10.o + ${CC} ${CCFLAGS} a10.o b10.o main10.o -o main10.exe ${PASS_IFF_GOOD_MACHO} main10.exe eleven: @@ -193,9 +191,9 @@ eleven: # MachO : main11.c # #echo "Eleven..." - ${LLVMGCC} ${CCFLAGS} --emit-llvm a11.c -c -o a11.o - ${LLVMGCC} ${CCFLAGS} main11.c -c -o main11.o - ${LLVMGCC} ${CCFLAGS} a11.o main11.o -o main11.exe + ${CC} ${CCFLAGS} -flto a11.c -c -o a11.o + ${CC} ${CCFLAGS} main11.c -c -o main11.o + ${CC} ${CCFLAGS} a11.o main11.o -o main11.exe ${PASS_IFF_GOOD_MACHO} main11.exe twelve: @@ -204,9 +202,9 @@ twelve: # MachO : main12.c # #echo "verify tentative def in llvm .o referenced from mach-o" - ${LLVMGCC} ${CCFLAGS} --emit-llvm a12.c -c -o a12.o - ${LLVMGCC} ${CCFLAGS} main12.c -c -o main12.o - ${LLVMGCC} ${CCFLAGS} a12.o main12.o -o main12.exe + ${CC} ${CCFLAGS} -flto a12.c -c -o a12.o + ${CC} ${CCFLAGS} main12.c -c -o main12.o + ${CC} ${CCFLAGS} a12.o main12.o -o main12.exe ${PASS_IFF_GOOD_MACHO} main12.exe thirteen: @@ -215,49 +213,49 @@ thirteen: # MachO : main13.cc # # echo "Thirteen..." - ${LLVMGCC} ${CXXFLAGS} --emit-llvm a13.cc -c -o a13.o - ${LLVMGCC} ${CXXFLAGS} main13.cc -c -o main13.o - ${LLVMGXX} a13.o main13.o -o main13.exe + ${CC} ${CXXFLAGS} -flto a13.cc -c -o a13.o + ${CC} ${CXXFLAGS} main13.cc -c -o main13.o + ${CXX} a13.o main13.o -o main13.exe fourteen: # # llvm : a14.c b14.c # # echo "verify an used hidden symbol is removed from a dylib" - ${LLVMGCC} ${CXXFLAGS} -O4 -dynamiclib a14.c b14.c -o ab14.dylib + ${CC} ${CXXFLAGS} -O4 -dynamiclib a14.c b14.c -o ab14.dylib ${FAIL_IF_BAD_MACHO} ab14.dylib nm -m ab14.dylib | grep _X | ${PASS_IFF_EMPTY} fifteen: # echo "verify -dead_strip works with hidden symbols" - ${LLVMGCC} ${CXXFLAGS} -O4 -Wl,-dead_strip a15.c c15.c -o main15.exe - ${LLVMGCC} ${CXXFLAGS} -O4 a15.c c15.c -o main15.exe + ${CC} ${CXXFLAGS} -O4 -Wl,-dead_strip a15.c c15.c -o main15.exe + ${CC} ${CXXFLAGS} -O4 a15.c c15.c -o main15.exe ${FAIL_IF_BAD_MACHO} main15.exe - ${LLVMGCC} ${CXXFLAGS} -O4 -Wl,-dead_strip -dynamiclib a15.c b15.c -o a15.dylib - ${LLVMGCC} ${CXXFLAGS} -O4 a15.c b15.c -dynamiclib -o a15.dylib + ${CC} ${CXXFLAGS} -O4 -Wl,-dead_strip -dynamiclib a15.c b15.c -o a15.dylib + ${CC} ${CXXFLAGS} -O4 a15.c b15.c -dynamiclib -o a15.dylib ${FAIL_IF_BAD_MACHO} a15.dylib sixteen: # echo "verify -save-temps" - ${LLVMGCC} ${CCFLAGS} --emit-llvm main16.c -c -o main16.o - ${LLVMGCC} ${CCFLAGS} main16.o -o main16.exe -Wl,-save-temps + ${CC} ${CCFLAGS} -flto main16.c -c -o main16.o + ${CC} ${CCFLAGS} main16.o -o main16.exe -Wl,-save-temps ${PASS_IFF} test -e main16.exe.lto.bc ${PASS_IFF} test -e main16.exe.lto.o seventeen: # echo "verify ld -r of all bitcode files produces a bitcode file" - ${LLVMGCC} ${CCFLAGS} --emit-llvm a17.c -c -o a17.o - ${LLVMGCC} ${CCFLAGS} --emit-llvm b17.c -c -o b17.o + ${CC} ${CCFLAGS} -flto a17.c -c -o a17.o + ${CC} ${CCFLAGS} -flto b17.c -c -o b17.o ${LD} -arch ${ARCH} -r a17.o b17.o -o ab17.o file ab17.o | grep "Mach-O" | ${PASS_IFF_EMPTY} # echo "verify ld -r of bitcode and mach-o produces mach-o" - ${LLVMGCC} ${CCFLAGS} b17.c -c -o b17m.o + ${CC} ${CCFLAGS} b17.c -c -o b17m.o ${LD} -arch ${ARCH} -r a17.o b17m.o -o ab17m.o file ab17m.o | grep "Mach-O" | ${PASS_IFF_STDIN} eighteen: #echo verify ld -r -keep_private_externs works - ${LLVMGCC} ${CCFLAGS} --emit-llvm a18.c -c -o a18.o + ${CC} ${CCFLAGS} -flto a18.c -c -o a18.o ${LD} -arch ${ARCH} -r -keep_private_externs a18.o -o a18-rkpe.o nm -nm a18-rkpe.o | grep _common_hidden1 | grep "private external" | ${FAIL_IF_EMPTY} nm -nm a18-rkpe.o | grep _func_hidden2 | grep "private external" | ${FAIL_IF_EMPTY} @@ -268,16 +266,16 @@ eighteen: nineteen: #echo verify missing symbol error - ${LLVMGCC} ${CCFLAGS} --emit-llvm main19.c -c -o main19.o - ${FAIL_IF_SUCCESS} ${LLVMGCC} ${CCFLAGS} main19.o -o main19.exe 2>fail.log + ${CC} ${CCFLAGS} -flto main19.c -c -o main19.o + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main19.o -o main19.exe 2>fail.log grep _foo fail.log | ${PASS_IFF_STDIN} twenty: #echo verify bitcode files in archives works - #${LLVMGCC} ${CCFLAGS} --emit-llvm a20.c -c -o a20.o - #${LLVMGCC} ${CCFLAGS} --emit-llvm b20.c -c -o b20.o + #${CC} ${CCFLAGS} -flto a20.c -c -o a20.o + #${CC} ${CCFLAGS} -flto b20.c -c -o b20.o #libtool -static a20.o b20.o -o lib20.a - #${LLVMGCC} ${CCFLAGS} main20.c lib20.a -all_load -o main20.exe + #${CC} ${CCFLAGS} main20.c lib20.a -all_load -o main20.exe #nm main20.exe | grep _foo | ${PASS_IFF_STDIN} @@ -285,5 +283,5 @@ twenty: clean: - rm -rf *.o main*.exe big.* *.dylib main16.exe.lto.bc fail.log lib20.a main21.preload lib21.a + rm -rf *.o main*.exe big.* *.dylib main16.*.bc fail.log lib20.a main21.preload lib21.a diff --git a/unit-tests/test-cases/lto-archive-dylib/Makefile b/unit-tests/test-cases/lto-archive-dylib/Makefile index e538ee1..ba8eed7 100644 --- a/unit-tests/test-cases/lto-archive-dylib/Makefile +++ b/unit-tests/test-cases/lto-archive-dylib/Makefile @@ -30,16 +30,13 @@ include ${TESTROOT}/include/common.makefile # symbol from a dylib. # -LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH} -LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH} - run: all all: ${CC} ${CCFLAGS} foo.c -c -o foo.o libtool -static foo.o -o libfoo.a - ${LLVMGCC} ${CCFLAGS} --emit-llvm main.c -c -o main.o - ${LLVMGCC} ${CCFLAGS} main.o -o main libfoo.a + ${CC} ${CCFLAGS} -flto main.c -c -o main.o + ${CC} ${CCFLAGS} main.o -o main libfoo.a ${PASS_IFF_GOOD_MACHO} main clean: diff --git a/unit-tests/test-cases/lto-dead_strip-all-hidden/Makefile b/unit-tests/test-cases/lto-dead_strip-all-hidden/Makefile index 060e5a1..379a282 100644 --- a/unit-tests/test-cases/lto-dead_strip-all-hidden/Makefile +++ b/unit-tests/test-cases/lto-dead_strip-all-hidden/Makefile @@ -27,14 +27,12 @@ include ${TESTROOT}/include/common.makefile # Link Time Optimization error with 'dead code strip' + hidden symbol # -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 bar.c -c -o bar.o -fvisibility=hidden - ${LLVMGCC} ${CCFLAGS} -dynamiclib bar.o -o libbar.dylib -dead_strip + ${CC} ${CCFLAGS} -flto bar.c -c -o bar.o -fvisibility=hidden + ${CC} ${CCFLAGS} -dynamiclib bar.o -o libbar.dylib -dead_strip ${PASS_IFF_GOOD_MACHO} libbar.dylib clean: diff --git a/unit-tests/test-cases/lto-dead_strip-coalesce/Makefile b/unit-tests/test-cases/lto-dead_strip-coalesce/Makefile new file mode 100644 index 0000000..2f3f504 --- /dev/null +++ b/unit-tests/test-cases/lto-dead_strip-coalesce/Makefile @@ -0,0 +1,39 @@ +## +# 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 case for +# mach-o has coalescable strings which are dead stripped away +# but come back from bitcode file. + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -c -flto -o foo.o + ${CC} ${CCFLAGS} main.c -c -o main.o + ${CC} ${CCFLAGS} main.o foo.o -o main -dead_strip -framework Foundation + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main main.o foo.o diff --git a/unit-tests/test-cases/lto-dead_strip-coalesce/foo.c b/unit-tests/test-cases/lto-dead_strip-coalesce/foo.c new file mode 100644 index 0000000..f47f84d --- /dev/null +++ b/unit-tests/test-cases/lto-dead_strip-coalesce/foo.c @@ -0,0 +1,21 @@ + +#include +#include + +void foo1() +{ + CFStringGetLength(CFSTR("test1")); + strlen("str1"); +} + +void foo2() +{ + CFStringGetLength(CFSTR("test2")); + strlen("str2"); +} + +void foo3() +{ + CFStringGetLength(CFSTR("test3")); + strlen("str3"); +} diff --git a/unit-tests/test-cases/lto-dead_strip-coalesce/main.c b/unit-tests/test-cases/lto-dead_strip-coalesce/main.c new file mode 100644 index 0000000..ecbd4f2 --- /dev/null +++ b/unit-tests/test-cases/lto-dead_strip-coalesce/main.c @@ -0,0 +1,34 @@ + +#include +#include + + +extern void foo1(); + + +void t1() +{ + CFStringGetLength(CFSTR("test1")); + strlen("str1"); +} + +void t2() +{ + CFStringGetLength(CFSTR("test2")); + strlen("str2"); +} + +void t3() +{ + CFStringGetLength(CFSTR("test3")); + strlen("str3"); +} + + +int main() +{ + t2(); + foo1(); + return 0; +} + diff --git a/unit-tests/test-cases/lto-dead_strip-objc/Makefile b/unit-tests/test-cases/lto-dead_strip-objc/Makefile index ccd5b59..afd6948 100644 --- a/unit-tests/test-cases/lto-dead_strip-objc/Makefile +++ b/unit-tests/test-cases/lto-dead_strip-objc/Makefile @@ -27,14 +27,12 @@ include ${TESTROOT}/include/common.makefile # Link Time Optimization crashes linker with 'dead code strip' + hidden symbol # -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 foo.m -c -o foo.o -fvisibility=hidden - ${LLVMGCC} ${CCFLAGS} -dynamiclib foo.o -o libfoo.dylib -dead_strip -framework Foundation + ${CC} ${CCFLAGS} -flto foo.m -c -o foo.o -fvisibility=hidden + ${CC} ${CCFLAGS} -dynamiclib foo.o -o libfoo.dylib -dead_strip -framework Foundation ${PASS_IFF_GOOD_MACHO} libfoo.dylib clean: diff --git a/unit-tests/test-cases/lto-dead_strip-some-hidden/Makefile b/unit-tests/test-cases/lto-dead_strip-some-hidden/Makefile index d563c92..ad1fe8b 100644 --- a/unit-tests/test-cases/lto-dead_strip-some-hidden/Makefile +++ b/unit-tests/test-cases/lto-dead_strip-some-hidden/Makefile @@ -27,14 +27,12 @@ include ${TESTROOT}/include/common.makefile # Link Time Optimization crashes linker with 'dead code strip' + hidden symbol # -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 bar.c -c -o bar.o -fvisibility=hidden - ${LLVMGCC} ${CCFLAGS} -dynamiclib bar.o -o libbar.dylib -dead_strip + ${CC} ${CCFLAGS} -flto bar.c -c -o bar.o -fvisibility=hidden + ${CC} ${CCFLAGS} -dynamiclib bar.o -o libbar.dylib -dead_strip ${PASS_IFF_GOOD_MACHO} libbar.dylib clean: diff --git a/unit-tests/test-cases/lto-dead_strip-tentative/Makefile b/unit-tests/test-cases/lto-dead_strip-tentative/Makefile index fa0a02b..5a54bcf 100644 --- a/unit-tests/test-cases/lto-dead_strip-tentative/Makefile +++ b/unit-tests/test-cases/lto-dead_strip-tentative/Makefile @@ -27,17 +27,15 @@ include ${TESTROOT}/include/common.makefile # Link Time Optimization error with tentative defs and -dead_strip # -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 foo.c -c -o foo.o - ${LLVMGCC} ${CCFLAGS} --emit-llvm bar.c -c -o bar.o - ${LLVMGCC} ${CCFLAGS} --emit-llvm baz.c -c -o baz.o - ${LLVMGCC} ${CCFLAGS} main.c -c -o main.o - ${LLVMGCC} ${CCFLAGS} main.o foo.o bar.o baz.o -o main -dead_strip + ${CC} ${CCFLAGS} -flto foo.c -c -o foo.o + ${CC} ${CCFLAGS} -flto bar.c -c -o bar.o + ${CC} ${CCFLAGS} -flto baz.c -c -o baz.o + ${CC} ${CCFLAGS} main.c -c -o main.o + ${CC} ${CCFLAGS} main.o foo.o bar.o baz.o -o main -dead_strip ${PASS_IFF_GOOD_MACHO} main clean: diff --git a/unit-tests/test-cases/lto-dead_strip-unused/Makefile b/unit-tests/test-cases/lto-dead_strip-unused/Makefile index a518d82..b27291a 100644 --- a/unit-tests/test-cases/lto-dead_strip-unused/Makefile +++ b/unit-tests/test-cases/lto-dead_strip-unused/Makefile @@ -27,15 +27,12 @@ include ${TESTROOT}/include/common.makefile # LTO with 'dead code strip' can't ignore unused functions with undefined references # -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 bar.c -c -o bar.o - ${LLVMGCC} ${CCFLAGS} --emit-llvm main.c -c -o main.o - ${LLVMGCC} ${CCFLAGS} -dead_strip main.o bar.o -o main + ${CC} ${CCFLAGS} -flto bar.c -c -o bar.o + ${CC} ${CCFLAGS} -flto main.c -c -o main.o + ${CC} ${CCFLAGS} -dead_strip main.o bar.o -o main ${PASS_IFF_GOOD_MACHO} main clean: diff --git a/unit-tests/test-cases/lto-llvm-options/Makefile b/unit-tests/test-cases/lto-llvm-options/Makefile index 1edf08a..095405a 100644 --- a/unit-tests/test-cases/lto-llvm-options/Makefile +++ b/unit-tests/test-cases/lto-llvm-options/Makefile @@ -28,16 +28,13 @@ include ${TESTROOT}/include/common.makefile # results in foo() not being inlined # -LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH} -LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH} - run: all all: - ${LLVMGCC} ${CCFLAGS} --emit-llvm main.c -c -o main.o -fvisibility=hidden - ${LLVMGCC} ${CCFLAGS} main.o -o main + ${CC} ${CCFLAGS} -flto main.c -c -o main.o -fvisibility=hidden + ${CC} ${CCFLAGS} main.o -o main nm main | grep _foo | ${FAIL_IF_STDIN} - ${LLVMGCC} ${CCFLAGS} main.o -o main2 -fvisibility=hidden -Wl,-mllvm -Wl,--disable-inlining + ${CC} ${CCFLAGS} main.o -o main2 -fvisibility=hidden -Wl,-mllvm -Wl,--disable-inlining nm main2 | grep _foo | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} main diff --git a/unit-tests/test-cases/lto-objc-archive/Makefile b/unit-tests/test-cases/lto-objc-archive/Makefile index 57fae1d..968671a 100644 --- a/unit-tests/test-cases/lto-objc-archive/Makefile +++ b/unit-tests/test-cases/lto-objc-archive/Makefile @@ -28,8 +28,6 @@ include ${TESTROOT}/include/common.makefile # Check that LTO can bring in an ObjC class from an archive member # -LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH} -LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH} run: all @@ -39,8 +37,8 @@ all: ${CC} ${CCFLAGS} foo2.c -c -o foo2.o ${CC} ${CCFLAGS} bar2.c -c -o bar2.o libtool -static foo.o bar.o foo2.o bar2.o -o libfoobar.a - ${LLVMGCC} ${CCFLAGS} --emit-llvm main.m -c -o main.o - ${LLVMGCC} ${CCFLAGS} main.o -o main libfoobar.a -framework Foundation + ${CC} ${CCFLAGS} -flto main.m -c -o main.o + ${CC} ${CCFLAGS} main.o -o main libfoobar.a -framework Foundation ${PASS_IFF_GOOD_MACHO} main clean: diff --git a/unit-tests/test-cases/lto-objc-image-info/Makefile b/unit-tests/test-cases/lto-objc-image-info/Makefile index 8b13444..1a6f059 100644 --- a/unit-tests/test-cases/lto-objc-image-info/Makefile +++ b/unit-tests/test-cases/lto-objc-image-info/Makefile @@ -27,16 +27,13 @@ include ${TESTROOT}/include/common.makefile # trivial Objective-C app fails when using libLTO # -LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH} -LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH} - IMAGE_INFO = "__image_info" ifeq ($(ARCH),x86_64) IMAGE_INFO = "__objc_imageinfo" endif -ifeq ($(ARCH),armv6) +ifeq ($(FILEARCH),arm) IMAGE_INFO = "__objc_imageinfo" endif @@ -44,8 +41,8 @@ endif run: all all: - ${LLVMGCC} ${CCFLAGS} --emit-llvm main.m -c -o main.o - ${LLVMGCC} ${CCFLAGS} main.o -o main -framework Foundation + ${CC} ${CCFLAGS} -flto main.m -c -o main.o + ${CC} ${CCFLAGS} main.o -o main -framework Foundation size -l main | grep ${IMAGE_INFO} | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} main diff --git a/unit-tests/test-cases/lto-object_path/Makefile b/unit-tests/test-cases/lto-object_path/Makefile index aeda85d..180cfb3 100644 --- a/unit-tests/test-cases/lto-object_path/Makefile +++ b/unit-tests/test-cases/lto-object_path/Makefile @@ -28,13 +28,12 @@ include ${TESTROOT}/include/common.makefile # being produced. # -LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH} run: all all: - ${LLVMGCC} ${CCFLAGS} --emit-llvm main.c -c -o main.o -fvisibility=hidden - ${LLVMGCC} ${CCFLAGS} main.o -o main -Wl,-object_path_lto,`pwd`/main.tmp.o + ${CC} ${CCFLAGS} -flto main.c -c -o main.o -fvisibility=hidden + ${CC} ${CCFLAGS} main.o -o main -Wl,-object_path_lto,`pwd`/main.tmp.o nm main.tmp.o | grep _main | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} main diff --git a/unit-tests/test-cases/lto-weak-native-override/Makefile b/unit-tests/test-cases/lto-weak-native-override/Makefile index 55ac0f1..2be86f3 100644 --- a/unit-tests/test-cases/lto-weak-native-override/Makefile +++ b/unit-tests/test-cases/lto-weak-native-override/Makefile @@ -28,15 +28,12 @@ include ${TESTROOT}/include/common.makefile # and mach-o has a strong _foo. # -LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH} -LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH} - run: all all: ${CC} ${CCFLAGS} foo.c -c -o foo.o - ${LLVMGCC} ${CCFLAGS} --emit-llvm main.c -c -o main.o - ${LLVMGCC} ${CCFLAGS} main.o foo.o -o main + ${CC} ${CCFLAGS} -flto main.c -c -o main.o + ${CC} ${CCFLAGS} main.o foo.o -o main otool -Iv main | grep _abort | ${FAIL_IF_STDIN} ${PASS_IFF_GOOD_MACHO} main diff --git a/unit-tests/test-cases/lto-weak-native-override/main.c b/unit-tests/test-cases/lto-weak-native-override/main.c index 6a433db..5f5d498 100644 --- a/unit-tests/test-cases/lto-weak-native-override/main.c +++ b/unit-tests/test-cases/lto-weak-native-override/main.c @@ -12,6 +12,6 @@ __attribute__((visibility("hidden"),weak)) void foo() int main() { foo(); - + return 0; } diff --git a/unit-tests/test-cases/lto-weak_import/Makefile b/unit-tests/test-cases/lto-weak_import/Makefile index e53ad48..ff4f7cc 100644 --- a/unit-tests/test-cases/lto-weak_import/Makefile +++ b/unit-tests/test-cases/lto-weak_import/Makefile @@ -28,15 +28,12 @@ include ${TESTROOT}/include/common.makefile # Check that LTO can bring in an ObjC class from an archive member # -LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH} -LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH} - run: all all: ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib - ${LLVMGCC} ${CCFLAGS} --emit-llvm main.c -c -o main.o - ${LLVMGCC} ${CCFLAGS} main.o -o main libfoo.dylib + ${CC} ${CCFLAGS} -flto main.c -c -o main.o + ${CC} ${CCFLAGS} main.o -o main libfoo.dylib ${PASS_IFF_GOOD_MACHO} main clean: diff --git a/unit-tests/test-cases/merge_zero_fill_sections/Makefile b/unit-tests/test-cases/merge_zero_fill_sections/Makefile new file mode 100755 index 0000000..bec9e76 --- /dev/null +++ b/unit-tests/test-cases/merge_zero_fill_sections/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 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 the -merge_zero_fill_sections works +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c -o main -Wl,-merge_zero_fill_sections + size -l main | grep __bss | ${FAIL_IF_STDIN} + size -l main | grep __common | ${FAIL_IF_STDIN} + size -l main | grep __zerofill | grep "offset 0" | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm main diff --git a/unit-tests/test-cases/merge_zero_fill_sections/main.c b/unit-tests/test-cases/merge_zero_fill_sections/main.c new file mode 100755 index 0000000..87528ec --- /dev/null +++ b/unit-tests/test-cases/merge_zero_fill_sections/main.c @@ -0,0 +1,41 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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@ + */ +#include + +static int a[2000]; +int b[2000]; + +extern void* zerofill_start __asm("section$start$__DATA$__zerofill"); +extern void* zerofill__end __asm("section$end$__DATA$__zerofill"); + +void* start = &zerofill_start; +void* end = &zerofill__end; + +int main() +{ + a[0] = 0; + b[0] = 0; + return 0; +} + diff --git a/unit-tests/test-cases/non-lazy-sections-r/foo.s b/unit-tests/test-cases/non-lazy-sections-r/foo.s index d307880..1749c7e 100644 --- a/unit-tests/test-cases/non-lazy-sections-r/foo.s +++ b/unit-tests/test-cases/non-lazy-sections-r/foo.s @@ -1,4 +1,5 @@ .text + .align 4 .globl _test _test: #if __i386__ diff --git a/unit-tests/test-cases/objc-category-warning/Makefile b/unit-tests/test-cases/objc-category-warning/Makefile new file mode 100644 index 0000000..95cc91d --- /dev/null +++ b/unit-tests/test-cases/objc-category-warning/Makefile @@ -0,0 +1,55 @@ +## +# 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 + +# +# Verify optimization in which categories are merged into classes +# +OPTIONS = + +ifeq ($(ARCH),i386) + OPTIONS = -fobjc-abi-version=2 -Wl,-objc_abi_version,2 +endif + +all: all-${FILEARCH} + +all-ppc: + ${PASS_IFF} true + +all-i386: all-rest +all-x86_64: all-rest +all-arm: all-rest + +all-rest: + ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat.m -DOVERRIDE_CLASS=1 -framework Foundation -o libfoo.dylib 2> warnings1.txt + grep warning warnings1.txt | grep instance_method | ${FAIL_IF_EMPTY} + grep warning warnings1.txt | grep class_method | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat.m copycat.m -framework Foundation -o libfoo2.dylib 2> warnings2.txt + grep warning warnings2.txt | grep instance_method_fromcat | ${FAIL_IF_EMPTY} + grep warning warnings2.txt | grep class_method_fromcat | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libfoo2.dylib + +clean: + rm -rf lib*.dylib warnings*.txt \ No newline at end of file diff --git a/unit-tests/test-cases/objc-category-warning/cat.m b/unit-tests/test-cases/objc-category-warning/cat.m new file mode 100644 index 0000000..e60f37b --- /dev/null +++ b/unit-tests/test-cases/objc-category-warning/cat.m @@ -0,0 +1,24 @@ +#include + +@interface Foo : NSObject +-(void) method1; +@end + +@interface Foo(fromcat) +- (void) instance_method_fromcat; ++ (void) class_method_fromcat; +#if OVERRIDE_CLASS +- (void) instance_method; ++ (void) class_method; +#endif +@end + +@implementation Foo(fromcat) +- (void) instance_method_fromcat {} ++ (void) class_method_fromcat {} +#if OVERRIDE_CLASS +- (void) instance_method {} ++ (void) class_method {} +#endif +@end + diff --git a/unit-tests/test-cases/objc-category-warning/copycat.m b/unit-tests/test-cases/objc-category-warning/copycat.m new file mode 100644 index 0000000..309be02 --- /dev/null +++ b/unit-tests/test-cases/objc-category-warning/copycat.m @@ -0,0 +1,16 @@ +#include + +@interface Foo : NSObject +-(void) method1; +@end + +@interface Foo(copycat) +- (void) instance_method_fromcat; ++ (void) class_method_fromcat; +@end + +@implementation Foo(copycat) +- (void) instance_method_fromcat {} ++ (void) class_method_fromcat {} +@end + diff --git a/unit-tests/test-cases/objc-category-warning/foo.m b/unit-tests/test-cases/objc-category-warning/foo.m new file mode 100644 index 0000000..2a75794 --- /dev/null +++ b/unit-tests/test-cases/objc-category-warning/foo.m @@ -0,0 +1,13 @@ +#include + +@interface Foo : NSObject +-(void) instance_method; ++(void) class_method; +@end + + +@implementation Foo +-(void) instance_method {} ++(void) class_method {} +@end + diff --git a/unit-tests/test-cases/objc-class-alias/Makefile b/unit-tests/test-cases/objc-class-alias/Makefile index c8c1b27..52840c9 100644 --- a/unit-tests/test-cases/objc-class-alias/Makefile +++ b/unit-tests/test-cases/objc-class-alias/Makefile @@ -30,6 +30,9 @@ CLASS_NAME_FOO = .objc_class_name_Foo ifeq (${ARCH},x86_64) CLASS_NAME_FOO = '_OBJC_CLASS_$$_Foo' endif +ifeq (${FILEARCH},arm) + CLASS_NAME_FOO = '_OBJC_CLASS_$$_Foo' +endif run: all diff --git a/unit-tests/test-cases/objc-gc-checks/Makefile b/unit-tests/test-cases/objc-gc-checks/Makefile index b18012a..67e04a3 100644 --- a/unit-tests/test-cases/objc-gc-checks/Makefile +++ b/unit-tests/test-cases/objc-gc-checks/Makefile @@ -31,18 +31,19 @@ IMAGE_INFO = "__image_info" ifeq ($(ARCH),x86_64) IMAGE_INFO = "__objc_imageinfo" endif -ifeq ($(ARCH),armv6) - IMAGE_INFO = "__objc_imageinfo" -endif +test: test-${FILEARCH} +test-i386: test-macosx +test-x86_64: test-macosx +test-arm: test-good # # Validate that the linker catches illegal combinations of .o files # compiled with different GC settings. # -test: +test-macosx: ${CC} ${CCFLAGS} foo.m -c -o foo.o ${FAIL_IF_BAD_OBJ} foo.o @@ -127,7 +128,9 @@ test: ${CC} ${CCFLAGS} foo.o runtime.c -dynamiclib -o libfoo.dylib ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} bar-gc-only.o runtime.c -dynamiclib libfoo.dylib -o libbar.dylib 2> fail.log + ${PASS_IFF} true +test-good: ${PASS_IFF} true clean: diff --git a/unit-tests/test-cases/objc-properties/Makefile b/unit-tests/test-cases/objc-properties/Makefile index 999bf2f..8788364 100644 --- a/unit-tests/test-cases/objc-properties/Makefile +++ b/unit-tests/test-cases/objc-properties/Makefile @@ -12,7 +12,6 @@ run: all all: ${CC} ${CCFLAGS} test.m -dynamiclib -framework Foundation -o libtest.dylib - ${CC} ${CCFLAGS} test.m -dynamiclib -framework Foundation -o libtest.dylib -Wl,-no_compact_linkedit -ldylib1.o ${PASS_IFF_GOOD_MACHO} libtest.dylib clean: diff --git a/unit-tests/test-cases/order_file/extra.s b/unit-tests/test-cases/order_file/extra.s index eba52c0..9a6b194 100644 --- a/unit-tests/test-cases/order_file/extra.s +++ b/unit-tests/test-cases/order_file/extra.s @@ -1,6 +1,7 @@ .text + .align 4 .globl _foo1 _foo1: nop diff --git a/unit-tests/test-cases/prebound-main/Makefile b/unit-tests/test-cases/prebound-main/Makefile deleted file mode 100644 index 285d767..0000000 --- a/unit-tests/test-cases/prebound-main/Makefile +++ /dev/null @@ -1,31 +0,0 @@ - -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -SHELL = bash # use bash shell so we can redirect just stderr - -# -# Verify -prebind for 10.3 make ppc prebound and all others not prebound -# - -ifeq (,${findstring 64,$(ARCH)}) - ifeq (${ARCH},i386) - KEYWORD = NOUNDEFS - else - KEYWORD = PREBOUND - endif -else - KEYWORD = NOUNDEFS -endif - - -run: all - -all: - # SnowLeopard is missing libmx.dylib which gcc thinks it needs - ${CC} ${CCFLAGS} main.c -o main -prebind -mmacosx-version-min=10.3 -nostdlib -lcrt1.o -lSystem - otool -hv main | grep ${KEYWORD} | ${FAIL_IF_EMPTY} - ${PASS_IFF_GOOD_MACHO} main - -clean: - rm main diff --git a/unit-tests/test-cases/prebound-split-seg/Makefile b/unit-tests/test-cases/prebound-split-seg/Makefile index 07bce59..67ed3fa 100644 --- a/unit-tests/test-cases/prebound-split-seg/Makefile +++ b/unit-tests/test-cases/prebound-split-seg/Makefile @@ -29,11 +29,22 @@ SHELL = bash # use bash shell so we can redirect just stderr # The point of this test is to build a prebound split-seg library # -run: all +all: all-${ARCH} -all: +all-i386: all-32 +all-ppc: all-32 +all-x86_64: all-good +all-armv6: all-good +all-armv7: all-good + + +all-good: + ${PASS_IFF} true + + +all-32: ${CC} ${CCFLAGS} -seg_addr_table address_table -prebind bar.c -dynamiclib -o libbar.dylib -install_name /foo/bar/libbar.dylib ${PASS_IFF_GOOD_MACHO} libbar.dylib clean: - rm *.dylib + rm -rf *.dylib diff --git a/unit-tests/test-cases/re-export-and-use/Makefile b/unit-tests/test-cases/re-export-and-use/Makefile index b220580..b4de574 100644 --- a/unit-tests/test-cases/re-export-and-use/Makefile +++ b/unit-tests/test-cases/re-export-and-use/Makefile @@ -38,14 +38,14 @@ all: ${CC} ${CCFLAGS} -dynamiclib baz.c -o libbaz.dylib # verify uses of symbols from re-exported dylibs have ordinal that allows dyld to search amoung re-exported dylibs ${CC} ${CCFLAGS} -dynamiclib foo.c -DUSE_BAZ -o libfoo2.dylib -Wl,-reexport_library libpub.dylib -Wl,-reexport_library libbar.dylib -Wl,-reexport_library libbaz.dylib - dyldinfo -bind libfoo2.dylib | grep _bar | grep this-image | ${FAIL_IF_EMPTY} - dyldinfo -lazy_bind libfoo2.dylib | grep _bar | grep this-image | ${FAIL_IF_EMPTY} - dyldinfo -bind libfoo2.dylib | grep _baz | grep this-image | ${FAIL_IF_EMPTY} - dyldinfo -lazy_bind libfoo2.dylib | grep _baz | grep this-image | ${FAIL_IF_EMPTY} + ${DYLDINFO} -bind libfoo2.dylib | grep _bar | grep this-image | ${FAIL_IF_EMPTY} + ${DYLDINFO} -lazy_bind libfoo2.dylib | grep _bar | grep this-image | ${FAIL_IF_EMPTY} + ${DYLDINFO} -bind libfoo2.dylib | grep _baz | grep this-image | ${FAIL_IF_EMPTY} + ${DYLDINFO} -lazy_bind libfoo2.dylib | grep _baz | grep this-image | ${FAIL_IF_EMPTY} # verify that if only one non-publc re-exported dylib, don't use magic ordinal ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo1.dylib -Wl,-reexport_library libpub.dylib -Wl,-reexport_library libbar.dylib - dyldinfo -bind libfoo1.dylib | grep _bar | grep this-image | ${FAIL_IF_STDIN} - dyldinfo -lazy_bind libfoo1.dylib | grep _bar | grep this-image | ${FAIL_IF_STDIN} + ${DYLDINFO} -bind libfoo1.dylib | grep _bar | grep this-image | ${FAIL_IF_STDIN} + ${DYLDINFO} -lazy_bind libfoo1.dylib | grep _bar | grep this-image | ${FAIL_IF_STDIN} ${PASS_IFF_GOOD_MACHO} libfoo2.dylib diff --git a/unit-tests/test-cases/re-export-cases/Makefile b/unit-tests/test-cases/re-export-cases/Makefile index 64ec112..59887aa 100644 --- a/unit-tests/test-cases/re-export-cases/Makefile +++ b/unit-tests/test-cases/re-export-cases/Makefile @@ -28,136 +28,139 @@ include ${TESTROOT}/include/common.makefile # -run: all +all: all-${ARCH} -all: +all-i386: all-new all-old +all-x86_64: all-new all-old +all-armv6: all-new +all-armv7: all-new -# -sub_library for 10.4 - ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -mmacosx-version-min=10.4 - ${FAIL_IF_BAD_MACHO} libbar.dylib - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lbar -L. -sub_library libbar -mmacosx-version-min=10.4 - ${FAIL_IF_BAD_MACHO} libfoo.dylib - otool -lv libfoo.dylib | grep LC_SUB_LIBRARY | ${FAIL_IF_EMPTY} - otool -lv libfoo.dylib | grep LC_REEXPORT_DYLIB | ${FAIL_IF_STDIN} - -# -sub_library for 10.5 - ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -mmacosx-version-min=10.5 + +all-new: + # -sub_library for 10.5 + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib ${VERSION_NEW_LINKEDIT} ${FAIL_IF_BAD_MACHO} libbar.dylib - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lbar -L. -sub_library libbar -mmacosx-version-min=10.5 + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lbar -L. -sub_library libbar ${VERSION_NEW_LINKEDIT} ${FAIL_IF_BAD_MACHO} libfoo.dylib otool -lv libfoo.dylib | grep LC_REEXPORT_DYLIB | ${FAIL_IF_EMPTY} otool -lv libfoo.dylib | grep LC_SUB_LIBRARY | ${FAIL_IF_STDIN} - - -# -sub_umbrella for 10.4 - mkdir -p Bar.framework Foo.framework - ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" -mmacosx-version-min=10.4 - ${FAIL_IF_BAD_MACHO} Bar.framework/Bar - ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -framework Bar -sub_umbrella Bar -mmacosx-version-min=10.4 - ${FAIL_IF_BAD_MACHO} Foo.framework/Foo - otool -lv Foo.framework/Foo | grep LC_SUB_UMBRELLA | ${FAIL_IF_EMPTY} - otool -lv Foo.framework/Foo | grep LC_REEXPORT_DYLIB | ${FAIL_IF_STDIN} -# -sub_umbrella for 10.5 + # -sub_umbrella for 10.5 mkdir -p Bar.framework Foo.framework - ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" -mmacosx-version-min=10.5 + ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" ${VERSION_NEW_LINKEDIT} ${FAIL_IF_BAD_MACHO} Bar.framework/Bar - ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -framework Bar -sub_umbrella Bar -mmacosx-version-min=10.5 + ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -framework Bar -sub_umbrella Bar ${VERSION_NEW_LINKEDIT} ${FAIL_IF_BAD_MACHO} Foo.framework/Foo otool -lv Foo.framework/Foo | grep LC_REEXPORT_DYLIB | ${FAIL_IF_EMPTY} otool -lv Foo.framework/Foo | grep LC_SUB_UMBRELLA | ${FAIL_IF_STDIN} - -# -umbrella for 10.4 + # -umbrella for 10.5 mkdir -p Bar.framework Foo.framework - ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" -umbrella Foo -mmacosx-version-min=10.4 + ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" -umbrella Foo ${VERSION_NEW_LINKEDIT} ${FAIL_IF_BAD_MACHO} Bar.framework/Bar - ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -framework Bar -mmacosx-version-min=10.4 - ${FAIL_IF_BAD_MACHO} Foo.framework/Foo - otool -lv Bar.framework/Bar | grep LC_SUB_FRAMEWORK | ${FAIL_IF_EMPTY} - otool -lv Foo.framework/Foo | grep LC_REEXPORT_DYLIB | ${FAIL_IF_STDIN} - -# -umbrella for 10.5 - mkdir -p Bar.framework Foo.framework - ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" -umbrella Foo -mmacosx-version-min=10.5 - ${FAIL_IF_BAD_MACHO} Bar.framework/Bar - ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -framework Bar -mmacosx-version-min=10.5 + ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -framework Bar ${VERSION_NEW_LINKEDIT} ${FAIL_IF_BAD_MACHO} Foo.framework/Foo otool -lv Bar.framework/Bar | grep LC_SUB_FRAMEWORK | ${FAIL_IF_EMPTY} otool -lv Foo.framework/Foo | grep LC_REEXPORT_DYLIB | ${FAIL_IF_EMPTY} - -# -reexport_library for 10.4 - ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -mmacosx-version-min=10.4 + # -reexport_library for 10.5 + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib ${VERSION_NEW_LINKEDIT} ${FAIL_IF_BAD_MACHO} libbar.dylib - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-reexport_library,libbar.dylib -mmacosx-version-min=10.4 + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-reexport_library,libbar.dylib ${VERSION_NEW_LINKEDIT} ${FAIL_IF_BAD_MACHO} libfoo.dylib - otool -lv libfoo.dylib | grep LC_SUB_LIBRARY | ${FAIL_IF_EMPTY} - otool -lv libfoo.dylib | grep LC_REEXPORT_DYLIB | ${FAIL_IF_STDIN} + otool -lv libfoo.dylib | grep LC_REEXPORT_DYLIB | ${FAIL_IF_EMPTY} + otool -lv libfoo.dylib | grep LC_SUB_LIBRARY | ${FAIL_IF_STDIN} -# -reexport_library for 10.5 - ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -mmacosx-version-min=10.5 + # -reexport-l for 10.5 + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib ${VERSION_NEW_LINKEDIT} ${FAIL_IF_BAD_MACHO} libbar.dylib - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-reexport_library,libbar.dylib -mmacosx-version-min=10.5 + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-reexport-lbar -L. ${VERSION_NEW_LINKEDIT} ${FAIL_IF_BAD_MACHO} libfoo.dylib otool -lv libfoo.dylib | grep LC_REEXPORT_DYLIB | ${FAIL_IF_EMPTY} otool -lv libfoo.dylib | grep LC_SUB_LIBRARY | ${FAIL_IF_STDIN} + + # -reexport_framework for 10.5 + mkdir -p Bar.framework Foo.framework + ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" ${VERSION_NEW_LINKEDIT} + ${FAIL_IF_BAD_MACHO} Bar.framework/Bar + ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -Wl,-reexport_framework,Bar ${VERSION_NEW_LINKEDIT} + ${FAIL_IF_BAD_MACHO} Foo.framework/Foo + otool -lv Foo.framework/Foo | grep LC_REEXPORT_DYLIB | ${FAIL_IF_EMPTY} + otool -lv Foo.framework/Foo | grep LC_SUB_UMBRELLA | ${FAIL_IF_STDIN} + # -reexport_framework and -umbrella for 10.4 + mkdir -p Bar.framework Foo.framework + ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" -umbrella Foo ${VERSION_NEW_LINKEDIT} + ${FAIL_IF_BAD_MACHO} Bar.framework/Bar + ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -Wl,-reexport_framework,Bar ${VERSION_NEW_LINKEDIT} + ${FAIL_IF_BAD_MACHO} Foo.framework/Foo + otool -lv Bar.framework/Bar | grep LC_SUB_FRAMEWORK | ${FAIL_IF_EMPTY} + otool -lv Foo.framework/Foo | grep LC_REEXPORT_DYLIB | ${FAIL_IF_EMPTY} + + ${PASS_IFF} /usr/bin/true -# -reexport-l for 10.4 - ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -mmacosx-version-min=10.4 + + +all-old: + # -sub_library for 10.4 + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib ${VERSION_OLD_LINKEDIT} ${FAIL_IF_BAD_MACHO} libbar.dylib - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-reexport-lbar -L. -mmacosx-version-min=10.4 + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lbar -L. -sub_library libbar ${VERSION_OLD_LINKEDIT} ${FAIL_IF_BAD_MACHO} libfoo.dylib otool -lv libfoo.dylib | grep LC_SUB_LIBRARY | ${FAIL_IF_EMPTY} otool -lv libfoo.dylib | grep LC_REEXPORT_DYLIB | ${FAIL_IF_STDIN} -# -reexport-l for 10.5 - ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -mmacosx-version-min=10.5 - ${FAIL_IF_BAD_MACHO} libbar.dylib - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-reexport-lbar -L. -mmacosx-version-min=10.5 - ${FAIL_IF_BAD_MACHO} libfoo.dylib - otool -lv libfoo.dylib | grep LC_REEXPORT_DYLIB | ${FAIL_IF_EMPTY} - otool -lv libfoo.dylib | grep LC_SUB_LIBRARY | ${FAIL_IF_STDIN} - - -# -reexport_framework for 10.4 + # -sub_umbrella for 10.4 mkdir -p Bar.framework Foo.framework - ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" -mmacosx-version-min=10.4 + ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" ${VERSION_OLD_LINKEDIT} ${FAIL_IF_BAD_MACHO} Bar.framework/Bar - ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -Wl,-reexport_framework,Bar -mmacosx-version-min=10.4 + ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -framework Bar -sub_umbrella Bar ${VERSION_OLD_LINKEDIT} ${FAIL_IF_BAD_MACHO} Foo.framework/Foo otool -lv Foo.framework/Foo | grep LC_SUB_UMBRELLA | ${FAIL_IF_EMPTY} - otool -lv Foo.framework/Foo | grep LC_REEXPORT_DYLIB | ${FAIL_IF_STDIN} + otool -lv Foo.framework/Foo | grep LC_REEXPORT_DYLIB | ${FAIL_IF_STDIN} -# -reexport_framework for 10.5 + # -umbrella for 10.4 mkdir -p Bar.framework Foo.framework - ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" -mmacosx-version-min=10.5 + ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" -umbrella Foo ${VERSION_OLD_LINKEDIT} ${FAIL_IF_BAD_MACHO} Bar.framework/Bar - ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -Wl,-reexport_framework,Bar -mmacosx-version-min=10.5 + ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -framework Bar ${VERSION_OLD_LINKEDIT} ${FAIL_IF_BAD_MACHO} Foo.framework/Foo - otool -lv Foo.framework/Foo | grep LC_REEXPORT_DYLIB | ${FAIL_IF_EMPTY} - otool -lv Foo.framework/Foo | grep LC_SUB_UMBRELLA | ${FAIL_IF_STDIN} - + otool -lv Bar.framework/Bar | grep LC_SUB_FRAMEWORK | ${FAIL_IF_EMPTY} + otool -lv Foo.framework/Foo | grep LC_REEXPORT_DYLIB | ${FAIL_IF_STDIN} + + # -reexport_library for 10.4 + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib ${VERSION_OLD_LINKEDIT} + ${FAIL_IF_BAD_MACHO} libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-reexport_library,libbar.dylib ${VERSION_OLD_LINKEDIT} + ${FAIL_IF_BAD_MACHO} libfoo.dylib + otool -lv libfoo.dylib | grep LC_SUB_LIBRARY | ${FAIL_IF_EMPTY} + otool -lv libfoo.dylib | grep LC_REEXPORT_DYLIB | ${FAIL_IF_STDIN} -# -reexport_framework and -umbrella for 10.4 + # -reexport-l for 10.4 + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib ${VERSION_OLD_LINKEDIT} + ${FAIL_IF_BAD_MACHO} libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-reexport-lbar -L. ${VERSION_OLD_LINKEDIT} + ${FAIL_IF_BAD_MACHO} libfoo.dylib + otool -lv libfoo.dylib | grep LC_SUB_LIBRARY | ${FAIL_IF_EMPTY} + otool -lv libfoo.dylib | grep LC_REEXPORT_DYLIB | ${FAIL_IF_STDIN} + + # -reexport_framework for 10.4 mkdir -p Bar.framework Foo.framework - ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" -umbrella Foo -mmacosx-version-min=10.4 + ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" ${VERSION_OLD_LINKEDIT} ${FAIL_IF_BAD_MACHO} Bar.framework/Bar - ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -Wl,-reexport_framework,Bar -mmacosx-version-min=10.4 + ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -Wl,-reexport_framework,Bar ${VERSION_OLD_LINKEDIT} ${FAIL_IF_BAD_MACHO} Foo.framework/Foo - otool -lv Bar.framework/Bar | grep LC_SUB_FRAMEWORK | ${FAIL_IF_EMPTY} + otool -lv Foo.framework/Foo | grep LC_SUB_UMBRELLA | ${FAIL_IF_EMPTY} otool -lv Foo.framework/Foo | grep LC_REEXPORT_DYLIB | ${FAIL_IF_STDIN} - -# -reexport_framework and -umbrella for 10.4 + # -reexport_framework and -umbrella for 10.4 mkdir -p Bar.framework Foo.framework - ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" -umbrella Foo -mmacosx-version-min=10.5 + ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" -umbrella Foo ${VERSION_OLD_LINKEDIT} ${FAIL_IF_BAD_MACHO} Bar.framework/Bar - ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -Wl,-reexport_framework,Bar -mmacosx-version-min=10.5 + ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -Wl,-reexport_framework,Bar ${VERSION_OLD_LINKEDIT} ${FAIL_IF_BAD_MACHO} Foo.framework/Foo otool -lv Bar.framework/Bar | grep LC_SUB_FRAMEWORK | ${FAIL_IF_EMPTY} - otool -lv Foo.framework/Foo | grep LC_REEXPORT_DYLIB | ${FAIL_IF_EMPTY} + otool -lv Foo.framework/Foo | grep LC_REEXPORT_DYLIB | ${FAIL_IF_STDIN} ${PASS_IFF} /usr/bin/true diff --git a/unit-tests/test-cases/re-export-layers/Makefile b/unit-tests/test-cases/re-export-layers/Makefile index fc62273..3ec1fca 100644 --- a/unit-tests/test-cases/re-export-layers/Makefile +++ b/unit-tests/test-cases/re-export-layers/Makefile @@ -28,23 +28,17 @@ include ${TESTROOT}/include/common.makefile # -run: all -all: +all: all-${ARCH} +all-i386: all-new all-old +all-x86_64: all-new all-old +all-armv6: all-new +all-armv7: all-new -# -reexport_library for 10.4 - ${CC} ${CCFLAGS} -dynamiclib baz.c -o libbaz.dylib -mmacosx-version-min=10.4 - ${FAIL_IF_BAD_MACHO} libbaz.dylib - ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -Wl,-reexport_library,libbaz.dylib -mmacosx-version-min=10.4 - ${FAIL_IF_BAD_MACHO} libbar.dylib - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-reexport_library,libbar.dylib -mmacosx-version-min=10.4 - ${FAIL_IF_BAD_MACHO} libfoo.dylib - ${CC} ${CCFLAGS} main.c libfoo.dylib -o main - ${FAIL_IF_BAD_MACHO} main - -# -reexport_library for 10.5 and later +all-new: + # -reexport_library for 10.5 and later ${CC} ${CCFLAGS} -dynamiclib baz.c -o libbaz.dylib ${FAIL_IF_BAD_MACHO} libbaz.dylib ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -Wl,-reexport_library,libbaz.dylib @@ -54,6 +48,18 @@ all: ${CC} ${CCFLAGS} main.c libfoo.dylib -o main ${PASS_IFF_GOOD_MACHO} main +all-old: + # -reexport_library for 10.4 + ${CC} ${CCFLAGS} -dynamiclib baz.c -o libbaz.dylib -mmacosx-version-min=10.4 + ${FAIL_IF_BAD_MACHO} libbaz.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -Wl,-reexport_library,libbaz.dylib -mmacosx-version-min=10.4 + ${FAIL_IF_BAD_MACHO} libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-reexport_library,libbar.dylib -mmacosx-version-min=10.4 + ${FAIL_IF_BAD_MACHO} libfoo.dylib + ${CC} ${CCFLAGS} main.c libfoo.dylib -o main + ${FAIL_IF_BAD_MACHO} main + + clean: diff --git a/unit-tests/test-cases/re-export-optimizations-indirect/Makefile b/unit-tests/test-cases/re-export-optimizations-indirect/Makefile index fdb13d5..4eb7b43 100644 --- a/unit-tests/test-cases/re-export-optimizations-indirect/Makefile +++ b/unit-tests/test-cases/re-export-optimizations-indirect/Makefile @@ -29,11 +29,15 @@ include ${TESTROOT}/include/common.makefile # -run: all +all: all-${ARCH} -all: +all-i386: all-new all-old +all-x86_64: all-new all-old +all-armv6: all-new +all-armv7: all-new -# -sub_library for 10.4 +all-old: + # -sub_library for 10.4 ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name /usr/lib/libbar.dylib -mmacosx-version-min=10.4 ${FAIL_IF_BAD_MACHO} libbar.dylib ${CC} ${CCFLAGS} -dynamiclib middle.c -o libmiddle.dylib -lbar -L. -sub_library libbar -install_name /mid/libmiddle.dylib -mmacosx-version-min=10.4 @@ -45,17 +49,18 @@ all: ${CC} ${CCFLAGS} main.c -DCALL_BAR libfoo.dylib libother.dylib -o main -L. -mmacosx-version-min=10.4 nm -m main | grep _bar | grep libbar | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} main - -# -sub_library for 10.5 - ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name /usr/lib/libbar.dylib -mmacosx-version-min=10.5 + +all-new: + # -sub_library for 10.5 + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name /usr/lib/libbar.dylib ${FAIL_IF_BAD_MACHO} libbar.dylib - ${CC} ${CCFLAGS} -dynamiclib middle.c -o libmiddle.dylib -lbar -L. -sub_library libbar -install_name /mid/libmiddle.dylib -mmacosx-version-min=10.5 + ${CC} ${CCFLAGS} -dynamiclib middle.c -o libmiddle.dylib -lbar -L. -sub_library libbar -install_name /mid/libmiddle.dylib ${FAIL_IF_BAD_MACHO} libmiddle.dylib - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lmiddle -L. -sub_library libmiddle -install_name /usr/lib/libfoo.dylib -mmacosx-version-min=10.5 + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lmiddle -L. -sub_library libmiddle -install_name /usr/lib/libfoo.dylib ${FAIL_IF_BAD_MACHO} libfoo.dylib - ${CC} ${CCFLAGS} -dynamiclib other.c -o libother.dylib -lbar -L. -sub_library libbar -install_name /usr/lib/libother.dylib -mmacosx-version-min=10.5 + ${CC} ${CCFLAGS} -dynamiclib other.c -o libother.dylib -lbar -L. -sub_library libbar -install_name /usr/lib/libother.dylib ${FAIL_IF_BAD_MACHO} libother.dylib - ${CC} ${CCFLAGS} main.c -DCALL_BAR libfoo.dylib libother.dylib -o main -L. -mmacosx-version-min=10.5 + ${CC} ${CCFLAGS} main.c -DCALL_BAR libfoo.dylib libother.dylib -o main -L. nm -m main | grep _bar | grep libbar | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} main diff --git a/unit-tests/test-cases/re-export-optimizations/Makefile b/unit-tests/test-cases/re-export-optimizations/Makefile index a1dfd88..e71b8cc 100644 --- a/unit-tests/test-cases/re-export-optimizations/Makefile +++ b/unit-tests/test-cases/re-export-optimizations/Makefile @@ -29,11 +29,17 @@ include ${TESTROOT}/include/common.makefile # -run: all -all: +all: all-${ARCH} -# -sub_library for 10.4 +all-i386: all-new all-old +all-x86_64: all-new all-old +all-armv6: all-new +all-armv7: all-new + + +all-old: + # -sub_library for 10.4 ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name /usr/lib/libbar.dylib -mmacosx-version-min=10.4 ${FAIL_IF_BAD_MACHO} libbar.dylib ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lbar -L. -sub_library libbar -mmacosx-version-min=10.4 @@ -44,16 +50,17 @@ all: ${CC} ${CCFLAGS} main.c libfoo.dylib -o main -L. -mmacosx-version-min=10.4 otool -L main | grep libbar | ${FAIL_IF_STDIN} - -# -sub_library for 10.5 - ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name /usr/lib/libbar.dylib -mmacosx-version-min=10.5 + +all-new: + # -sub_library for 10.5 + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name /usr/lib/libbar.dylib ${FAIL_IF_BAD_MACHO} libbar.dylib - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lbar -L. -sub_library libbar -mmacosx-version-min=10.5 + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lbar -L. -sub_library libbar ${FAIL_IF_BAD_MACHO} libfoo.dylib - ${CC} ${CCFLAGS} main.c -DCALL_BAR libfoo.dylib -o main -L. -mmacosx-version-min=10.5 + ${CC} ${CCFLAGS} main.c -DCALL_BAR libfoo.dylib -o main -L. otool -L main | grep libbar | ${FAIL_IF_EMPTY} nm -m main | grep _bar | grep libbar | ${FAIL_IF_EMPTY} - ${CC} ${CCFLAGS} main.c libfoo.dylib -o main -L. -mmacosx-version-min=10.5 + ${CC} ${CCFLAGS} main.c libfoo.dylib -o main -L. otool -L main | grep libbar | ${FAIL_IF_STDIN} diff --git a/unit-tests/test-cases/read-only-relocs/Makefile b/unit-tests/test-cases/read-only-relocs/Makefile index 8db5b03..e4b8086 100644 --- a/unit-tests/test-cases/read-only-relocs/Makefile +++ b/unit-tests/test-cases/read-only-relocs/Makefile @@ -1,4 +1,4 @@ -## + ## # Copyright (c) 2006-2007 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ @@ -51,6 +51,10 @@ ifeq (${ARCH},x86_64) XRELOCS_NEEDED = ${FAIL_IF_STDIN} LRELOCS_NEEDED = ${FAIL_IF_STDIN} endif +ifeq (${FILEARCH},arm) + NO_PIC = -mdynamic-no-pic + STATIC = -static +endif diff --git a/unit-tests/test-cases/relocs-asm/relocs-asm.s b/unit-tests/test-cases/relocs-asm/relocs-asm.s index 86716da..67ff5e5 100644 --- a/unit-tests/test-cases/relocs-asm/relocs-asm.s +++ b/unit-tests/test-cases/relocs-asm/relocs-asm.s @@ -126,7 +126,7 @@ L101: .text _pointer_diffs: - .long _foo-1b +1: .long _foo-1b .long _foo+10-1b .long _test_branches-1b .long _test_branches+3-1b @@ -244,6 +244,12 @@ _arm16tests: movt r0, :upper16:_datahilo16alt+1792 movw r0, :lower16:_datahilo16alt+165 movt r0, :upper16:_datahilo16alt+165 + movw r0, :lower16:_thumbTarget + movt r0, :upper16:_thumbTarget + movw r0, :lower16:_externalTarget + movt r0, :upper16:_externalTarget + movw r0, :lower16:_externalTarget+61447 + movt r0, :upper16:_externalTarget+61447 Lpicbase: movw r0, :lower16:_datahilo16 - Lpicbase movt r0, :upper16:_datahilo16 - Lpicbase @@ -259,6 +265,8 @@ Lpicbase: movt r0, :upper16:_datahilo16alt+1792 - Lpicbase movw r0, :lower16:_datahilo16alt+165 - Lpicbase movt r0, :upper16:_datahilo16alt+165 - Lpicbase + movw r0, :lower16:_thumbTarget - Lpicbase + movt r0, :upper16:_thumbTarget - Lpicbase bx lr .code 16 @@ -278,6 +286,12 @@ _thumb16tests: movt r0, :upper16:_datahilo16alt+1792 movw r0, :lower16:_datahilo16alt+165 movt r0, :upper16:_datahilo16alt+165 + movw r0, :lower16:_thumbTarget + movt r0, :upper16:_thumbTarget + movw r0, :lower16:_externalTarget + movt r0, :upper16:_externalTarget + movw r0, :lower16:_externalTarget+61447 + movt r0, :upper16:_externalTarget+61447 Lpicbase2: movw r0, :lower16:_datahilo16 - Lpicbase2 movt r0, :upper16:_datahilo16 - Lpicbase2 @@ -293,12 +307,22 @@ Lpicbase2: movt r0, :upper16:_datahilo16alt+1792 - Lpicbase2 movw r0, :lower16:_datahilo16alt+165 - Lpicbase2 movt r0, :upper16:_datahilo16alt+165 - Lpicbase2 + movw r0, :lower16:_thumbTarget - Lpicbase2 + movt r0, :upper16:_thumbTarget - Lpicbase2 bx lr - + + .code 16 + .thumb_func _thumbTarget +_thumbTarget: + nop + bx lr + .data _datahilo16: .long 0 _datahilo16alt: .long 0 + + #endif #endif @@ -727,6 +751,9 @@ _b: .long _external+16 .long _test_weak .long _test_weak+16 + .long Lother - . + 0x4000000 +Lother: + .long 0 #elif __ppc64__ || __x86_64__ .quad _test_calls .quad _test_calls+16 @@ -734,6 +761,9 @@ _b: .quad _external+16 .quad _test_weak .quad _test_weak+16 + .quad Lother - . + 0x4000000 +Lother: + .quad 0 #endif # test that reloc sizes are the same diff --git a/unit-tests/test-cases/shared-cache-dylib/foo.c b/unit-tests/test-cases/shared-cache-dylib/foo.c index 6924ac6..68edc41 100644 --- a/unit-tests/test-cases/shared-cache-dylib/foo.c +++ b/unit-tests/test-cases/shared-cache-dylib/foo.c @@ -1,3 +1,6 @@ -void foo() {} + +int x; + +int foo() { return x; } diff --git a/unit-tests/test-cases/stabs-directory-slash/Makefile b/unit-tests/test-cases/stabs-directory-slash/Makefile index 5318933..d6c5b82 100644 --- a/unit-tests/test-cases/stabs-directory-slash/Makefile +++ b/unit-tests/test-cases/stabs-directory-slash/Makefile @@ -31,9 +31,9 @@ include ${TESTROOT}/include/common.makefile run: all all: - $(CXX) -gstabs+ main.c -o outfile + $(CC) -gstabs+ main.c -o outfile ${FAIL_IF_BAD_MACHO} outfile - nm -ap outfile | ${PASS_IFF} grep '.*\.*test-cases.*/$$' + nm -ap outfile | grep '.*\.*test-cases.*/$$' | ${PASS_IFF_STDIN} clean: rm outfile* diff --git a/unit-tests/test-cases/stabs-directory-slash/main.c b/unit-tests/test-cases/stabs-directory-slash/main.c index 54dc4c5..a46866d 100644 --- a/unit-tests/test-cases/stabs-directory-slash/main.c +++ b/unit-tests/test-cases/stabs-directory-slash/main.c @@ -1,3 +1,4 @@ -main() +int main() { + return 0; } diff --git a/unit-tests/test-cases/stack_addr_size/Makefile b/unit-tests/test-cases/stack_addr_size/Makefile index 670f014..c88f66b 100644 --- a/unit-tests/test-cases/stack_addr_size/Makefile +++ b/unit-tests/test-cases/stack_addr_size/Makefile @@ -25,7 +25,7 @@ include ${TESTROOT}/include/common.makefile # Test the ld option -stack_addr and -stack_size used together -ifeq ($(ARCH),armv6) +ifeq ($(FILEARCH),arm) STACK_ADDR = 0x2C000000 STACK_SIZE = 0x05000000 STACK_TOP = 0x27000000 @@ -51,7 +51,12 @@ all: otool -l main | grep -A6 __UNIXSTACK > main.otool grep " vmsize[ ]*${STACK_SIZE}" main.otool | ${FAIL_IF_EMPTY} grep " vmaddr[ ]*${STACK_TOP}" main.otool | ${FAIL_IF_EMPTY} - ${PASS_IFF_GOOD_MACHO} main + ${FAIL_IF_BAD_MACHO} main + ${CC} ${CCFLAGS} -static main.c -o main2 -e _main -nostdlib -Wl,-new_linker -Wl,-stack_size,${STACK_SIZE} -Wl,-stack_addr,${STACK_ADDR} + otool -l main2 | grep -A6 __UNIXSTACK > main2.otool + grep " vmsize[ ]*${STACK_SIZE}" main2.otool | ${FAIL_IF_EMPTY} + grep " vmaddr[ ]*${STACK_TOP}" main2.otool | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main2 clean: - rm -rf main main.otool + rm -rf main main.otool main2.otool main2 diff --git a/unit-tests/test-cases/stack_size_no_addr/Makefile b/unit-tests/test-cases/stack_size_no_addr/Makefile index 6134c7e..cbb6d49 100644 --- a/unit-tests/test-cases/stack_size_no_addr/Makefile +++ b/unit-tests/test-cases/stack_size_no_addr/Makefile @@ -25,10 +25,10 @@ include ${TESTROOT}/include/common.makefile # Test the ld option -stack_size adds a custom stack segment -ifeq ($(ARCH),armv6) +ifeq (${FILEARCH},arm) STACK_ADDR = 0x30000000 STACK_SIZE = 0x05000000 - STACK_TOP = 0x2b000000 + STACK_TOP = 0x2a000000 else ifeq (,${findstring 64,$(ARCH)}) STACK_ADDR = 0xC0000000 diff --git a/unit-tests/test-cases/strip_local/hello.c b/unit-tests/test-cases/strip_local/hello.c index 2deed42..09e802f 100644 --- a/unit-tests/test-cases/strip_local/hello.c +++ b/unit-tests/test-cases/strip_local/hello.c @@ -29,5 +29,6 @@ extern void func(int); int main() { func(data); + return 0; } diff --git a/unit-tests/test-cases/switch-jump-table/Makefile b/unit-tests/test-cases/switch-jump-table/Makefile index 847dc78..72bc68b 100644 --- a/unit-tests/test-cases/switch-jump-table/Makefile +++ b/unit-tests/test-cases/switch-jump-table/Makefile @@ -30,28 +30,25 @@ include ${TESTROOT}/include/common.makefile # SPEC2000/eon built with -mdynamic-no-pic won't run # -run: test-run-${ARCH} +all: test-${FILEARCH} -test-run-ppc: - ${PASS_IFF} true - -test-run-x86_64: - ${PASS_IFF} true - -test-run-armv6: test-run-i386 +test-ppc: test-good +test-x86_64: test-good +test-i386: test +test-arm: test -test-run-i386: +test: # check jump table in a weak function - ${CC} ${CCFLAGS} main.c switch.s -o main + ${CC} ${CCFLAGS} main.c switch.s -o main -Wl,-no_pie otool -rv main | grep _foo | ${FAIL_IF_STDIN} otool -rv main | grep _bar | ${FAIL_IF_STDIN} # check jump table in a regular function with -flat_namespace - ${CC} ${CCFLAGS} main.c switch.s -o main -flat_namespace + ${CC} ${CCFLAGS} main.c switch.s -o main -flat_namespace -Wl,-no_pie otool -rv main | grep _foo | ${FAIL_IF_STDIN} otool -rv main | grep _bar | ${FAIL_IF_STDIN} # check jump table in a regular function that is interposable - ${CC} ${CCFLAGS} main.c switch.s -o main -Wl,-interposable_list,interpose.exp + ${CC} ${CCFLAGS} main.c switch.s -o main -Wl,-interposable_list,interpose.exp -Wl,-no_pie otool -rv main | grep _foo | ${FAIL_IF_STDIN} otool -rv main | grep _bar | ${FAIL_IF_STDIN} # check jump table with -pie, should have no external and some local relocations @@ -60,6 +57,8 @@ test-run-i386: # otool -rv main | grep "Local relocation" | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} main +test-good: + ${PASS_IFF} true clean: rm -f main diff --git a/unit-tests/test-cases/switch-jump-table/switch.s b/unit-tests/test-cases/switch-jump-table/switch.s index 12b559f..b2b0249 100644 --- a/unit-tests/test-cases/switch-jump-table/switch.s +++ b/unit-tests/test-cases/switch-jump-table/switch.s @@ -9,6 +9,7 @@ */ .globl _foo .weak_definition _foo + .align 4 _foo: nop nop @@ -29,6 +30,7 @@ L3: nop to a jump table */ .text + .align 4 .globl _bar _bar: nop nop diff --git a/unit-tests/test-cases/tentative-and-archive-code/Makefile b/unit-tests/test-cases/tentative-and-archive-code/Makefile new file mode 100644 index 0000000..7c2c28f --- /dev/null +++ b/unit-tests/test-cases/tentative-and-archive-code/Makefile @@ -0,0 +1,64 @@ +## +# 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 + + +# +# Test how tentative definitions interact with archives +# main.c has a tenative definition for _foo which +# should cause libfoo_data.a to be loaded, but not libfoo_tent.a +# nor libfoo_code.a. +# +# Snow Leopard linker bug with common symbols +# + +run: all + +all: + ${CC} ${CCFLAGS} junk.c -c -o junk.o + # verify data symbol does get pulled in + ${CC} ${CCFLAGS} foo_data.c -c -o foo_data.o + libtool -static junk.o foo_data.o -o libfoo_data.a + ${CC} ${CCFLAGS} main.c libfoo_data.a -o main_data + nm -m main_data | grep _foo | grep __TEXT | ${FAIL_IF_STDIN} + # verify tentative does not + ${CC} ${CCFLAGS} foo_tent.c -c -o foo_tent.o + libtool -static junk.o foo_tent.o -o libfoo_tent.a + ${CC} ${CCFLAGS} main.c libfoo_tent.a -o main_tent + nm -m main_tent | grep _foo | grep __TEXT | ${FAIL_IF_STDIN} + # verify code does not + ${CC} ${CCFLAGS} foo_code.c -c -o foo_code.o + libtool -static junk.o foo_code.o -o libfoo_code.a + ${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} + ${PASS_IFF_GOOD_MACHO} main_code + +clean: + rm -rf junk.o foo_data.o libfoo_data.a main_data foo_tent.o libfoo_tent.a main_tent foo_code.o libfoo_code.a main_code main_code_force main_code_force_warnings.txt + diff --git a/unit-tests/test-cases/tentative-and-archive-code/foo_code.c b/unit-tests/test-cases/tentative-and-archive-code/foo_code.c new file mode 100644 index 0000000..277b5ae --- /dev/null +++ b/unit-tests/test-cases/tentative-and-archive-code/foo_code.c @@ -0,0 +1,3 @@ + +int foo() { return 10; } + diff --git a/unit-tests/test-cases/tentative-and-archive-code/foo_data.c b/unit-tests/test-cases/tentative-and-archive-code/foo_data.c new file mode 100644 index 0000000..e7d3845 --- /dev/null +++ b/unit-tests/test-cases/tentative-and-archive-code/foo_data.c @@ -0,0 +1,2 @@ + +int foo = 5; diff --git a/unit-tests/test-cases/tentative-and-archive-code/foo_tent.c b/unit-tests/test-cases/tentative-and-archive-code/foo_tent.c new file mode 100644 index 0000000..42e0a8e --- /dev/null +++ b/unit-tests/test-cases/tentative-and-archive-code/foo_tent.c @@ -0,0 +1,2 @@ + +int foo; diff --git a/unit-tests/test-cases/tentative-and-archive-code/junk.c b/unit-tests/test-cases/tentative-and-archive-code/junk.c new file mode 100644 index 0000000..6ecfcf1 --- /dev/null +++ b/unit-tests/test-cases/tentative-and-archive-code/junk.c @@ -0,0 +1 @@ +void junk() {} diff --git a/unit-tests/test-cases/tentative-and-archive-code/main.c b/unit-tests/test-cases/tentative-and-archive-code/main.c new file mode 100644 index 0000000..08ed143 --- /dev/null +++ b/unit-tests/test-cases/tentative-and-archive-code/main.c @@ -0,0 +1,8 @@ + +int foo; + +int main() +{ + foo = 3; + return 0; +} diff --git a/unit-tests/test-cases/tlv-dead_strip/Makefile b/unit-tests/test-cases/tlv-dead_strip/Makefile new file mode 100644 index 0000000..46e695c --- /dev/null +++ b/unit-tests/test-cases/tlv-dead_strip/Makefile @@ -0,0 +1,39 @@ +## +# 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 main executable can use TLVs with -dead_strip +# + +run: all + +all: + clang ${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 | grep S_THREAD_LOCAL_REGULAR | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm main diff --git a/unit-tests/test-cases/tlv-dead_strip/main.c b/unit-tests/test-cases/tlv-dead_strip/main.c new file mode 100644 index 0000000..a3e3fee --- /dev/null +++ b/unit-tests/test-cases/tlv-dead_strip/main.c @@ -0,0 +1,33 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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@ + */ + +__thread int gTLSThreadID = 0; + +int foobar() { + return gTLSThreadID; +} + +int main(void) { + return foobar(); +} diff --git a/unit-tests/test-cases/umbrella-dylib/Makefile b/unit-tests/test-cases/umbrella-dylib/Makefile index b20f368..0835fdb 100644 --- a/unit-tests/test-cases/umbrella-dylib/Makefile +++ b/unit-tests/test-cases/umbrella-dylib/Makefile @@ -40,10 +40,10 @@ all: ${CC} ${CCFLAGS} -dynamiclib -Wl,-reexport_library,liba.dylib -Wl,-reexport_library,libb.dylib -Wl,-reexport_library,libc.dylib -o libBig.dylib -install_name /usr/lib/libBig.dylib # re-link against correct dylibs now that everything is built ${CC} ${CCFLAGS} -dynamiclib a.c -o liba.dylib libc.dylib -install_name /usr/local/lib/big/liba.dylib -umbrella Big - dyldinfo -lazy_bind liba.dylib | grep c2 | grep libc | ${FAIL_IF_EMPTY} + ${DYLDINFO} -lazy_bind liba.dylib | grep c2 | grep libc | ${FAIL_IF_EMPTY} ${CC} ${CCFLAGS} -dynamiclib b.c -o libb.dylib libc.dylib -install_name /usr/local/lib/big/libb.dylib -umbrella Big ${CC} ${CCFLAGS} -dynamiclib c.c -o libc.dylib liba.dylib -install_name /usr/local/lib/big/libc.dylib -umbrella Big - dyldinfo -lazy_bind libc.dylib | grep a2 | grep liba | ${FAIL_IF_EMPTY} + ${DYLDINFO} -lazy_bind libc.dylib | grep a2 | grep liba | ${FAIL_IF_EMPTY} ${CC} ${CCFLAGS} -dynamiclib -Wl,-reexport_library,liba.dylib -Wl,-reexport_library,libb.dylib -Wl,-reexport_library,libc.dylib -o libBig.dylib -install_name /usr/lib/libBig.dylib ${CC} ${CCFLAGS} main.c -o main libBig.dylib -L. ${PASS_IFF_GOOD_MACHO} main diff --git a/unit-tests/test-cases/weak-def-auto-hide/other.s b/unit-tests/test-cases/weak-def-auto-hide/other.s index 5fd7ec4..c524ebf 100644 --- a/unit-tests/test-cases/weak-def-auto-hide/other.s +++ b/unit-tests/test-cases/weak-def-auto-hide/other.s @@ -1,7 +1,8 @@ .text - + .align 4 + .globl _my_weak .weak_def_can_be_hidden _my_weak _my_weak: nop diff --git a/unit-tests/test-cases/weak-def-hidden-and-global/Makefile b/unit-tests/test-cases/weak-def-hidden-and-global/Makefile new file mode 100644 index 0000000..b275b62 --- /dev/null +++ b/unit-tests/test-cases/weak-def-hidden-and-global/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + + +# +# clang ld: bad codegen, pointer diff +# Test that codegen needing direct access to a weak symbol just issues +# a warning. Check that if export list makes weak symbol hidden, there is +# no warning. +# + +run: all + +all: + ${CC} ${CCFLAGS} myglobal.c myhidden.s -dynamiclib -o libmy.dylib 2>warnings.txt + grep "global weak" warnings.txt | ${FAIL_IF_EMPTY} + ${FAIL_IF_BAD_MACHO} libmy.dylib + ${CC} ${CCFLAGS} myglobal.c myhidden.s -dynamiclib -Wl,-exported_symbol,_test -o libmy2.dylib + ${PASS_IFF_GOOD_MACHO} libmy2.dylib + +clean: + rm -f libmy.dylib libmy2.dylib warnings.txt diff --git a/unit-tests/test-cases/weak-def-hidden-and-global/myglobal.c b/unit-tests/test-cases/weak-def-hidden-and-global/myglobal.c new file mode 100644 index 0000000..c671809 --- /dev/null +++ b/unit-tests/test-cases/weak-def-hidden-and-global/myglobal.c @@ -0,0 +1,4 @@ + +__attribute__((weak)) +int myweak = 10; + diff --git a/unit-tests/test-cases/weak-def-hidden-and-global/myhidden.s b/unit-tests/test-cases/weak-def-hidden-and-global/myhidden.s new file mode 100644 index 0000000..9b6a57e --- /dev/null +++ b/unit-tests/test-cases/weak-def-hidden-and-global/myhidden.s @@ -0,0 +1,45 @@ + .data + .globl _myweak + .private_extern _myweak + .weak_definition _myweak +_myweak: + .long 0 + + + .text + .align 2 +#if __ARM_ARCH_7A__ + .code 16 + .thumb_func _test +#endif + + .globl _test +_test: +#if __x86_64__ + nop + movl _myweak(%rip), %eax + ret +#elif __i386__ + call L1 +L1: popl %eax + movl _myweak-L1(%eax), %eax + ret +#elif __arm__ + +#if __ARM_ARCH_7A__ + movw r0, :lower16:(_myweak-(L4+4)) + movt r0, :upper16:(_myweak-(L4+4)) +L4: add r0, pc + ldr r0, [r0] + bx lr +#else + ldr r0, L2 +L3: ldr r0, [pc, r0] + bx lr + .align 2 +L2: .long _myweak-(L3+8) +#endif + + +#endif + diff --git a/unit-tests/test-cases/weak_dylib/Makefile b/unit-tests/test-cases/weak_dylib/Makefile index 0a00a39..6fd999e 100644 --- a/unit-tests/test-cases/weak_dylib/Makefile +++ b/unit-tests/test-cases/weak_dylib/Makefile @@ -37,13 +37,33 @@ all: ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib ${FAIL_IF_BAD_MACHO} libbar.dylib - ${CC} ${CCFLAGS} -mmacosx-version-min=10.4 main.c -o main libfoo.dylib libbar.dylib + ${CC} ${CCFLAGS} ${VERSION_OLD_LINKEDIT} main.c -o main libfoo.dylib libbar.dylib # libfoo.dylib should be weakly loaded because all symbols are weakly imported otool -lv main | grep -B2 libfoo.dylib | grep LC_LOAD_WEAK_DYLIB | ${FAIL_IF_EMPTY} - # libbar.dylib should not be weakly loaded because _bar4 is not weakly imported + # libbar.dylib should not be weakly loaded because _bar1 is not weakly imported otool -lv main | grep -B2 libbar.dylib | grep LC_LOAD_DYLIB | ${FAIL_IF_EMPTY} - ${PASS_IFF_GOOD_MACHO} main + ${FAIL_IF_BAD_MACHO} main + + ${CC} ${CCFLAGS} ${VERSION_NEW_LINKEDIT} main.c -o main2 libfoo.dylib libbar.dylib + # libfoo.dylib should be weakly loaded because all symbols are weakly imported + otool -lv main2 | grep -B2 libfoo.dylib | grep LC_LOAD_WEAK_DYLIB | ${FAIL_IF_EMPTY} + # libbar.dylib should not be weakly loaded because _bar1 is not weakly imported + otool -lv main2 | grep -B2 libbar.dylib | grep LC_LOAD_DYLIB | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main2 + + ${CC} ${CCFLAGS} ${VERSION_NEW_LINKEDIT} data.c -o data libfoo.dylib libbar.dylib + # libfoo.dylib should be weakly loaded because all symbols are weakly imported + otool -lv data | grep -B2 libfoo.dylib | grep LC_LOAD_WEAK_DYLIB | ${FAIL_IF_EMPTY} + # libbar.dylib should not be weakly loaded because _bar1 is not weakly imported + otool -lv data | grep -B2 libbar.dylib | grep LC_LOAD_DYLIB | ${FAIL_IF_EMPTY} + ${FAIL_IF_BAD_MACHO} data + ${CC} ${CCFLAGS} ${VERSION_NEW_LINKEDIT} data.c -o data2 libfoo.dylib libbar.dylib + # libfoo.dylib should be weakly loaded because all symbols are weakly imported + otool -lv data2 | grep -B2 libfoo.dylib | grep LC_LOAD_WEAK_DYLIB | ${FAIL_IF_EMPTY} + # libbar.dylib should not be weakly loaded because _bar1 is not weakly imported + otool -lv data2 | grep -B2 libbar.dylib | grep LC_LOAD_DYLIB | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} data2 clean: - rm -rf libfoo.dylib libbar.dylib main + rm -rf libfoo.dylib libbar.dylib main main2 data data2 diff --git a/unit-tests/test-cases/weak_dylib/data.c b/unit-tests/test-cases/weak_dylib/data.c new file mode 100644 index 0000000..98cd752 --- /dev/null +++ b/unit-tests/test-cases/weak_dylib/data.c @@ -0,0 +1,15 @@ + +#include "foo.h" +#include "bar.h" + +void* pfoo4 = &foo4; +void* pfoo2 = &foo2; + +void* pbar2 = &bar2; +void* pbar1 = &bar1; // not weak + +int main (void) +{ + return 0; +} + diff --git a/unit-tests/test-cases/weak_import-addend/Makefile b/unit-tests/test-cases/weak_import-addend/Makefile new file mode 100644 index 0000000..898424b --- /dev/null +++ b/unit-tests/test-cases/weak_import-addend/Makefile @@ -0,0 +1,52 @@ +## +# 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 the weak_import attribute works +# + +all: all-${ARCH} + +all-ppc: all-true + +all-ppc64: all-true + +all-i386: all-true + +all-armv6: all-true + +all-armv7: all-true + +all-true: + ${PASS_IFF} true + +all-x86_64: + ${CC} ${CCFLAGS} -dynamiclib test.s -o libtest.dylib -mmacosx-version-min=10.6 + ${DYLDINFO} -bind -lazy_bind libtest.dylib | grep _malloc | grep weak | ${FAIL_IF_EMPTY} + ${DYLDINFO} -bind -lazy_bind libtest.dylib | grep _free | grep weak | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libtest.dylib + +clean: + rm -rf libtest.dylib diff --git a/unit-tests/test-cases/weak_import-addend/test.s b/unit-tests/test-cases/weak_import-addend/test.s new file mode 100644 index 0000000..fb39453 --- /dev/null +++ b/unit-tests/test-cases/weak_import-addend/test.s @@ -0,0 +1,12 @@ + + + .text +_foo: +#if __x86_64__ + .weak_reference _malloc + .weak_reference _free + cmpq $0, _malloc@GOTPCREL(%rip) + cmpq $0xFFFF, _free@GOTPCREL(%rip) +#endif + nop + diff --git a/unit-tests/test-cases/weak_import-local/Makefile b/unit-tests/test-cases/weak_import-local/Makefile index 4d21410..bc94bd0 100644 --- a/unit-tests/test-cases/weak_import-local/Makefile +++ b/unit-tests/test-cases/weak_import-local/Makefile @@ -33,7 +33,7 @@ run: all all: ${CC} ${CCFLAGS} main.c foo.c -o main ${FAIL_IF_BAD_MACHO} main - ${CC} ${CCFLAGS} main.c foo.c -o main -mmacosx-version-min=10.4 + ${CC} ${CCFLAGS} main.c foo.c -o main ${VERSION_OLD_LINKEDIT} ${PASS_IFF_GOOD_MACHO} main clean: diff --git a/unit-tests/test-cases/weak_import3/Makefile b/unit-tests/test-cases/weak_import3/Makefile index 98a2779..2c8806d 100644 --- a/unit-tests/test-cases/weak_import3/Makefile +++ b/unit-tests/test-cases/weak_import3/Makefile @@ -31,13 +31,16 @@ include ${TESTROOT}/include/common.makefile run: all all: - ${CC} ${CCFLAGS} -c foo.c -o foo-${ARCH}.o - ${FAIL_IF_BAD_OBJ} foo-${ARCH}.o + ${CC} ${CCFLAGS} -c foo.c -o foo.o + ${FAIL_IF_BAD_OBJ} foo.o - ${CC} ${CCFLAGS} -c foo1.c -o foo1-${ARCH}.o - ${FAIL_IF_BAD_OBJ} foo1-${ARCH}.o + ${CC} ${CCFLAGS} -c foo1.c -o foo1.o + ${FAIL_IF_BAD_OBJ} foo1.o - ${PASS_IFF_ERROR} ${CC} ${CCFLAGS} -dynamiclib foo-${ARCH}.o foo1-${ARCH}.o -o libfoo-${ARCH}.dylib 2>/dev/null + ${PASS_IFF_ERROR} ${CC} ${CCFLAGS} -dynamiclib foo.o foo1.o -o libfoo.dylib clean: - rm -rf *.o *.dylib main-* + rm -rf foo.o foo1.o libfoo.dylib + + +#2>/dev/null \ No newline at end of file diff --git a/unit-tests/test-cases/zero-fill3/Makefile b/unit-tests/test-cases/zero-fill3/Makefile index 6266019..d16b9d0 100644 --- a/unit-tests/test-cases/zero-fill3/Makefile +++ b/unit-tests/test-cases/zero-fill3/Makefile @@ -28,22 +28,20 @@ include ${TESTROOT}/include/common.makefile # can link a program with a large zero-fill section # -run: test-run-${ARCH} +all: test-${FILEARCH} # i386 catches the problem in the assembler phase -test-run-i386: +test-i386: ${PASS_IFF} true -test-run-ppc test-run-ppc64: test-${ARCH} - -test-run-x86_64 test-ppc64: +test-x86_64: ${CC} ${CCFLAGS} -DSHRINK=1 test.c --save-temps -o test-${ARCH} ${PASS_IFF_GOOD_MACHO} test-${ARCH} test-ppc: ${PASS_IFF_ERROR} ${CC} ${CCFLAGS} -DSHRINK=4 test.c --save-temps -o test-${ARCH} 2>/dev/null -test-run-armv6: +test-arm: ${PASS_IFF_ERROR} ${CC} ${CCFLAGS} -DSHRINK=4 test.c --save-temps -o test-${ARCH} 2>/dev/null clean: -- 2.45.2